diff --git a/AppSim/Source/freertos.cpp b/AppSim/Source/freertos.cpp index 68af132b..5273c8b6 100644 --- a/AppSim/Source/freertos.cpp +++ b/AppSim/Source/freertos.cpp @@ -21,7 +21,7 @@ int main() { "main", 8192, nullptr, - tt::ThreadPriorityNormal, + tt::Thread::PriorityNormal, nullptr ); diff --git a/AppSim/Source/lvgl_task.cpp b/AppSim/Source/lvgl_task.cpp index 04b8e38d..44ba9c34 100644 --- a/AppSim/Source/lvgl_task.cpp +++ b/AppSim/Source/lvgl_task.cpp @@ -81,7 +81,7 @@ void lvgl_task_start() { "lvgl", 8192, NULL, - tt::ThreadPriorityHigh, // Should be higher than main app task + tt::Thread::PriorityHigh, // Should be higher than main app task NULL ); diff --git a/Boards/M5stackShared/Source/m5stack_lvgl.cpp b/Boards/M5stackShared/Source/m5stack_lvgl.cpp index ccd9553f..2cfd9807 100644 --- a/Boards/M5stackShared/Source/m5stack_lvgl.cpp +++ b/Boards/M5stackShared/Source/m5stack_lvgl.cpp @@ -12,7 +12,7 @@ bool m5stack_lvgl_init() { static lv_display_t* display = nullptr; const lvgl_port_cfg_t lvgl_cfg = { - .task_priority = tt::ThreadPriorityHigh, + .task_priority = tt::Thread::PriorityHigh, .task_stack = 8096, .task_affinity = -1, // core pinning .task_max_sleep_ms = 500, diff --git a/Boards/WaveshareS3Touch/display.cpp b/Boards/WaveshareS3Touch/display.cpp index 9829a4d8..3c24d69e 100644 --- a/Boards/WaveshareS3Touch/display.cpp +++ b/Boards/WaveshareS3Touch/display.cpp @@ -104,9 +104,9 @@ lv_disp_t* ws3t_display_create() { lvgl_mux = xSemaphoreCreateRecursiveMutex(); tt_assert(lvgl_mux); - tt::Thread* thread = tt::thread_alloc_ex("display_task", 8192, &display_task, nullptr); - tt::thread_set_priority(thread, tt::ThreadPriorityHigh); // TODO: try out THREAD_PRIORITY_RENDER - tt::thread_start(thread); + tt::Thread* thread = new tt::Thread("display_task", 8192, &display_task, nullptr); + thread->setPriority(tt::Thread::PriorityHigh); // TODO: try out THREAD_PRIORITY_RENDER + thread->start(); esp_lcd_panel_handle_t panel_handle = NULL; esp_lcd_rgb_panel_config_t panel_config = { diff --git a/Boards/YellowBoard/lvgl.cpp b/Boards/YellowBoard/lvgl.cpp index 3065a953..f7d8702c 100644 --- a/Boards/YellowBoard/lvgl.cpp +++ b/Boards/YellowBoard/lvgl.cpp @@ -14,7 +14,7 @@ bool twodotfour_lvgl_init() { static esp_lcd_touch_handle_t touch_handle; const lvgl_port_cfg_t lvgl_cfg = { - .task_priority = tt::ThreadPriorityHigh, + .task_priority = tt::Thread::PriorityHigh, .task_stack = 8096, .task_affinity = -1, // core pinning .task_max_sleep_ms = 500, diff --git a/Tactility/Source/Apps/Gpio/Gpio.cpp b/Tactility/Source/Apps/Gpio/Gpio.cpp index 8ad4449a..2fb0506a 100644 --- a/Tactility/Source/Apps/Gpio/Gpio.cpp +++ b/Tactility/Source/Apps/Gpio/Gpio.cpp @@ -90,14 +90,14 @@ static int32_t gpio_task(void* context) { static void task_start(Gpio* gpio) { tt_assert(gpio->thread == nullptr); lock(gpio); - gpio->thread = thread_alloc_ex( + gpio->thread = new Thread( "gpio", 4096, &gpio_task, gpio ); gpio->thread_interrupted = false; - thread_start(gpio->thread); + gpio->thread->start(); unlock(gpio); } @@ -107,10 +107,10 @@ static void task_stop(Gpio* gpio) { gpio->thread_interrupted = true; unlock(gpio); - thread_join(gpio->thread); + gpio->thread->join(); lock(gpio); - thread_free(gpio->thread); + delete gpio->thread; gpio->thread = nullptr; unlock(gpio); } diff --git a/Tactility/Source/Apps/Power/Power.cpp b/Tactility/Source/Apps/Power/Power.cpp index eb3cd9ca..e9f6e0f0 100644 --- a/Tactility/Source/Apps/Power/Power.cpp +++ b/Tactility/Source/Apps/Power/Power.cpp @@ -20,9 +20,8 @@ typedef struct { lv_obj_t* current; } AppData; -static void app_update_ui(App app) { - auto* data = static_cast(tt_app_get_data(app)); - +static void app_update_ui(void* callbackContext) { + auto* data = (AppData*)callbackContext; bool charging_enabled = data->power->is_charging_enabled(); const char* charge_state = data->power->is_charging() ? "yes" : "no"; uint8_t charge_level = data->power->get_charge_level(); @@ -46,11 +45,10 @@ static void on_power_enabled_change(lv_event_t* event) { auto* enable_switch = static_cast(lv_event_get_target(event)); if (code == LV_EVENT_VALUE_CHANGED) { bool is_on = lv_obj_has_state(enable_switch, LV_STATE_CHECKED); - App app = lv_event_get_user_data(event); - auto* data = static_cast(tt_app_get_data(app)); + auto* data = static_cast(lv_event_get_user_data(event)); if (data->power->is_charging_enabled() != is_on) { data->power->set_charging_enabled(is_on); - app_update_ui(app); + app_update_ui(data); } } } @@ -80,7 +78,7 @@ static void app_show(App app, lv_obj_t* parent) { lv_obj_set_align(enable_label, LV_ALIGN_LEFT_MID); lv_obj_t* enable_switch = lv_switch_create(switch_container); - lv_obj_add_event_cb(enable_switch, on_power_enabled_change, LV_EVENT_ALL, app); + lv_obj_add_event_cb(enable_switch, on_power_enabled_change, LV_EVENT_ALL, data); lv_obj_set_align(enable_switch, LV_ALIGN_RIGHT_MID); data->enable_switch = enable_switch; @@ -88,27 +86,27 @@ static void app_show(App app, lv_obj_t* parent) { data->charge_level = lv_label_create(wrapper); data->current = lv_label_create(wrapper); - app_update_ui(app); - timer_start(data->update_timer, ms_to_ticks(1000)); + app_update_ui(data); + data->update_timer->start(ms_to_ticks(1000)); } static void app_hide(TT_UNUSED App app) { auto* data = static_cast(tt_app_get_data(app)); - timer_stop(data->update_timer); + data->update_timer->stop(); } static void app_start(App app) { - auto* data = static_cast(malloc(sizeof(AppData))); - tt_app_set_data(app, data); - data->update_timer = timer_alloc(&app_update_ui, TimerTypePeriodic, app); + auto* data = new AppData(); + data->update_timer = new Timer(Timer::TypePeriodic, &app_update_ui, data); data->power = get_config()->hardware->power; assert(data->power != nullptr); // The Power app only shows up on supported devices + tt_app_set_data(app, data); } static void app_stop(App app) { auto* data = static_cast(tt_app_get_data(app)); - timer_free(data->update_timer); - free(data); + delete data->update_timer; + delete data; } extern const AppManifest manifest = { diff --git a/Tactility/Source/Services/Gui/Gui.cpp b/Tactility/Source/Services/Gui/Gui.cpp index 8deed48d..e3e915f3 100644 --- a/Tactility/Source/Services/Gui/Gui.cpp +++ b/Tactility/Source/Services/Gui/Gui.cpp @@ -37,7 +37,7 @@ Gui* gui_alloc() { auto* instance = static_cast(malloc(sizeof(Gui))); memset(instance, 0, sizeof(Gui)); tt_check(instance != NULL); - instance->thread = thread_alloc_ex( + instance->thread = new Thread( "gui", 4096, // Last known minimum was 2800 for launching desktop &gui_main, @@ -56,7 +56,7 @@ Gui* gui_alloc() { void gui_free(Gui* instance) { tt_assert(instance != nullptr); - thread_free(instance->thread); + delete instance->thread; tt_mutex_free(instance->mutex); tt_check(lvgl::lock(1000 / portTICK_PERIOD_MS)); @@ -80,7 +80,7 @@ void unlock() { void request_draw() { tt_assert(gui); - ThreadId thread_id = thread_get_id(gui->thread); + ThreadId thread_id = gui->thread->getId(); thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW); } @@ -138,17 +138,17 @@ static int32_t gui_main(TT_UNUSED void* p) { static void start(TT_UNUSED Service& service) { gui = gui_alloc(); - thread_set_priority(gui->thread, THREAD_PRIORITY_SERVICE); - thread_start(gui->thread); + gui->thread->setPriority(THREAD_PRIORITY_SERVICE); + gui->thread->start(); } static void stop(TT_UNUSED Service& service) { lock(); - ThreadId thread_id = thread_get_id(gui->thread); + ThreadId thread_id = gui->thread->getId(); thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT); - thread_join(gui->thread); - thread_free(gui->thread); + gui->thread->join(); + delete gui->thread; unlock(); diff --git a/Tactility/Source/Services/Loader/Loader.cpp b/Tactility/Source/Services/Loader/Loader.cpp index 665471eb..805803aa 100644 --- a/Tactility/Source/Services/Loader/Loader.cpp +++ b/Tactility/Source/Services/Loader/Loader.cpp @@ -31,7 +31,7 @@ static Loader* loader_alloc() { loader_singleton = new Loader(); loader_singleton->pubsub_internal = tt_pubsub_alloc(); loader_singleton->pubsub_external = tt_pubsub_alloc(); - loader_singleton->thread = thread_alloc_ex( + loader_singleton->thread = new Thread( "loader", 4096, // Last known minimum was 2400 for starting Hello World app &loader_main, @@ -45,7 +45,7 @@ static Loader* loader_alloc() { static void loader_free() { tt_assert(loader_singleton != nullptr); - thread_free(loader_singleton->thread); + delete loader_singleton->thread; tt_pubsub_free(loader_singleton->pubsub_internal); tt_pubsub_free(loader_singleton->pubsub_external); tt_mutex_free(loader_singleton->mutex); @@ -334,18 +334,21 @@ static void loader_start(TT_UNUSED Service& service) { tt_check(loader_singleton == nullptr); loader_singleton = loader_alloc(); - thread_set_priority(loader_singleton->thread, THREAD_PRIORITY_SERVICE); - thread_start(loader_singleton->thread); + loader_singleton->thread->setPriority(THREAD_PRIORITY_SERVICE); + loader_singleton->thread->start(); } static void loader_stop(TT_UNUSED Service& service) { tt_check(loader_singleton != nullptr); // Send stop signal to thread and wait for thread to finish + loader_lock(); LoaderMessage message(LoaderMessageTypeServiceStop); loader_singleton->queue.put(&message, TtWaitForever); - thread_join(loader_singleton->thread); - thread_free(loader_singleton->thread); + loader_unlock(); + + loader_singleton->thread->join(); + delete loader_singleton->thread; loader_free(); loader_singleton = nullptr; diff --git a/Tactility/Source/Services/Screenshot/ScreenshotTask.cpp b/Tactility/Source/Services/Screenshot/ScreenshotTask.cpp index e405716a..05247fff 100644 --- a/Tactility/Source/Services/Screenshot/ScreenshotTask.cpp +++ b/Tactility/Source/Services/Screenshot/ScreenshotTask.cpp @@ -126,13 +126,13 @@ static int32_t screenshot_task(void* context) { static void task_start(ScreenshotTaskData* data) { task_lock(data); tt_check(data->thread == NULL); - data->thread = thread_alloc_ex( + data->thread = new Thread( "screenshot", 8192, &screenshot_task, data ); - thread_start(data->thread); + data->thread->start(); task_unlock(data); } @@ -175,10 +175,10 @@ void task_stop(ScreenshotTask* task) { data->interrupted = true; task_unlock(data); - thread_join(data->thread); + data->thread->join(); task_lock(data); - thread_free(data->thread); + delete data->thread; data->thread = nullptr; task_unlock(data); } diff --git a/Tactility/Source/Services/Statusbar/Statusbar.cpp b/Tactility/Source/Services/Statusbar/Statusbar.cpp index 62fe79dd..da1508ea 100644 --- a/Tactility/Source/Services/Statusbar/Statusbar.cpp +++ b/Tactility/Source/Services/Statusbar/Statusbar.cpp @@ -136,7 +136,7 @@ static ServiceData* service_data_alloc() { auto* data = static_cast(malloc(sizeof(ServiceData))); *data = (ServiceData) { .mutex = tt_mutex_alloc(MutexTypeNormal), - .thread = thread_alloc(), + .thread = new Thread(), .service_interrupted = false, .wifi_icon_id = lvgl::statusbar_icon_add(nullptr), .wifi_last_icon = nullptr, @@ -156,7 +156,7 @@ static ServiceData* service_data_alloc() { static void service_data_free(ServiceData* data) { tt_mutex_free(data->mutex); - thread_free(data->thread); + delete data->thread; lvgl::statusbar_icon_remove(data->wifi_icon_id); lvgl::statusbar_icon_remove(data->sdcard_icon_id); lvgl::statusbar_icon_remove(data->power_icon_id); @@ -188,11 +188,10 @@ static void on_start(Service& service) { ServiceData* data = service_data_alloc(); service.setData(data); - thread_set_callback(data->thread, service_main); - thread_set_current_priority(ThreadPriorityLow); - thread_set_stack_size(data->thread, 3000); - thread_set_context(data->thread, data); - thread_start(data->thread); + data->thread->setCallback(service_main, data); + data->thread->setPriority(Thread::PriorityLow); + data->thread->setStackSize(3000); + data->thread->start(); } static void on_stop(Service& service) { @@ -203,7 +202,7 @@ static void on_stop(Service& service) { data->service_interrupted = true; service_data_unlock(data); tt_mutex_release(data->mutex); - thread_join(data->thread); + data->thread->join(); service_data_free(data); } diff --git a/TactilityCore/Source/Thread.cpp b/TactilityCore/Source/Thread.cpp index 6675c1f6..49ba3bc3 100644 --- a/TactilityCore/Source/Thread.cpp +++ b/TactilityCore/Source/Thread.cpp @@ -1,20 +1,11 @@ #include "Thread.h" -#include -#include +#include #include "Check.h" #include "CoreDefines.h" #include "Kernel.h" #include "Log.h" -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#else -#include "FreeRTOS.h" -#include "task.h" -#endif - namespace tt { #define TAG "Thread" @@ -28,32 +19,15 @@ namespace tt { #define THREAD_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_TASK_NOTIFY) - 1U)) #define EVENT_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_EVENT_GROUPS) - 1U)) -static_assert(ThreadPriorityHighest <= TT_CONFIG_THREAD_MAX_PRIORITIES, "highest thread priority is higher than max priority"); +static_assert(Thread::PriorityHighest <= TT_CONFIG_THREAD_MAX_PRIORITIES, "highest thread priority is higher than max priority"); static_assert(TT_CONFIG_THREAD_MAX_PRIORITIES <= configMAX_PRIORITIES, "highest tactility priority is higher than max FreeRTOS priority"); -struct Thread { - ThreadState state; - int32_t ret; - - ThreadCallback callback; - void* context; - - ThreadStateCallback state_callback; - void* state_context; - - char* name; - char* appid; - - ThreadPriority priority; - - TaskHandle_t task_handle; - - // Keep all non-alignable byte types in one place, - // this ensures that the size of this structure is minimal - bool is_static; - - configSTACK_DEPTH_TYPE stack_size; -}; +void setState(Thread::Data* data, Thread::State state) { + data->state = state; + if (data->stateCallback) { + data->stateCallback(state, data->stateCallbackContext); + } +} /** Catch threads that are trying to exit wrong way */ __attribute__((__noreturn__)) void thread_catch() { //-V1082 @@ -64,238 +38,178 @@ __attribute__((__noreturn__)) void thread_catch() { //-V1082 __builtin_unreachable(); } -static void thread_set_state(Thread* thread, ThreadState state) { - tt_assert(thread); - thread->state = state; - if (thread->state_callback) { - thread->state_callback(state, thread->state_context); - } -} static void thread_body(void* context) { tt_assert(context); - auto* thread = static_cast(context); + auto* data = static_cast(context); - // Store thread instance to thread local storage + // Store thread data instance to thread local storage tt_assert(pvTaskGetThreadLocalStoragePointer(nullptr, 0) == nullptr); - vTaskSetThreadLocalStoragePointer(nullptr, 0, thread); + vTaskSetThreadLocalStoragePointer(nullptr, 0, data->thread); - tt_assert(thread->state == ThreadStateStarting); - thread_set_state(thread, ThreadStateRunning); + tt_assert(data->state == Thread::StateStarting); + setState(data, Thread::StateRunning); + data->callbackResult = data->callback(data->callbackContext); + tt_assert(data->state == Thread::StateRunning); - thread->ret = thread->callback(thread->context); - - tt_assert(thread->state == ThreadStateRunning); - - if (thread->is_static) { + if (data->isStatic) { TT_LOG_I( TAG, "%s static task memory will not be reclaimed", - thread->name ? thread->name : "" + data->name.empty() ? "" : data->name.c_str() ); } - thread_set_state(thread, ThreadStateStopped); + setState(data, Thread::StateStopped); vTaskSetThreadLocalStoragePointer(nullptr, 0, nullptr); - thread->task_handle = nullptr; + data->taskHandle = nullptr; vTaskDelete(nullptr); thread_catch(); } -Thread* thread_alloc() { - auto* thread = static_cast(malloc(sizeof(Thread))); - // TODO: create default struct instead of using memset() - memset(thread, 0, sizeof(Thread)); - thread->is_static = false; +Thread::Thread() : + data({ + .taskHandle = nullptr, + .state = StateStopped, + .callback = nullptr, + .callbackContext = nullptr, + .callbackResult = 0, + .stateCallback = nullptr, + .stateCallbackContext = nullptr, + .name = std::string(), + .priority = PriorityNormal, + .isStatic = false, + .stackSize = 0, + }) { } - Thread* parent = nullptr; - if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { - // TLS is not available, if we called not from thread context - parent = (Thread*)pvTaskGetThreadLocalStoragePointer(nullptr, 0); - - if (parent && parent->appid) { - thread_set_appid(thread, parent->appid); - } else { - thread_set_appid(thread, "unknown"); - } - } else { - // If scheduler is not started, we are starting driver thread - thread_set_appid(thread, "driver"); - } - - return thread; -} - -Thread* thread_alloc_ex( - const char* name, - uint32_t stack_size, - ThreadCallback callback, - void* context -) { - Thread* thread = thread_alloc(); - thread_set_name(thread, name); - thread_set_stack_size(thread, stack_size); - thread_set_callback(thread, callback); - thread_set_context(thread, context); - return thread; -} - -void thread_free(Thread* thread) { - tt_assert(thread); +Thread::Thread( + const std::string& name, + configSTACK_DEPTH_TYPE stackSize, + Callback callback, + _Nullable void* callbackContext) : + data({ + .taskHandle = nullptr, + .state = StateStopped, + .callback = callback, + .callbackContext = callbackContext, + .callbackResult = 0, + .stateCallback = nullptr, + .stateCallbackContext = nullptr, + .name = name, + .priority = PriorityNormal, + .isStatic = false, + .stackSize = stackSize, + }) { } +Thread::~Thread() { // Ensure that use join before free - tt_assert(thread->state == ThreadStateStopped); - tt_assert(thread->task_handle == nullptr); - - if (thread->name) free(thread->name); - if (thread->appid) free(thread->appid); - - free(thread); + tt_assert(data.state == StateStopped); + tt_assert(data.taskHandle == nullptr); } -void thread_set_name(Thread* thread, const char* name) { - tt_assert(thread); - tt_assert(thread->state == ThreadStateStopped); - if (thread->name) free(thread->name); - thread->name = name ? strdup(name) : nullptr; +void Thread::setName(const std::string& newName) { + tt_assert(data.state == StateStopped); + data.name = newName; } -void thread_set_appid(Thread* thread, const char* appid) { - tt_assert(thread); - tt_assert(thread->state == ThreadStateStopped); - if (thread->appid) free(thread->appid); - thread->appid = appid ? strdup(appid) : nullptr; + +void Thread::markAsStatic() { + data.isStatic = true; } -void thread_mark_as_static(Thread* thread) { - thread->is_static = true; +bool Thread::isMarkedAsStatic() const { + return data.isStatic; } -bool thread_mark_is_static(ThreadId thread_id) { - auto hTask = (TaskHandle_t)thread_id; - assert(!TT_IS_IRQ_MODE() && (hTask != nullptr)); - auto* thread = (Thread*)pvTaskGetThreadLocalStoragePointer(hTask, 0); - assert(thread != nullptr); - return thread->is_static; +void Thread::setStackSize(size_t stackSize) { + tt_assert(data.state == StateStopped); + tt_assert(stackSize % 4 == 0); + data.stackSize = stackSize; } -void thread_set_stack_size(Thread* thread, size_t stack_size) { - tt_assert(thread); - tt_assert(thread->state == ThreadStateStopped); - tt_assert(stack_size % 4 == 0); - thread->stack_size = stack_size; +void Thread::setCallback(Callback callback, _Nullable void* callbackContext) { + tt_assert(data.state == StateStopped); + data.callback = callback; + data.callbackContext = callbackContext; } -void thread_set_callback(Thread* thread, ThreadCallback callback) { - tt_assert(thread); - tt_assert(thread->state == ThreadStateStopped); - thread->callback = callback; -} -void thread_set_context(Thread* thread, void* context) { - tt_assert(thread); - tt_assert(thread->state == ThreadStateStopped); - thread->context = context; -} - -void thread_set_priority(Thread* thread, ThreadPriority priority) { - tt_assert(thread); - tt_assert(thread->state == ThreadStateStopped); +void Thread::setPriority(Priority priority) { + tt_assert(data.state == StateStopped); tt_assert(priority >= 0 && priority <= TT_CONFIG_THREAD_MAX_PRIORITIES); - thread->priority = priority; + data.priority = priority; } -void thread_set_current_priority(ThreadPriority priority) { - UBaseType_t new_priority = priority ? priority : ThreadPriorityNormal; - vTaskPrioritySet(nullptr, new_priority); + +void Thread::setStateCallback(StateCallback callback, _Nullable void* callbackContext) { + tt_assert(data.state == StateStopped); + data.stateCallback = callback; + data.stateCallbackContext = callbackContext; } -ThreadPriority thread_get_current_priority() { - return (ThreadPriority)uxTaskPriorityGet(nullptr); +Thread::State Thread::getState() const { + return data.state; } -void thread_set_state_callback(Thread* thread, ThreadStateCallback callback) { - tt_assert(thread); - tt_assert(thread->state == ThreadStateStopped); - thread->state_callback = callback; -} +void Thread::start() { + tt_assert(data.callback); + tt_assert(data.state == StateStopped); + tt_assert(data.stackSize > 0 && data.stackSize < (UINT16_MAX * sizeof(StackType_t))); -void thread_set_state_context(Thread* thread, void* context) { - tt_assert(thread); - tt_assert(thread->state == ThreadStateStopped); - thread->state_context = context; -} + setState(&data, StateStarting); -ThreadState thread_get_state(Thread* thread) { - tt_assert(thread); - return thread->state; -} - -void thread_start(Thread* thread) { - tt_assert(thread); - tt_assert(thread->callback); - tt_assert(thread->state == ThreadStateStopped); - tt_assert(thread->stack_size > 0 && thread->stack_size < (UINT16_MAX * sizeof(StackType_t))); - - thread_set_state(thread, ThreadStateStarting); - - uint32_t stack = thread->stack_size / sizeof(StackType_t); - UBaseType_t priority = thread->priority ? thread->priority : ThreadPriorityNormal; - if (thread->is_static) { + uint32_t stack_depth = data.stackSize / sizeof(StackType_t); + if (data.isStatic) { #if configSUPPORT_STATIC_ALLOCATION == 1 - thread->task_handle = xTaskCreateStatic( + data.taskHandle = xTaskCreateStatic( thread_body, - thread->name, - stack, - thread, - priority, - static_cast(malloc(sizeof(StackType_t) * stack)), + data.name.c_str(), + stack_depth, + &data, + data.priority, + static_cast(malloc(sizeof(StackType_t) * stack_depth)), static_cast(malloc(sizeof(StaticTask_t))) ); #else TT_LOG_E(TAG, "static tasks are not supported by current FreeRTOS config/platform - creating regular one"); - BaseType_t ret = xTaskCreate( - thread_body, thread->name, stack, thread, priority, &(thread->task_handle) + BaseType_t result = xTaskCreate( + thread_body, data.name.c_str(), stack_depth, this, data.priority, &(data.taskHandle) ); - tt_check(ret == pdPASS); + tt_check(result == pdPASS); #endif } else { - BaseType_t ret = xTaskCreate( - thread_body, thread->name, stack, thread, priority, &(thread->task_handle) + BaseType_t result = xTaskCreate( + thread_body, data.name.c_str(), stack_depth, this, data.priority, &(data.taskHandle) ); - tt_check(ret == pdPASS); + tt_check(result == pdPASS); } - tt_check(thread->state == ThreadStateStopped || thread->task_handle); + tt_check(data.state == StateStopped || data.taskHandle); } -bool thread_join(Thread* thread) { - tt_assert(thread); - - tt_check(thread_get_current() != thread); +bool Thread::join() { + tt_check(thread_get_current() != this); // !!! IMPORTANT NOTICE !!! // // If your thread exited, but your app stuck here: some other thread uses // all cpu time, which delays kernel from releasing task handle - while (thread->task_handle) { + while (data.taskHandle) { delay_ms(10); } return true; } -ThreadId thread_get_id(Thread* thread) { - tt_assert(thread); - return thread->task_handle; +ThreadId Thread::getId() { + return data.taskHandle; } -int32_t thread_get_return_code(Thread* thread) { - tt_assert(thread); - tt_assert(thread->state == ThreadStateStopped); - return thread->ret; +int32_t Thread::getReturnCode() { + tt_assert(data.state == StateStopped); + return data.callbackResult; } ThreadId thread_get_current_id() { @@ -307,11 +221,28 @@ Thread* thread_get_current() { return thread; } +void thread_set_current_priority(Thread::Priority priority) { + UBaseType_t new_priority = priority ? priority : Thread::PriorityNormal; + vTaskPrioritySet(nullptr, new_priority); +} + +Thread::Priority thread_get_current_priority() { + return (Thread::Priority)uxTaskPriorityGet(nullptr); +} + void thread_yield() { tt_assert(!TT_IS_IRQ_MODE()); taskYIELD(); } +bool thread_mark_is_static(ThreadId thread_id) { + auto hTask = (TaskHandle_t)thread_id; + assert(!TT_IS_IRQ_MODE() && (hTask != nullptr)); + auto* thread = (Thread*)pvTaskGetThreadLocalStoragePointer(hTask, 0); + assert(thread != nullptr); + return thread->isMarkedAsStatic(); +} + uint32_t thread_flags_set(ThreadId thread_id, uint32_t flags) { auto hTask = (TaskHandle_t)thread_id; uint32_t rflags; @@ -470,20 +401,6 @@ const char* thread_get_name(ThreadId thread_id) { return (name); } -const char* thread_get_appid(ThreadId thread_id) { - auto hTask = (TaskHandle_t)thread_id; - const char* appid = "system"; - - if (!TT_IS_IRQ_MODE() && (hTask != nullptr)) { - auto* thread = (Thread*)pvTaskGetThreadLocalStoragePointer(hTask, 0); - if (thread) { - appid = thread->appid; - } - } - - return (appid); -} - uint32_t thread_get_stack_space(ThreadId thread_id) { auto hTask = (TaskHandle_t)thread_id; uint32_t sz; diff --git a/TactilityCore/Source/Thread.h b/TactilityCore/Source/Thread.h index bf135d8c..0141379f 100644 --- a/TactilityCore/Source/Thread.h +++ b/TactilityCore/Source/Thread.h @@ -5,203 +5,211 @@ #include #include +#include + +#ifdef ESP_PLATFORM +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#else +#include "FreeRTOS.h" +#include "task.h" +#endif namespace tt { -/** ThreadState */ -typedef enum { - ThreadStateStopped, - ThreadStateStarting, - ThreadStateRunning, -} ThreadState; +typedef TaskHandle_t ThreadId; -/** ThreadPriority */ -typedef enum { - ThreadPriorityNone = 0, /**< Uninitialized, choose system default */ - ThreadPriorityIdle = 1, - ThreadPriorityLowest = 2, - ThreadPriorityLow = 3, - ThreadPriorityNormal = 4, - ThreadPriorityHigh = 5, - ThreadPriorityHigher = 6, - ThreadPriorityHighest = 7 -} ThreadPriority; +class Thread { +public: -#define THREAD_PRIORITY_APP ThreadPriorityNormal -#define THREAD_PRIORITY_SERVICE ThreadPriorityHigh -#define THREAD_PRIORITY_RENDER ThreadPriorityHigher + typedef enum { + StateStopped, + StateStarting, + StateRunning, + } State; + + /** ThreadPriority */ + typedef enum { + PriorityNone = 0, /**< Uninitialized, choose system default */ + PriorityIdle = 1, + PriorityLowest = 2, + PriorityLow = 3, + PriorityNormal = 4, + PriorityHigh = 5, + PriorityHigher = 6, + PriorityHighest = 7 + } Priority; + + + /** ThreadCallback Your callback to run in new thread + * @warning never use osThreadExit in Thread + */ + typedef int32_t (*Callback)(void* context); + + /** Write to stdout callback + * @param data pointer to data + * @param size data size @warning your handler must consume everything + */ + typedef void (*StdoutWriteCallback)(const char* data, size_t size); + + /** Thread state change callback called upon thread state change + * @param state new thread state + * @param context callback context + */ + typedef void (*StateCallback)(State state, void* context); + + typedef struct { + Thread* thread; + TaskHandle_t taskHandle; + + State state; + + Callback callback; + void* callbackContext; + int32_t callbackResult; + + StateCallback stateCallback; + void* stateCallbackContext; + + std::string name; + + Priority priority; + + // Keep all non-alignable byte types in one place, + // this ensures that the size of this structure is minimal + bool isStatic; + + configSTACK_DEPTH_TYPE stackSize; + } Data; + + Thread(); + + /** Allocate Thread, shortcut version + + * @param name + * @param stack_size + * @param callback + * @param context + * @return Thread* + */ + Thread( + const std::string& name, + configSTACK_DEPTH_TYPE stackSize, + Callback callback, + _Nullable void* callbackContext + ); + + ~Thread(); + + /** Set Thread name + * + * @param name string + */ + void setName(const std::string& name); + + /** Mark thread as service + * The service cannot be stopped or removed, and cannot exit from the thread body + */ + void markAsStatic(); + + /** Check if thread is as service + * If true, the service cannot be stopped or removed, and cannot exit from the thread body + */ + bool isMarkedAsStatic() const; + + /** Set Thread stack size + * + * @param thread Thread instance + * @param stackSize stack size in bytes + */ + void setStackSize(size_t stackSize); + + /** Set Thread callback + * + * @param thread Thread instance + * @param callback ThreadCallback, called upon thread run + * @param callbackContext what to pass to the callback + */ + void setCallback(Callback callback, _Nullable void* callbackContext = nullptr); + + /** Set Thread priority + * + * @param thread Thread instance + * @param priority ThreadPriority value + */ + void setPriority(Priority priority); + + + /** Set Thread state change callback + * + * @param thread Thread instance + * @param callback state change callback + * @param context pointer to context + */ + void setStateCallback(StateCallback callback, _Nullable void* callbackContext = nullptr); + + /** Get Thread state + * + * @param thread Thread instance + * + * @return thread state from ThreadState + */ + [[nodiscard]] State getState() const; + + /** Start Thread + * + * @param thread Thread instance + */ + void start(); + + /** Join Thread + * + * @warning Use this method only when CPU is not busy(Idle task receives + * control), otherwise it will wait forever. + * + * @param thread Thread instance + * + * @return success result + */ + bool join(); + + /** Get FreeRTOS ThreadId for Thread instance + * + * @param thread Thread instance + * + * @return ThreadId or nullptr + */ + ThreadId getId(); + + /** Get thread return code + * + * @param thread Thread instance + * + * @return return code + */ + int32_t getReturnCode(); + +private: + + Data data; +}; + +#define THREAD_PRIORITY_APP Thread::PriorityNormal +#define THREAD_PRIORITY_SERVICE Thread::PriorityHigh +#define THREAD_PRIORITY_RENDER Thread::PriorityHigher #define THREAD_PRIORITY_ISR (TT_CONFIG_THREAD_MAX_PRIORITIES - 1) -/** Thread anonymous structure */ -typedef struct Thread Thread; - -/** ThreadId proxy type to OS low level functions */ -typedef void* ThreadId; - -/** ThreadCallback Your callback to run in new thread - * @warning never use osThreadExit in Thread - */ -typedef int32_t (*ThreadCallback)(void* context); - -/** Write to stdout callback - * @param data pointer to data - * @param size data size @warning your handler must consume everything - */ -typedef void (*ThreadStdoutWriteCallback)(const char* data, size_t size); - -/** Thread state change callback called upon thread state change - * @param state new thread state - * @param context callback context - */ -typedef void (*ThreadStateCallback)(ThreadState state, void* context); - -/** Allocate Thread - * - * @return Thread instance - */ -Thread* thread_alloc(); - -/** Allocate Thread, shortcut version - * - * @param name - * @param stack_size - * @param callback - * @param context - * @return Thread* - */ -Thread* thread_alloc_ex( - const char* name, - uint32_t stack_size, - ThreadCallback callback, - void* context -); - -/** Release Thread - * - * @warning see tt_thread_join - * - * @param thread Thread instance - */ -void thread_free(Thread* thread); - -/** Set Thread name - * - * @param thread Thread instance - * @param name string - */ -void thread_set_name(Thread* thread, const char* name); - -/** - * @brief Set Thread appid - * Technically, it is like a "process id", but it is not a system-wide unique identifier. - * All threads spawned by the same app will have the same appid. - * - * @param thread - * @param appid - */ -void thread_set_appid(Thread* thread, const char* appid); - -/** Mark thread as service - * The service cannot be stopped or removed, and cannot exit from the thread body - * - * @param thread - */ -void thread_mark_as_static(Thread* thread); - -/** Set Thread stack size - * - * @param thread Thread instance - * @param stack_size stack size in bytes - */ -void thread_set_stack_size(Thread* thread, size_t stack_size); - -/** Set Thread callback - * - * @param thread Thread instance - * @param callback ThreadCallback, called upon thread run - */ -void thread_set_callback(Thread* thread, ThreadCallback callback); - -/** Set Thread context - * - * @param thread Thread instance - * @param context pointer to context for thread callback - */ -void thread_set_context(Thread* thread, void* context); - -/** Set Thread priority - * - * @param thread Thread instance - * @param priority ThreadPriority value - */ -void thread_set_priority(Thread* thread, ThreadPriority priority); - /** Set current thread priority * * @param priority ThreadPriority value */ -void thread_set_current_priority(ThreadPriority priority); +void thread_set_current_priority(Thread::Priority priority); /** Get current thread priority * * @return ThreadPriority value */ -ThreadPriority thread_get_current_priority(); - -/** Set Thread state change callback - * - * @param thread Thread instance - * @param callback state change callback - */ -void thread_set_state_callback(Thread* thread, ThreadStateCallback callback); - -/** Set Thread state change context - * - * @param thread Thread instance - * @param context pointer to context - */ -void thread_set_state_context(Thread* thread, void* context); - -/** Get Thread state - * - * @param thread Thread instance - * - * @return thread state from ThreadState - */ -ThreadState thread_get_state(Thread* thread); - -/** Start Thread - * - * @param thread Thread instance - */ -void thread_start(Thread* thread); - -/** Join Thread - * - * @warning Use this method only when CPU is not busy(Idle task receives - * control), otherwise it will wait forever. - * - * @param thread Thread instance - * - * @return bool - */ -bool thread_join(Thread* thread); - -/** Get FreeRTOS ThreadId for Thread instance - * - * @param thread Thread instance - * - * @return ThreadId or NULL - */ -ThreadId thread_get_id(Thread* thread); - -/** Get thread return code - * - * @param thread Thread instance - * - * @return return code - */ -int32_t thread_get_return_code(Thread* thread); +Thread::Priority thread_get_current_priority(); /** Thread related methods that doesn't involve Thread directly */ @@ -238,14 +246,6 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout); */ const char* thread_get_name(ThreadId thread_id); -/** - * @brief Get thread appid - * - * @param thread_id - * @return const char* appid - */ -const char* thread_get_appid(ThreadId thread_id); - /** * @brief Get thread stack watermark * diff --git a/TactilityCore/Source/Timer.cpp b/TactilityCore/Source/Timer.cpp index 8199502c..1e66c281 100644 --- a/TactilityCore/Source/Timer.cpp +++ b/TactilityCore/Source/Timer.cpp @@ -1,148 +1,89 @@ #include "Timer.h" #include "Check.h" #include "Kernel.h" -#include - -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#include "freertos/timers.h" -#else -#include "FreeRTOS.h" -#include "timers.h" -#endif namespace tt { -typedef struct { - TimerCallback func; - void* context; -} TimerCallback_t; - static void timer_callback(TimerHandle_t hTimer) { - auto* callback = static_cast(pvTimerGetTimerID(hTimer)); + auto* timer = static_cast(pvTimerGetTimerID(hTimer)); - if (callback != nullptr) { - callback->func(callback->context); + if (timer != nullptr) { + timer->callback(timer->callbackContext); } } -Timer* timer_alloc(TimerCallback func, TimerType type, void* context) { - tt_assert((kernel_is_irq() == 0U) && (func != nullptr)); - auto* callback = static_cast(malloc(sizeof(TimerCallback_t))); +Timer::Timer(Type type, Callback callback, void* callbackContext) { + tt_assert((kernel_is_irq() == 0U) && (callback != nullptr)); - callback->func = func; - callback->context = context; + this->callback = callback; + this->callbackContext = callbackContext; UBaseType_t reload; - if (type == TimerTypeOnce) { + if (type == TypeOnce) { reload = pdFALSE; } else { reload = pdTRUE; } - // TimerCallback function is always provided as a callback and is used to call application - // specified function with its context both stored in structure callb. - // TODO: should we use pointer to function or function directly as-is? - TimerHandle_t hTimer = xTimerCreate(nullptr, portMAX_DELAY, (BaseType_t)reload, callback, timer_callback); - tt_assert(hTimer); - - /* Return timer ID */ - return (Timer*)hTimer; + this->timerHandle = xTimerCreate(nullptr, portMAX_DELAY, (BaseType_t)reload, this, timer_callback); + tt_assert(this->timerHandle); } -void timer_free(Timer* instance) { +Timer::~Timer() { tt_assert(!kernel_is_irq()); - tt_assert(instance); - - auto hTimer = static_cast(instance); - auto* callback = static_cast(pvTimerGetTimerID(hTimer)); - - tt_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS); - - while (timer_is_running(instance)) delay_tick(2); - - /* Return allocated memory to dynamic pool */ - free(callback); + tt_check(xTimerDelete(timerHandle, portMAX_DELAY) == pdPASS); } -TtStatus timer_start(Timer* instance, uint32_t ticks) { +TtStatus Timer::start(uint32_t ticks) { tt_assert(!kernel_is_irq()); - tt_assert(instance); tt_assert(ticks < portMAX_DELAY); - auto hTimer = static_cast(instance); - TtStatus stat; - - if (xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS) { - stat = TtStatusOk; + if (xTimerChangePeriod(timerHandle, ticks, portMAX_DELAY) == pdPASS) { + return TtStatusOk; } else { - stat = TtStatusErrorResource; + return TtStatusErrorResource; } - - /* Return execution status */ - return (stat); } -TtStatus timer_restart(Timer* instance, uint32_t ticks) { +TtStatus Timer::restart(uint32_t ticks) { tt_assert(!kernel_is_irq()); - tt_assert(instance); tt_assert(ticks < portMAX_DELAY); - auto hTimer = static_cast(instance); - TtStatus stat; - - if (xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS && - xTimerReset(hTimer, portMAX_DELAY) == pdPASS) { - stat = TtStatusOk; + if (xTimerChangePeriod(timerHandle, ticks, portMAX_DELAY) == pdPASS && + xTimerReset(timerHandle, portMAX_DELAY) == pdPASS) { + return TtStatusOk; } else { - stat = TtStatusErrorResource; + return TtStatusErrorResource; } - - /* Return execution status */ - return (stat); } -TtStatus timer_stop(Timer* instance) { +TtStatus Timer::stop() { tt_assert(!kernel_is_irq()); - tt_assert(instance); - - auto hTimer = static_cast(instance); - - tt_check(xTimerStop(hTimer, portMAX_DELAY) == pdPASS); - + tt_check(xTimerStop(timerHandle, portMAX_DELAY) == pdPASS); return TtStatusOk; } -uint32_t timer_is_running(Timer* instance) { +bool Timer::isRunning() { tt_assert(!kernel_is_irq()); - tt_assert(instance); - - auto hTimer = static_cast(instance); - - /* Return 0: not running, 1: running */ - return (uint32_t)xTimerIsTimerActive(hTimer); + return xTimerIsTimerActive(timerHandle) == pdTRUE; } -uint32_t timer_get_expire_time(Timer* instance) { +uint32_t Timer::getExpireTime() { tt_assert(!kernel_is_irq()); - tt_assert(instance); - - auto hTimer = static_cast(instance); - - return (uint32_t)xTimerGetExpiryTime(hTimer); + return (uint32_t)xTimerGetExpiryTime(timerHandle); } -void timer_pending_callback(TimerPendigCallback callback, void* context, uint32_t arg) { +void Timer::pendingCallback(PendigCallback callback, void* callbackContext, uint32_t arg) { BaseType_t ret = pdFAIL; if (kernel_is_irq()) { - ret = xTimerPendFunctionCallFromISR(callback, context, arg, nullptr); + ret = xTimerPendFunctionCallFromISR(callback, callbackContext, arg, nullptr); } else { - ret = xTimerPendFunctionCall(callback, context, arg, TtWaitForever); + ret = xTimerPendFunctionCall(callback, callbackContext, arg, TtWaitForever); } tt_assert(ret == pdPASS); } -void timer_set_thread_priority(TimerThreadPriority priority) { +void Timer::setThreadPriority(TimerThreadPriority priority) { tt_assert(!kernel_is_irq()); TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle(); diff --git a/TactilityCore/Source/Timer.h b/TactilityCore/Source/Timer.h index 60f5aa4d..0354f2ad 100644 --- a/TactilityCore/Source/Timer.h +++ b/TactilityCore/Source/Timer.h @@ -2,101 +2,109 @@ #include "CoreTypes.h" +#ifdef ESP_PLATFORM +#include "freertos/FreeRTOS.h" +#include "freertos/timers.h" +#else +#include "FreeRTOS.h" +#include "timers.h" +#endif + namespace tt { -typedef void (*TimerCallback)(void* context); -typedef enum { - TimerTypeOnce = 0, ///< One-shot timer. - TimerTypePeriodic = 1 ///< Repeating timer. -} TimerType; +class Timer { +private: + TimerHandle_t timerHandle; +public: -typedef void Timer; + typedef void (*Callback)(void* context); + typedef void (*PendigCallback)(void* context, uint32_t arg); -/** Allocate timer - * - * @param[in] func The callback function - * @param[in] type The timer type - * @param context The callback context - * - * @return The pointer to Timer instance - */ -Timer* timer_alloc(TimerCallback func, TimerType type, void* context); -/** Free timer - * - * @param instance The pointer to Timer instance - */ -void timer_free(Timer* instance); + Callback callback; + void* callbackContext; -/** Start timer - * - * @warning This is asynchronous call, real operation will happen as soon as - * timer service process this request. - * - * @param instance The pointer to Timer instance - * @param[in] ticks The interval in ticks - * - * @return The status. - */ -TtStatus timer_start(Timer* instance, uint32_t ticks); + typedef enum { + TypeOnce = 0, ///< One-shot timer. + TypePeriodic = 1 ///< Repeating timer. + } Type; -/** Restart timer with previous timeout value - * - * @warning This is asynchronous call, real operation will happen as soon as - * timer service process this request. - * - * @param instance The pointer to Timer instance - * @param[in] ticks The interval in ticks - * - * @return The status. - */ -TtStatus timer_restart(Timer* instance, uint32_t ticks); + /** + * @param[in] type The timer type + * @param[in] callback The callback function + * @param callbackContext The callback context + */ + Timer(Type type, Callback callback, void* callbackContext); -/** Stop timer - * - * @warning This is asynchronous call, real operation will happen as soon as - * timer service process this request. - * - * @param instance The pointer to Timer instance - * - * @return The status. - */ -TtStatus timer_stop(Timer* instance); + ~Timer(); -/** Is timer running - * - * @warning This cal may and will return obsolete timer state if timer - * commands are still in the queue. Please read FreeRTOS timer - * documentation first. - * - * @param instance The pointer to Timer instance - * - * @return 0: not running, 1: running - */ -uint32_t timer_is_running(Timer* instance); + /** Start timer + * + * @warning This is asynchronous call, real operation will happen as soon as + * timer service process this request. + * + * @param[in] ticks The interval in ticks + * @return The status. + */ + TtStatus start(uint32_t ticks); + + /** Restart timer with previous timeout value + * + * @warning This is asynchronous call, real operation will happen as soon as + * timer service process this request. + * + * @param[in] ticks The interval in ticks + * + * @return The status. + */ + TtStatus restart(uint32_t ticks); + + + /** Stop timer + * + * @warning This is asynchronous call, real operation will happen as soon as + * timer service process this request. + * + * @return The status. + */ + TtStatus stop(); + + /** Is timer running + * + * @warning This cal may and will return obsolete timer state if timer + * commands are still in the queue. Please read FreeRTOS timer + * documentation first. + * + * @return true when running + */ + bool isRunning(); + + /** Get timer expire time + * + * @param instance The Timer instance + * + * @return expire tick + */ + uint32_t getExpireTime(); + + void pendingCallback(PendigCallback callback, void* callbackContext, uint32_t arg); + + typedef enum { + TimerThreadPriorityNormal, /**< Lower then other threads */ + TimerThreadPriorityElevated, /**< Same as other threads */ + } TimerThreadPriority; + + /** Set Timer thread priority + * + * @param[in] priority The priority + */ + void setThreadPriority(TimerThreadPriority priority); +}; -/** Get timer expire time - * - * @param instance The Timer instance - * - * @return expire tick - */ -uint32_t timer_get_expire_time(Timer* instance); -typedef void (*TimerPendigCallback)(void* context, uint32_t arg); -void timer_pending_callback(TimerPendigCallback callback, void* context, uint32_t arg); -typedef enum { - TimerThreadPriorityNormal, /**< Lower then other threads */ - TimerThreadPriorityElevated, /**< Same as other threads */ -} TimerThreadPriority; -/** Set Timer thread priority - * - * @param[in] priority The priority - */ -void timer_set_thread_priority(TimerThreadPriority priority); } // namespace diff --git a/TactilityHeadless/Source/Services/Sdcard/Sdcard.cpp b/TactilityHeadless/Source/Services/Sdcard/Sdcard.cpp index 1f4f9527..03a12b3b 100644 --- a/TactilityHeadless/Source/Services/Sdcard/Sdcard.cpp +++ b/TactilityHeadless/Source/Services/Sdcard/Sdcard.cpp @@ -22,7 +22,7 @@ static ServiceData* service_data_alloc() { auto* data = static_cast(malloc(sizeof(ServiceData))); *data = (ServiceData) { .mutex = tt_mutex_alloc(MutexTypeNormal), - .thread = thread_alloc_ex( + .thread = new Thread( "sdcard", 3000, // Minimum is ~2800 @ ESP-IDF 5.1.2 when ejecting sdcard &sdcard_task, @@ -31,13 +31,13 @@ static ServiceData* service_data_alloc() { .last_state = hal::sdcard::StateUnmounted, .interrupted = false }; - thread_set_priority(data->thread, ThreadPriorityLow); + data->thread->setPriority(Thread::PriorityLow); return data; } static void service_data_free(ServiceData* data) { tt_mutex_free(data->mutex); - thread_free(data->thread); + delete data->thread; } static void service_data_lock(ServiceData* data) { @@ -79,7 +79,7 @@ static void on_start(Service& service) { if (get_hardware_config()->sdcard != nullptr) { ServiceData* data = service_data_alloc(); service.setData(data); - thread_start(data->thread); + data->thread->start(); } else { TT_LOG_I(TAG, "task not started due to config"); } @@ -92,7 +92,7 @@ static void on_stop(Service& service) { data->interrupted = true; service_data_unlock(data); - thread_join(data->thread); + data->thread->join(); service_data_free(data); } diff --git a/Tests/TactilityCore/CMakeLists.txt b/Tests/TactilityCore/CMakeLists.txt index a81fd687..d3b97b15 100644 --- a/Tests/TactilityCore/CMakeLists.txt +++ b/Tests/TactilityCore/CMakeLists.txt @@ -7,6 +7,9 @@ set(CMAKE_CXX_COMPILER g++) file(GLOB_RECURSE TEST_SOURCES ${PROJECT_SOURCE_DIR}/*.cpp) add_executable(TactilityCoreTests EXCLUDE_FROM_ALL ${TEST_SOURCES}) +add_definitions(-D_Nullable=) +add_definitions(-D_Nonnull=) + target_include_directories(TactilityCoreTests PRIVATE ${DOCTESTINC} ) diff --git a/Tests/TactilityCore/MutexTest.cpp b/Tests/TactilityCore/MutexTest.cpp index 9291e8b7..40c4ca9a 100644 --- a/Tests/TactilityCore/MutexTest.cpp +++ b/Tests/TactilityCore/MutexTest.cpp @@ -14,23 +14,23 @@ TEST_CASE("a mutex can block a thread") { auto* mutex = tt_mutex_alloc(MutexTypeNormal); tt_mutex_acquire(mutex, TtWaitForever); - Thread* thread = thread_alloc_ex( + Thread* thread = new Thread( "thread", 1024, &thread_with_mutex_parameter, mutex ); - thread_start(thread); + thread->start(); delay_ms(5); - CHECK_EQ(thread_get_state(thread), ThreadStateRunning); + CHECK_EQ(thread->getState(), Thread::StateRunning); tt_mutex_release(mutex); delay_ms(5); - CHECK_EQ(thread_get_state(thread), ThreadStateStopped); + CHECK_EQ(thread->getState(), Thread::StateStopped); - thread_join(thread); - thread_free(thread); + thread->join(); + delete thread; tt_mutex_free(mutex); } diff --git a/Tests/TactilityCore/ThreadTest.cpp b/Tests/TactilityCore/ThreadTest.cpp index 3847aa89..20496c4d 100644 --- a/Tests/TactilityCore/ThreadTest.cpp +++ b/Tests/TactilityCore/ThreadTest.cpp @@ -25,79 +25,79 @@ static int thread_with_return_code(void* parameter) { TEST_CASE("when a thread is started then its callback should be called") { bool has_called = false; - auto* thread = thread_alloc_ex( + auto* thread = new Thread( "immediate return task", 4096, &immediate_return_thread, &has_called ); CHECK(!has_called); - thread_start(thread); - thread_join(thread); - thread_free(thread); + thread->start(); + thread->join(); + delete thread; CHECK(has_called); } TEST_CASE("a thread can be started and stopped") { bool interrupted = false; - auto* thread = thread_alloc_ex( + auto* thread = new Thread( "interruptable thread", 4096, &interruptable_thread, &interrupted ); CHECK(thread); - thread_start(thread); + thread->start(); interrupted = true; - thread_join(thread); - thread_free(thread); + thread->join(); + delete thread; } TEST_CASE("thread id should only be set at when thread is started") { bool interrupted = false; - auto* thread = thread_alloc_ex( + auto* thread = new Thread( "interruptable thread", 4096, &interruptable_thread, &interrupted ); - CHECK_EQ(thread_get_id(thread), nullptr); - thread_start(thread); - CHECK_NE(thread_get_id(thread), nullptr); + CHECK_EQ(thread->getId(), nullptr); + thread->start(); + CHECK_NE(thread->getId(), nullptr); interrupted = true; - thread_join(thread); - CHECK_EQ(thread_get_id(thread), nullptr); - thread_free(thread); + thread->join(); + CHECK_EQ(thread->getId(), nullptr); + delete thread; } TEST_CASE("thread state should be correct") { bool interrupted = false; - auto* thread = thread_alloc_ex( + auto* thread = new Thread( "interruptable thread", 4096, &interruptable_thread, &interrupted ); - CHECK_EQ(thread_get_state(thread), ThreadStateStopped); - thread_start(thread); - ThreadState state = thread_get_state(thread); - CHECK((state == ThreadStateStarting || state == ThreadStateRunning)); + CHECK_EQ(thread->getState(), Thread::StateStopped); + thread->start(); + Thread::State state = thread->getState(); + CHECK((state == Thread::StateStarting || state == Thread::StateRunning)); interrupted = true; - thread_join(thread); - CHECK_EQ(thread_get_state(thread), ThreadStateStopped); - thread_free(thread); + thread->join(); + CHECK_EQ(thread->getState(), Thread::StateStopped); + delete thread; } TEST_CASE("thread id should only be set at when thread is started") { int code = 123; - auto* thread = thread_alloc_ex( + auto* thread = new Thread( "return code", 4096, &thread_with_return_code, &code ); - thread_start(thread); - thread_join(thread); - CHECK_EQ(thread_get_return_code(thread), code); - thread_free(thread); + thread->start(); + thread->join(); + CHECK_EQ(thread->getReturnCode(), code); + delete thread; } diff --git a/Tests/TactilityCore/TimerTest.cpp b/Tests/TactilityCore/TimerTest.cpp index 58e9e335..a74f75d0 100644 --- a/Tests/TactilityCore/TimerTest.cpp +++ b/Tests/TactilityCore/TimerTest.cpp @@ -16,24 +16,25 @@ static void timer_callback_with_counter(void* context) { TEST_CASE("a timer passes the context correctly") { int foo = 1; - auto* timer = timer_alloc(&timer_callback_with_context, TimerTypeOnce, &foo); - timer_start(timer, 1); + auto* timer = new Timer(Timer::TypeOnce, &timer_callback_with_context, &foo); + timer->start(1); delay_tick(10); - timer_stop(timer); - timer_free(timer); + timer->stop(); + delete timer; CHECK_EQ(timer_callback_context, &foo); } TEST_CASE("TimerTypePeriodic timers can be stopped and restarted") { int counter = 0; - auto* timer = timer_alloc(&timer_callback_with_counter, TimerTypePeriodic, &counter); - timer_start(timer, 1); + auto* timer = new Timer(Timer::TypePeriodic, &timer_callback_with_counter, &counter); + timer->start(1); delay_tick(10); - timer_stop(timer); + timer->stop(); + timer->start(1); delay_tick(10); - timer_stop(timer); - timer_free(timer); + timer->stop(); + delete timer; CHECK_GE(counter, 2); } @@ -41,24 +42,25 @@ TEST_CASE("TimerTypePeriodic timers can be stopped and restarted") { TEST_CASE("TimerTypePeriodic calls the callback periodically") { int counter = 0; int ticks_to_run = 10; - auto* timer = timer_alloc(&timer_callback_with_counter, TimerTypePeriodic, &counter); - timer_start(timer, 1); + auto* timer = new Timer(Timer::TypePeriodic, &timer_callback_with_counter, &counter); + timer->start(1); delay_tick(ticks_to_run); - timer_stop(timer); - timer_free(timer); + timer->stop(); + delete timer; CHECK_EQ(counter, ticks_to_run); } -TEST_CASE("restarting TimerTypeOnce timers has no effect") { +TEST_CASE("restarting TimerTypeOnce timers calls the callback again") { int counter = 0; - auto* timer = timer_alloc(&timer_callback_with_counter, TimerTypeOnce, &counter); - timer_start(timer, 1); + auto* timer = new Timer(Timer::TypeOnce, &timer_callback_with_counter, &counter); + timer->start(1); delay_tick(10); - timer_stop(timer); + timer->stop(); + timer->start(1); delay_tick(10); - timer_stop(timer); - timer_free(timer); + timer->stop(); + delete timer; - CHECK_EQ(counter, 1); + CHECK_EQ(counter, 2); }