Thread, Timer and flash.sh improvements (#165)

- Various improvements to Thread and Timer:
  - Remove "mark as static" option as it is unused
  - Implemented core pinning for ESP32 platforms
  - Use `TickType_t` consistently (instead of `uint32_t`)
  - Use `enum class` instead of `enum`
- Fix for `flash.sh` not working when using `pip` to install `esptool`
This commit is contained in:
Ken Van Hoeylandt 2025-01-13 20:20:43 +01:00 committed by GitHub
parent 5d189fe5a3
commit 43c78c69d8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 151 additions and 198 deletions

View File

@ -14,7 +14,7 @@
bool tdeck_init_lvgl() { bool tdeck_init_lvgl() {
static lv_disp_t* display = nullptr; static lv_disp_t* display = nullptr;
const lvgl_port_cfg_t lvgl_cfg = { const lvgl_port_cfg_t lvgl_cfg = {
.task_priority = tt::THREAD_PRIORITY_RENDER, .task_priority = static_cast<UBaseType_t>(tt::THREAD_PRIORITY_RENDER),
.task_stack = TDECK_LVGL_TASK_STACK_DEPTH, .task_stack = TDECK_LVGL_TASK_STACK_DEPTH,
.task_affinity = -1, // core pinning .task_affinity = -1, // core pinning
.task_max_sleep_ms = 500, .task_max_sleep_ms = 500,

View File

@ -12,7 +12,7 @@
bool initLvgl() { bool initLvgl() {
const lvgl_port_cfg_t lvgl_cfg = { const lvgl_port_cfg_t lvgl_cfg = {
.task_priority = tt::THREAD_PRIORITY_RENDER, .task_priority = static_cast<UBaseType_t>(tt::THREAD_PRIORITY_RENDER),
.task_stack = CORE2_LVGL_TASK_STACK_DEPTH, .task_stack = CORE2_LVGL_TASK_STACK_DEPTH,
.task_affinity = -1, // core pinning .task_affinity = -1, // core pinning
.task_max_sleep_ms = 500, .task_max_sleep_ms = 500,

View File

@ -12,7 +12,7 @@
bool initLvgl() { bool initLvgl() {
const lvgl_port_cfg_t lvgl_cfg = { const lvgl_port_cfg_t lvgl_cfg = {
.task_priority = tt::THREAD_PRIORITY_RENDER, .task_priority = static_cast<UBaseType_t>(tt::THREAD_PRIORITY_RENDER),
.task_stack = CORE2_LVGL_TASK_STACK_DEPTH, .task_stack = CORE2_LVGL_TASK_STACK_DEPTH,
.task_affinity = -1, // core pinning .task_affinity = -1, // core pinning
.task_max_sleep_ms = 500, .task_max_sleep_ms = 500,

View File

@ -65,7 +65,7 @@ void lvgl_task_start() {
"lvgl", "lvgl",
8192, 8192,
nullptr, nullptr,
tt::Thread::PriorityHigh, // Should be higher than main app task static_cast<UBaseType_t>(tt::Thread::Priority::High), // Should be higher than main app task
nullptr nullptr
); );

View File

@ -29,7 +29,7 @@ void freertosMain() {
"main", "main",
8192, 8192,
nullptr, nullptr,
tt::Thread::PriorityNormal, static_cast<UBaseType_t>(tt::Thread::Priority::Normal),
nullptr nullptr
); );

View File

@ -7,7 +7,7 @@
bool twodotfour_lvgl_init() { bool twodotfour_lvgl_init() {
const lvgl_port_cfg_t lvgl_cfg = { const lvgl_port_cfg_t lvgl_cfg = {
.task_priority = tt::THREAD_PRIORITY_RENDER, .task_priority = static_cast<UBaseType_t>(tt::THREAD_PRIORITY_RENDER),
.task_stack = 8096, .task_stack = 8096,
.task_affinity = -1, // core pinning .task_affinity = -1, // core pinning
.task_max_sleep_ms = 500, .task_max_sleep_ms = 500,

View File

@ -21,22 +21,35 @@ function is_bin_in_path {
function require_bin { function require_bin {
program=$1 program=$1
tip=$2
if ! is_bin_in_path $program; then if ! is_bin_in_path $program; then
echo -e "\e[31m⚠ $program not found!\n\t$tip\e[0m" exit 1
else
exit 0
fi fi
} }
require_bin esptool.py "install esptool from your package manager or install python and run 'pip install esptool'" # Find either esptool (installed via system package manager) or esptool.py (installed via pip)
require_bin jq "install jq from your package manager or install python and run 'pip install jq'" if ! is_bin_in_path esptool; then
if ! is_bin_in_path esptool.py; then
echo "\e[31m⚠ esptool not found! Install it from your package manager or install python and run 'pip install esptool'\e[0m"
exit 1
else
esptoolPath=esptool.py
fi
else
esptoolPath=esptool
fi
if [[ $1 -eq 0 ]]; then # Ensure the port was specified
if [ -z "$1" ]; then
echo -e "\e[31m⚠ Must Specify port as argument. For example:\n\tflash.sh /dev/ttyACM0\n\tflash.sh /dev/ttyUSB0\e[0m" echo -e "\e[31m⚠ Must Specify port as argument. For example:\n\tflash.sh /dev/ttyACM0\n\tflash.sh /dev/ttyUSB0\e[0m"
exit -1 exit -1
fi fi
# Take the flash_arg file contents and join each line in the file into a single line
flash_args=`grep \n Binaries/flash_args | awk '{print}' ORS=' '`
cd Binaries cd Binaries
# Create flash command based on partitions $esptoolPath --port $1 erase_flash
KEY_VALUES=`jq -r '.flash_files | keys[] as $k | "\($k) \(.[$k])"' flasher_args.json | tr "\n" " "` $esptoolPath --port $1 write_flash $flash_args
esptool.py --port $1 erase_flash cd -
esptool.py --port $1 --connect-attempts 10 -b 460800 write_flash $KEY_VALUES

View File

@ -28,12 +28,9 @@
- Attach ELF data to wrapper app (as app data) (check that app state is "running"!) so you can run more than 1 external apps at a time. - Attach ELF data to wrapper app (as app data) (check that app state is "running"!) so you can run more than 1 external apps at a time.
We'll need to keep track of all manifest instances, so that the wrapper can look up the relevant manifest for the relevant callbacks. We'll need to keep track of all manifest instances, so that the wrapper can look up the relevant manifest for the relevant callbacks.
- T-Deck: Clear screen before turning on blacklight - T-Deck: Clear screen before turning on blacklight
- Audio player app
- Audio recording app
- T-Deck: Use knob for UI selection - T-Deck: Use knob for UI selection
- Crash monitoring: Keep track of which system phase the app crashed in (e.g. which app in which state) - Crash monitoring: Keep track of which system phase the app crashed in (e.g. which app in which state)
- AppContext's onResult should pass the app id (or launch request id!) that was started, so we can differentiate between multiple types of apps being launched - AppContext's onResult should pass the app id (or launch request id!) that was started, so we can differentiate between multiple types of apps being launched
- Loader: Use main dispatcher instead of Thread
- Create more unit tests for `tactility-core` and `tactility` (PC-only for now) - Create more unit tests for `tactility-core` and `tactility` (PC-only for now)
- Show a warning screen if firmware encryption or secure boot are off when saving WiFi credentials. - Show a warning screen if firmware encryption or secure boot are off when saving WiFi credentials.
- Show a warning screen when a user plugs in the SD card on a device that only supports mounting at boot. - Show a warning screen when a user plugs in the SD card on a device that only supports mounting at boot.
@ -45,6 +42,8 @@
- Support hot-plugging SD card - Support hot-plugging SD card
# Nice-to-haves # Nice-to-haves
- Audio player app
- Audio recording app
- OTA updates - OTA updates
- Web flasher - Web flasher
- T-Deck Plus: Create separate board config? - T-Deck Plus: Create separate board config?

View File

@ -74,8 +74,6 @@ const struct esp_elfsym elf_symbols[] {
ESP_ELFSYM_EXPORT(tt_thread_alloc_ext), ESP_ELFSYM_EXPORT(tt_thread_alloc_ext),
ESP_ELFSYM_EXPORT(tt_thread_free), ESP_ELFSYM_EXPORT(tt_thread_free),
ESP_ELFSYM_EXPORT(tt_thread_set_name), ESP_ELFSYM_EXPORT(tt_thread_set_name),
ESP_ELFSYM_EXPORT(tt_thread_mark_as_static),
ESP_ELFSYM_EXPORT(tt_thread_is_marked_as_static),
ESP_ELFSYM_EXPORT(tt_thread_set_stack_size), ESP_ELFSYM_EXPORT(tt_thread_set_stack_size),
ESP_ELFSYM_EXPORT(tt_thread_set_callback), ESP_ELFSYM_EXPORT(tt_thread_set_callback),
ESP_ELFSYM_EXPORT(tt_thread_set_priority), ESP_ELFSYM_EXPORT(tt_thread_set_priority),

View File

@ -31,14 +31,6 @@ void tt_thread_set_name(ThreadHandle handle, const char* name) {
HANDLE_AS_THREAD(handle)->setName(name); HANDLE_AS_THREAD(handle)->setName(name);
} }
void tt_thread_mark_as_static(ThreadHandle handle) {
HANDLE_AS_THREAD(handle)->markAsStatic();
}
bool tt_thread_is_marked_as_static(ThreadHandle handle) {
return HANDLE_AS_THREAD(handle)->isMarkedAsStatic();
}
void tt_thread_set_stack_size(ThreadHandle handle, size_t size) { void tt_thread_set_stack_size(ThreadHandle handle, size_t size) {
HANDLE_AS_THREAD(handle)->setStackSize(size); HANDLE_AS_THREAD(handle)->setStackSize(size);
} }

View File

@ -38,14 +38,14 @@ typedef int32_t (*ThreadCallback)(void* context);
typedef void (*ThreadStateCallback)(ThreadState state, void* context); typedef void (*ThreadStateCallback)(ThreadState state, void* context);
typedef enum { typedef enum {
ThreadPriorityNone = 0, /**< Uninitialized, choose system default */ ThreadPriorityNone = 0U, /**< Uninitialized, choose system default */
ThreadPriorityIdle = 1, ThreadPriorityIdle = 1U,
ThreadPriorityLowest = 2, ThreadPriorityLowest = 2U,
ThreadPriorityLow = 3, ThreadPriorityLow = 3U,
ThreadPriorityNormal = 4, ThreadPriorityNormal = 4U,
ThreadPriorityHigh = 5, ThreadPriorityHigh = 5U,
ThreadPriorityHigher = 6, ThreadPriorityHigher = 6U,
ThreadPriorityHighest = 7 ThreadPriorityHighest = 7U
} ThreadPriority; } ThreadPriority;
ThreadHandle tt_thread_alloc(); ThreadHandle tt_thread_alloc();
@ -57,8 +57,6 @@ ThreadHandle tt_thread_alloc_ext(
); );
void tt_thread_free(ThreadHandle handle); void tt_thread_free(ThreadHandle handle);
void tt_thread_set_name(ThreadHandle handle, const char* name); void tt_thread_set_name(ThreadHandle handle, const char* name);
void tt_thread_mark_as_static(ThreadHandle handle);
bool tt_thread_is_marked_as_static(ThreadHandle handle);
void tt_thread_set_stack_size(ThreadHandle handle, size_t size); void tt_thread_set_stack_size(ThreadHandle handle, size_t size);
void tt_thread_set_callback(ThreadHandle handle, ThreadCallback callback, void* _Nullable callbackContext); void tt_thread_set_callback(ThreadHandle handle, ThreadCallback callback, void* _Nullable callbackContext);
void tt_thread_set_priority(ThreadHandle handle, ThreadPriority priority); void tt_thread_set_priority(ThreadHandle handle, ThreadPriority priority);

View File

@ -58,8 +58,8 @@ bool tt_timer_set_pending_callback(TimerHandle handle, TimerPendingCallback call
); );
} }
void tt_timer_set_thread_priority(TimerHandle handle, TimerThreadPriority priority) { void tt_timer_set_thread_priority(TimerHandle handle, ThreadPriority priority) {
((TimerWrapper*)handle)->timer->setThreadPriority((tt::Timer::ThreadPriority)priority); ((TimerWrapper*)handle)->timer->setThreadPriority((tt::Thread::Priority)priority);
} }
} }

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "tt_thread.h"
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#ifdef __cplusplus #ifdef __cplusplus
@ -16,11 +17,6 @@ typedef enum {
TimerTypePeriodic = 1 ///< Repeating timer. TimerTypePeriodic = 1 ///< Repeating timer.
} TimerType; } TimerType;
typedef enum {
TimerThreadPriorityNormal, /**< Lower then other threads */
TimerThreadPriorityElevated, /**< Same as other threads */
} TimerThreadPriority;
typedef void (*TimerCallback)(void* context); typedef void (*TimerCallback)(void* context);
typedef void (*TimerPendingCallback)(void* context, uint32_t arg); typedef void (*TimerPendingCallback)(void* context, uint32_t arg);
@ -32,7 +28,7 @@ bool tt_timer_stop(TimerHandle handle);
bool tt_timer_is_running(TimerHandle handle); bool tt_timer_is_running(TimerHandle handle);
uint32_t tt_timer_get_expire_time(TimerHandle handle); uint32_t tt_timer_get_expire_time(TimerHandle handle);
bool tt_timer_set_pending_callback(TimerHandle handle, TimerPendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeoutTicks); bool tt_timer_set_pending_callback(TimerHandle handle, TimerPendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeoutTicks);
void tt_timer_set_thread_priority(TimerHandle handle, TimerThreadPriority priority); void tt_timer_set_thread_priority(TimerHandle handle, ThreadPriority priority);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -18,7 +18,7 @@ DispatcherThread::DispatcherThread(const std::string& threadName, size_t threadS
} }
DispatcherThread::~DispatcherThread() { DispatcherThread::~DispatcherThread() {
if (thread->getState() != Thread::StateStopped) { if (thread->getState() != Thread::State::Stopped) {
stop(); stop();
} }
} }

View File

@ -19,7 +19,7 @@ namespace tt {
#define THREAD_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_TASK_NOTIFY) - 1U)) #define THREAD_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_TASK_NOTIFY) - 1U))
#define EVENT_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_EVENT_GROUPS) - 1U)) #define EVENT_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_EVENT_GROUPS) - 1U))
static_assert(Thread::PriorityHighest <= TT_CONFIG_THREAD_MAX_PRIORITIES, "highest thread priority is higher than max priority"); static_assert(static_cast<UBaseType_t>(Thread::Priority::Critical) <= 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"); static_assert(TT_CONFIG_THREAD_MAX_PRIORITIES <= configMAX_PRIORITIES, "highest tactility priority is higher than max FreeRTOS priority");
void setState(Thread::Data* data, Thread::State state) { void setState(Thread::Data* data, Thread::State state) {
@ -29,6 +29,8 @@ void setState(Thread::Data* data, Thread::State state) {
} }
} }
static_assert(configSUPPORT_DYNAMIC_ALLOCATION == 1);
/** Catch threads that are trying to exit wrong way */ /** Catch threads that are trying to exit wrong way */
__attribute__((__noreturn__)) void thread_catch() { //-V1082 __attribute__((__noreturn__)) void thread_catch() { //-V1082
// If you're here it means you're probably doing something wrong // If you're here it means you're probably doing something wrong
@ -47,20 +49,12 @@ static void thread_body(void* context) {
tt_assert(pvTaskGetThreadLocalStoragePointer(nullptr, 0) == nullptr); tt_assert(pvTaskGetThreadLocalStoragePointer(nullptr, 0) == nullptr);
vTaskSetThreadLocalStoragePointer(nullptr, 0, data->thread); vTaskSetThreadLocalStoragePointer(nullptr, 0, data->thread);
tt_assert(data->state == Thread::StateStarting); tt_assert(data->state == Thread::State::Starting);
setState(data, Thread::StateRunning); setState(data, Thread::State::Running);
data->callbackResult = data->callback(data->callbackContext); data->callbackResult = data->callback(data->callbackContext);
tt_assert(data->state == Thread::StateRunning); tt_assert(data->state == Thread::State::Running);
if (data->isStatic) { setState(data, Thread::State::Stopped);
TT_LOG_I(
TAG,
"%s static task memory will not be reclaimed",
data->name.empty() ? "<unnamed service>" : data->name.c_str()
);
}
setState(data, Thread::StateStopped);
vTaskSetThreadLocalStoragePointer(nullptr, 0, nullptr); vTaskSetThreadLocalStoragePointer(nullptr, 0, nullptr);
data->taskHandle = nullptr; data->taskHandle = nullptr;
@ -73,15 +67,14 @@ Thread::Thread() :
data({ data({
.thread = nullptr, .thread = nullptr,
.taskHandle = nullptr, .taskHandle = nullptr,
.state = StateStopped, .state = State::Stopped,
.callback = nullptr, .callback = nullptr,
.callbackContext = nullptr, .callbackContext = nullptr,
.callbackResult = 0, .callbackResult = 0,
.stateCallback = nullptr, .stateCallback = nullptr,
.stateCallbackContext = nullptr, .stateCallbackContext = nullptr,
.name = std::string(), .name = std::string(),
.priority = PriorityNormal, .priority = Priority::Normal,
.isStatic = false,
.stackSize = 0, .stackSize = 0,
}) { } }) { }
@ -89,64 +82,56 @@ Thread::Thread(
const std::string& name, const std::string& name,
configSTACK_DEPTH_TYPE stackSize, configSTACK_DEPTH_TYPE stackSize,
Callback callback, Callback callback,
_Nullable void* callbackContext) : _Nullable void* callbackContext,
portBASE_TYPE affinity
) :
data({ data({
.thread = nullptr, .thread = nullptr,
.taskHandle = nullptr, .taskHandle = nullptr,
.state = StateStopped, .state = State::Stopped,
.callback = callback, .callback = callback,
.callbackContext = callbackContext, .callbackContext = callbackContext,
.callbackResult = 0, .callbackResult = 0,
.stateCallback = nullptr, .stateCallback = nullptr,
.stateCallbackContext = nullptr, .stateCallbackContext = nullptr,
.name = name, .name = name,
.priority = PriorityNormal, .priority = Priority::Normal,
.isStatic = false,
.stackSize = stackSize, .stackSize = stackSize,
.affinity = affinity
}) { } }) { }
Thread::~Thread() { Thread::~Thread() {
// Ensure that use join before free // Ensure that use join before free
tt_assert(data.state == StateStopped); tt_assert(data.state == State::Stopped);
tt_assert(data.taskHandle == nullptr); tt_assert(data.taskHandle == nullptr);
} }
void Thread::setName(const std::string& newName) { void Thread::setName(const std::string& newName) {
tt_assert(data.state == StateStopped); tt_assert(data.state == State::Stopped);
data.name = newName; data.name = newName;
} }
void Thread::markAsStatic() {
data.isStatic = true;
}
bool Thread::isMarkedAsStatic() const {
return data.isStatic;
}
void Thread::setStackSize(size_t stackSize) { void Thread::setStackSize(size_t stackSize) {
tt_assert(data.state == StateStopped); tt_assert(data.state == State::Stopped);
tt_assert(stackSize % 4 == 0); tt_assert(stackSize % 4 == 0);
data.stackSize = stackSize; data.stackSize = stackSize;
} }
void Thread::setCallback(Callback callback, _Nullable void* callbackContext) { void Thread::setCallback(Callback callback, _Nullable void* callbackContext) {
tt_assert(data.state == StateStopped); tt_assert(data.state == State::Stopped);
data.callback = callback; data.callback = callback;
data.callbackContext = callbackContext; data.callbackContext = callbackContext;
} }
void Thread::setPriority(Priority priority) { void Thread::setPriority(Priority priority) {
tt_assert(data.state == StateStopped); tt_assert(data.state == State::Stopped);
tt_assert(priority >= 0 && priority <= TT_CONFIG_THREAD_MAX_PRIORITIES);
data.priority = priority; data.priority = priority;
} }
void Thread::setStateCallback(StateCallback callback, _Nullable void* callbackContext) { void Thread::setStateCallback(StateCallback callback, _Nullable void* callbackContext) {
tt_assert(data.state == StateStopped); tt_assert(data.state == State::Stopped);
data.stateCallback = callback; data.stateCallback = callback;
data.stateCallbackContext = callbackContext; data.stateCallbackContext = callbackContext;
} }
@ -157,38 +142,49 @@ Thread::State Thread::getState() const {
void Thread::start() { void Thread::start() {
tt_assert(data.callback); tt_assert(data.callback);
tt_assert(data.state == StateStopped); tt_assert(data.state == State::Stopped);
tt_assert(data.stackSize > 0 && data.stackSize < (UINT16_MAX * sizeof(StackType_t))); tt_assert(data.stackSize > 0U && data.stackSize < (UINT16_MAX * sizeof(StackType_t)));
setState(&data, StateStarting); setState(&data, State::Starting);
uint32_t stack_depth = data.stackSize / sizeof(StackType_t); uint32_t stack_depth = data.stackSize / sizeof(StackType_t);
if (data.isStatic) {
#if configSUPPORT_STATIC_ALLOCATION == 1 BaseType_t result;
data.taskHandle = xTaskCreateStatic( if (data.affinity != -1) {
#ifdef ESP_PLATFORM
result = xTaskCreatePinnedToCore(
thread_body, thread_body,
data.name.c_str(), data.name.c_str(),
stack_depth, stack_depth,
&data, this,
data.priority, static_cast<UBaseType_t>(data.priority),
static_cast<StackType_t*>(malloc(sizeof(StackType_t) * stack_depth)), &(data.taskHandle),
static_cast<StaticTask_t*>(malloc(sizeof(StaticTask_t))) data.affinity
); );
#else #else
TT_LOG_E(TAG, "static tasks are not supported by current FreeRTOS config/platform - creating regular one"); TT_LOG_W(TAG, "Pinned tasks are not supported by current FreeRTOS platform - creating regular one");
BaseType_t result = xTaskCreate( result = xTaskCreate(
thread_body, data.name.c_str(), stack_depth, this, data.priority, &(data.taskHandle) thread_body,
data.name.c_str(),
stack_depth,
this,
static_cast<UBaseType_t>(data.priority),
&(data.taskHandle)
); );
tt_check(result == pdPASS);
#endif #endif
} else { } else {
BaseType_t result = xTaskCreate( result = xTaskCreate(
thread_body, data.name.c_str(), stack_depth, this, data.priority, &(data.taskHandle) thread_body,
data.name.c_str(),
stack_depth,
this,
static_cast<UBaseType_t>(data.priority),
&(data.taskHandle)
); );
tt_check(result == pdPASS);
} }
tt_check(data.state == StateStopped || data.taskHandle); tt_check(result == pdPASS);
tt_check(data.state == State::Stopped || data.taskHandle);
} }
bool Thread::join() { bool Thread::join() {
@ -205,12 +201,12 @@ bool Thread::join() {
return true; return true;
} }
ThreadId Thread::getId() { ThreadId Thread::getId() const {
return data.taskHandle; return data.taskHandle;
} }
int32_t Thread::getReturnCode() { int32_t Thread::getReturnCode() const {
tt_assert(data.state == StateStopped); tt_assert(data.state == State::Stopped);
return data.callbackResult; return data.callbackResult;
} }
@ -224,8 +220,7 @@ Thread* thread_get_current() {
} }
void thread_set_current_priority(Thread::Priority priority) { void thread_set_current_priority(Thread::Priority priority) {
UBaseType_t new_priority = priority ? priority : Thread::PriorityNormal; vTaskPrioritySet(nullptr, static_cast<UBaseType_t>(priority));
vTaskPrioritySet(nullptr, new_priority);
} }
Thread::Priority thread_get_current_priority() { Thread::Priority thread_get_current_priority() {
@ -237,14 +232,6 @@ void thread_yield() {
taskYIELD(); 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) { uint32_t thread_flags_set(ThreadId thread_id, uint32_t flags) {
auto hTask = (TaskHandle_t)thread_id; auto hTask = (TaskHandle_t)thread_id;
uint32_t rflags; uint32_t rflags;

View File

@ -16,24 +16,23 @@ typedef TaskHandle_t ThreadId;
class Thread { class Thread {
public: public:
typedef enum { enum class State{
StateStopped, Stopped,
StateStarting, Starting,
StateRunning, Running,
} State; };
/** ThreadPriority */ /** ThreadPriority */
typedef enum { enum class Priority : UBaseType_t {
PriorityNone = 0, /**< Uninitialized, choose system default */ None = 0U, /**< Uninitialized, choose system default */
PriorityIdle = 1, Idle = 1U,
PriorityLowest = 2, Lower = 2U,
PriorityLow = 3, Low = 3U,
PriorityNormal = 4, Normal = 4U,
PriorityHigh = 5, High = 5U,
PriorityHigher = 6, Higher = 6U,
PriorityHighest = 7 Critical = 7U
} Priority; };
/** ThreadCallback Your callback to run in new thread /** ThreadCallback Your callback to run in new thread
* @warning never use osThreadExit in Thread * @warning never use osThreadExit in Thread
@ -55,41 +54,33 @@ public:
typedef struct { typedef struct {
Thread* thread; Thread* thread;
TaskHandle_t taskHandle; TaskHandle_t taskHandle;
State state; State state;
Callback callback; Callback callback;
void* callbackContext; void* callbackContext;
int32_t callbackResult; int32_t callbackResult;
StateCallback stateCallback; StateCallback stateCallback;
void* stateCallbackContext; void* stateCallbackContext;
std::string name; std::string name;
Priority priority; 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; configSTACK_DEPTH_TYPE stackSize;
portBASE_TYPE affinity;
} Data; } Data;
Thread(); Thread();
/** Allocate Thread, shortcut version /** Allocate Thread, shortcut version
* @param[in] name * @param[in] name the name of the thread
* @param[in] stack_size * @param[in] stackSize in bytes
* @param[in] callback * @param[in] callback
* @param[in] callbackContext * @param[in] callbackContext
* @return Thread* * @param[in] affinity Which CPU core to pin this task to, -1 means unpinned (only works on ESP32)
*/ */
Thread( Thread(
const std::string& name, const std::string& name,
configSTACK_DEPTH_TYPE stackSize, configSTACK_DEPTH_TYPE stackSize,
Callback callback, Callback callback,
_Nullable void* callbackContext _Nullable void* callbackContext,
portBASE_TYPE affinity = -1
); );
~Thread(); ~Thread();
@ -99,16 +90,6 @@ public:
*/ */
void setName(const std::string& name); 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 /** Set Thread stack size
* @param[in] stackSize stack size in bytes * @param[in] stackSize stack size in bytes
*/ */
@ -137,8 +118,7 @@ public:
*/ */
State getState() const; State getState() const;
/** Start Thread /** Start Thread */
*/
void start(); void start();
/** Join Thread /** Join Thread
@ -150,10 +130,10 @@ public:
/** Get FreeRTOS ThreadId for Thread instance /** Get FreeRTOS ThreadId for Thread instance
* @return ThreadId or nullptr * @return ThreadId or nullptr
*/ */
ThreadId getId(); ThreadId getId() const;
/** @return thread return code */ /** @return thread return code */
int32_t getReturnCode(); int32_t getReturnCode() const;
private: private:
@ -161,9 +141,9 @@ private:
}; };
#define THREAD_PRIORITY_APP Thread::PriorityNormal #define THREAD_PRIORITY_APP Thread::PriorityNormal
#define THREAD_PRIORITY_SERVICE Thread::PriorityHigh #define THREAD_PRIORITY_SERVICE Thread::Priority::High
#define THREAD_PRIORITY_RENDER Thread::PriorityHigher #define THREAD_PRIORITY_RENDER Thread::Priority::Higher
#define THREAD_PRIORITY_ISR (TT_CONFIG_THREAD_MAX_PRIORITIES - 1) #define THREAD_PRIORITY_ISR Thread::Priority::Critical
/** Set current thread priority /** Set current thread priority
* @param[in] priority ThreadPriority value * @param[in] priority ThreadPriority value
@ -220,10 +200,4 @@ void thread_resume(ThreadId threadId);
*/ */
bool thread_is_suspended(ThreadId threadId); bool thread_is_suspended(ThreadId threadId);
/** Check if the thread was created with static memory
* @param[in] threadId thread id
* @return true if thread memory is static
*/
bool thread_mark_is_static(ThreadId threadId);
} // namespace } // namespace

View File

@ -36,16 +36,16 @@ Timer::~Timer() {
tt_check(xTimerDelete(timerHandle, portMAX_DELAY) == pdPASS); tt_check(xTimerDelete(timerHandle, portMAX_DELAY) == pdPASS);
} }
bool Timer::start(uint32_t intervalTicks) { bool Timer::start(TickType_t interval) {
tt_assert(!TT_IS_ISR()); tt_assert(!TT_IS_ISR());
tt_assert(intervalTicks < portMAX_DELAY); tt_assert(interval < portMAX_DELAY);
return xTimerChangePeriod(timerHandle, intervalTicks, portMAX_DELAY) == pdPASS; return xTimerChangePeriod(timerHandle, interval, portMAX_DELAY) == pdPASS;
} }
bool Timer::restart(uint32_t intervalTicks) { bool Timer::restart(TickType_t interval) {
tt_assert(!TT_IS_ISR()); tt_assert(!TT_IS_ISR());
tt_assert(intervalTicks < portMAX_DELAY); tt_assert(interval < portMAX_DELAY);
return xTimerChangePeriod(timerHandle, intervalTicks, portMAX_DELAY) == pdPASS && return xTimerChangePeriod(timerHandle, interval, portMAX_DELAY) == pdPASS &&
xTimerReset(timerHandle, portMAX_DELAY) == pdPASS; xTimerReset(timerHandle, portMAX_DELAY) == pdPASS;
} }
@ -59,9 +59,9 @@ bool Timer::isRunning() {
return xTimerIsTimerActive(timerHandle) == pdTRUE; return xTimerIsTimerActive(timerHandle) == pdTRUE;
} }
uint32_t Timer::getExpireTime() { TickType_t Timer::getExpireTime() {
tt_assert(!TT_IS_ISR()); tt_assert(!TT_IS_ISR());
return (uint32_t)xTimerGetExpiryTime(timerHandle); return xTimerGetExpiryTime(timerHandle);
} }
bool Timer::setPendingCallback(PendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeout) { bool Timer::setPendingCallback(PendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeout) {
@ -73,19 +73,13 @@ bool Timer::setPendingCallback(PendingCallback callback, void* callbackContext,
} }
} }
void Timer::setThreadPriority(ThreadPriority priority) { void Timer::setThreadPriority(Thread::Priority priority) {
tt_assert(!TT_IS_ISR()); tt_assert(!TT_IS_ISR());
TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle(); TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle();
tt_assert(task_handle); // Don't call this method before timer task start tt_assert(task_handle); // Don't call this method before timer task start
if (priority == TimerThreadPriorityNormal) { vTaskPrioritySet(task_handle, static_cast<UBaseType_t>(priority));
vTaskPrioritySet(task_handle, configTIMER_TASK_PRIORITY);
} else if (priority == TimerThreadPriorityElevated) {
vTaskPrioritySet(task_handle, configMAX_PRIORITIES - 1);
} else {
tt_crash("Unsupported timer priority");
}
} }
} // namespace } // namespace

View File

@ -3,6 +3,7 @@
#include "CoreTypes.h" #include "CoreTypes.h"
#include "RtosCompatTimers.h" #include "RtosCompatTimers.h"
#include "Thread.h"
#include <memory> #include <memory>
namespace tt { namespace tt {
@ -34,17 +35,17 @@ public:
/** Start timer /** Start timer
* @warning This is asynchronous call, real operation will happen as soon as timer service process this request. * @warning This is asynchronous call, real operation will happen as soon as timer service process this request.
* @param[in] ticks The interval in ticks * @param[in] interval The interval in ticks
* @return success result * @return success result
*/ */
bool start(uint32_t intervalTicks); bool start(TickType_t interval);
/** Restart timer with previous timeout value /** Restart timer with previous timeout value
* @warning This is asynchronous call, real operation will happen as soon as timer service process this request. * @warning This is asynchronous call, real operation will happen as soon as timer service process this request.
* @param[in] ticks The interval in ticks * @param[in] interval The interval in ticks
* @return success result * @return success result
*/ */
bool restart(uint32_t intervalTicks); bool restart(TickType_t interval);
/** Stop timer /** Stop timer
* @warning This is asynchronous call, real operation will happen as soon as timer service process this request. * @warning This is asynchronous call, real operation will happen as soon as timer service process this request.
@ -61,7 +62,7 @@ public:
/** Get timer expire time /** Get timer expire time
* @return expire tick * @return expire tick
*/ */
uint32_t getExpireTime(); TickType_t getExpireTime();
/** /**
* Calls xTimerPendFunctionCall internally. * Calls xTimerPendFunctionCall internally.
@ -69,18 +70,19 @@ public:
* @param[in] callbackContext the first function argument * @param[in] callbackContext the first function argument
* @param[in] callbackArg the second function argument * @param[in] callbackArg the second function argument
* @param[in] timeout the function timeout (must set to 0 in ISR mode) * @param[in] timeout the function timeout (must set to 0 in ISR mode)
* @return true on success
*/ */
bool setPendingCallback(PendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeout); bool setPendingCallback(PendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeout);
typedef enum { enum class Priority{
TimerThreadPriorityNormal, /**< Lower then other threads */ Normal, /**< Lower then other threads */
TimerThreadPriorityElevated, /**< Same as other threads */ Elevated, /**< Same as other threads */
} ThreadPriority; };
/** Set Timer thread priority /** Set Timer thread priority
* @param[in] priority The priority * @param[in] priority The priority
*/ */
void setThreadPriority(ThreadPriority priority); void setThreadPriority(Thread::Priority priority);
}; };
} // namespace } // namespace

View File

@ -23,12 +23,12 @@ TEST_CASE("a mutex can block a thread") {
thread.start(); thread.start();
kernel::delayMillis(5); kernel::delayMillis(5);
CHECK_EQ(thread.getState(), Thread::StateRunning); CHECK_EQ(thread.getState(), Thread::State::Running);
mutex.unlock(); mutex.unlock();
kernel::delayMillis(5); kernel::delayMillis(5);
CHECK_EQ(thread.getState(), Thread::StateStopped); CHECK_EQ(thread.getState(), Thread::State::Stopped);
thread.join(); thread.join();
} }

View File

@ -78,13 +78,13 @@ TEST_CASE("thread state should be correct") {
&interruptable_thread, &interruptable_thread,
&interrupted &interrupted
); );
CHECK_EQ(thread->getState(), Thread::StateStopped); CHECK_EQ(thread->getState(), Thread::State::Stopped);
thread->start(); thread->start();
Thread::State state = thread->getState(); Thread::State state = thread->getState();
CHECK((state == Thread::StateStarting || state == Thread::StateRunning)); CHECK((state == Thread::State::Starting || state == Thread::State::Running));
interrupted = true; interrupted = true;
thread->join(); thread->join();
CHECK_EQ(thread->getState(), Thread::StateStopped); CHECK_EQ(thread->getState(), Thread::State::Stopped);
delete thread; delete thread;
} }