Thread Management
oveRTOS threads are portable wrappers around the native thread primitive of each supported backend: FreeRTOS tasks, Zephyr threads, NuttX pthreads, and POSIX pthreads. The same ove/thread.h API compiles unchanged for all four backends — priority mapping, state reporting, and stack profiling are translated at compile time with no runtime indirection.
Thread States
stateDiagram-v2
[*] --> READY: ove_thread_create() / ove_thread_init()
READY --> RUNNING: scheduler selects thread
RUNNING --> READY: ove_thread_yield() / preempted
RUNNING --> BLOCKED: ove_thread_sleep_ms() / sync wait
BLOCKED --> READY: delay expires / object signalled
RUNNING --> SUSPENDED: ove_thread_suspend()
READY --> SUSPENDED: ove_thread_suspend()
SUSPENDED --> READY: ove_thread_resume()
RUNNING --> TERMINATED: entry function returns
TERMINATED --> [*]: ove_thread_destroy() / ove_thread_deinit()
| State | Meaning |
|---|---|
| READY | Thread is runnable; waiting for the CPU. |
| RUNNING | Currently executing on the CPU. |
| BLOCKED | Sleeping or waiting on a sync object; will be unblocked automatically. |
| SUSPENDED | Explicitly paused via ove_thread_suspend(); stays suspended until ove_thread_resume(). |
| TERMINATED | Entry function has returned. The handle is still valid until the thread is destroyed. |
Priority Levels
| Constant | Value | Description |
|---|---|---|
OVE_PRIO_IDLE |
0 | Lowest priority; runs only when no other thread is ready. Suitable for background statistics collection. |
OVE_PRIO_LOW |
1 | Low-priority background work (logging, cleanup). |
OVE_PRIO_BELOW_NORMAL |
2 | Below-default priority; useful for deferring non-urgent work. |
OVE_PRIO_NORMAL |
3 | Default application priority. Most application threads should use this level. |
OVE_PRIO_ABOVE_NORMAL |
4 | Slightly elevated; useful for UI or moderate-latency tasks. |
OVE_PRIO_HIGH |
5 | High priority; prefer for time-sensitive processing threads. |
OVE_PRIO_REALTIME |
6 | Real-time priority; use with care — starvation of lower-priority threads is possible. |
OVE_PRIO_CRITICAL |
7 | Highest priority; reserved for critical system tasks (watchdog feeds, safety monitors). |
Each value maps to a backend-specific numeric priority at initialisation time. Higher values mean higher scheduling priority on all backends.
Thread Lifecycle
sequenceDiagram
participant App as Application
participant Sched as Scheduler
participant T as Worker Thread
App->>Sched: ove_thread_create(&handle, STACK_SZ, &desc)
Note over Sched: Thread enters READY state
Sched->>T: entry(arg) thread begins executing
activate T
T->>Sched: ove_thread_sleep_ms(100)
Note over T,Sched: Thread moves to BLOCKED
deactivate T
Sched-->>T: delay expires, READY then RUNNING
activate T
T->>Sched: ove_thread_yield()
Note over T,Sched: Voluntarily relinquishes CPU
deactivate T
Sched-->>T: rescheduled
activate T
App->>Sched: ove_thread_suspend(handle)
Note over T,Sched: Thread moves to SUSPENDED
App->>Sched: ove_thread_resume(handle)
Note over T,Sched: Thread returns to READY
T-->>Sched: entry returns, TERMINATED
deactivate T
App->>Sched: ove_thread_destroy(handle)
Note over Sched: Resources freed
Allocation Strategies
Heap allocation — ove_thread_create / ove_thread_destroy
The preferred API. Works in both standard heap mode and zero-heap mode (CONFIG_OVE_ZERO_HEAP). In zero-heap mode the macro generates per-call-site static storage — stack_sz must be a compile-time constant. Do not call inside a loop to create multiple independent threads; use ove_thread_init() with separate storage instead.
#include <ove/ove.h>
static void worker_entry(void *arg)
{
(void)arg;
for (;;) {
/* do work */
ove_thread_sleep_ms(50);
}
}
static ove_thread_t worker;
void app_start(void)
{
struct ove_thread_desc desc = {
.name = "worker",
.entry = worker_entry,
.arg = NULL,
.priority = OVE_PRIO_NORMAL,
};
ove_thread_create(&worker, 2048, &desc);
}
Static allocation — ove_thread_init / ove_thread_deinit
Use when the thread object lives in an array, a struct, or when the allocation site cannot be a unique call-site (e.g. inside a loop). The caller supplies both a storage object and a stack buffer.
#include <ove/ove.h>
/* Declare storage at file scope (backend-specific opaque type). */
static ove_thread_storage_t worker_storage;
static uint8_t worker_stack[2048] __attribute__((aligned(8)));
static ove_thread_t worker;
void app_start(void)
{
struct ove_thread_desc desc = {
.name = "worker",
.entry = worker_entry,
.arg = NULL,
.priority = OVE_PRIO_NORMAL,
.stack_size = sizeof(worker_stack),
.stack = worker_stack,
};
ove_thread_init(&worker, &worker_storage, &desc);
}
OVE_THREAD_DEFINE_STATIC macro
Combines storage declaration, stack allocation, and initialisation into a single file-scope statement. Works in both heap and zero-heap mode.
OVE_THREAD_DEFINE_STATIC(worker, worker_entry, NULL, OVE_PRIO_NORMAL, 2048);
API Reference
| Function | Signature | Description |
|---|---|---|
ove_thread_init |
int (ove_thread_t *handle, ove_thread_storage_t *storage, const struct ove_thread_desc *desc) |
Initialise a thread from caller-supplied static storage and stack. |
ove_thread_deinit |
int (ove_thread_t handle) |
Stop and release a thread created with ove_thread_init(). Static storage is not freed. |
ove_thread_create |
int (ove_thread_t *handle, size_t stack_sz, const struct ove_thread_desc *desc) |
Create a thread (heap or zero-heap macro). stack_sz must be a compile-time constant in zero-heap mode. |
ove_thread_destroy |
int (ove_thread_t handle) |
Stop and free a thread created with ove_thread_create(). |
ove_thread_get_self |
ove_thread_t (void) |
Return the handle of the currently executing thread. |
ove_thread_set_priority |
void (ove_thread_t handle, ove_prio_t prio) |
Change the scheduling priority of a thread at runtime. |
ove_thread_sleep_ms |
void (uint32_t ms) |
Block the calling thread for at least ms milliseconds. Passing 0 yields for one scheduler tick. |
ove_thread_yield |
void (void) |
Voluntarily relinquish the CPU to another ready thread of equal or higher priority. |
ove_thread_start_scheduler |
void (void) |
Start the RTOS scheduler. Usually called indirectly via ove_run(). Does not return on most platforms. |
ove_thread_suspend |
void (ove_thread_t handle) |
Prevent a thread from being scheduled. May be called on the calling thread itself. |
ove_thread_resume |
void (ove_thread_t handle) |
Resume a previously suspended thread. |
ove_thread_get_stack_usage |
size_t (ove_thread_t handle) |
Return the historical peak stack usage in bytes (high-water mark). Returns 0 if the backend does not support stack profiling. |
ove_thread_get_state |
ove_thread_state_t (ove_thread_t handle) |
Query the current execution state of a thread. |
ove_thread_get_runtime_stats |
int (ove_thread_t handle, struct ove_thread_stats *stats) |
Retrieve total CPU time (runtime_us) and utilisation percentage (cpu_percent_x100) since the scheduler started. Returns OVE_ERR_NOT_SUPPORTED if the backend does not provide runtime accounting. |
Runtime Statistics and Stack Profiling
graph LR
T["Worker Thread<br/><small>ove_thread_t handle</small>"]
subgraph Stats
direction TB
R["ove_thread_get_runtime_stats()"]
S["ove_thread_get_stack_usage()"]
ST["ove_thread_get_state()"]
end
T --> R
T --> S
T --> ST
R --> RU["runtime_us<br/><small>total CPU time μs</small>"]
R --> CPU["cpu_percent_x100<br/><small>e.g. 1250 = 12.50%</small>"]
S --> HW["stack high-water mark<br/><small>bytes used at peak</small>"]
ST --> STATE["OVE_THREAD_STATE_*<br/><small>RUNNING / READY / BLOCKED<br/>SUSPENDED / TERMINATED</small>"]
ove_thread_get_runtime_stats() returns OVE_ERR_NOT_SUPPORTED when the backend does not provide CPU accounting. ove_thread_get_stack_usage() returns 0 when stack profiling is unavailable.
struct ove_thread_stats stats;
int rc = ove_thread_get_runtime_stats(worker, &stats);
if (rc == OVE_OK) {
/* stats.cpu_percent_x100 == 1250 means 12.50 % */
}
size_t peak = ove_thread_get_stack_usage(worker);
/* peak > 0 only when the backend supports high-water marking */
Example: Worker Thread with Sleep Loop
A typical pattern for a periodic background task — create once at startup, run forever with a fixed sleep interval, and inspect the stack peak during development.
#include <ove/ove.h>
#define WORKER_STACK_SZ 2048
#define WORKER_PERIOD_MS 100
static volatile uint32_t sensor_value;
static ove_thread_t sensor_thread;
static void sensor_entry(void *arg)
{
(void)arg;
for (;;) {
sensor_value = read_sensor();
ove_thread_sleep_ms(WORKER_PERIOD_MS);
}
}
void ove_main(void)
{
struct ove_thread_desc desc = {
.name = "sensor",
.entry = sensor_entry,
.arg = NULL,
.priority = OVE_PRIO_NORMAL,
};
ove_thread_create(&sensor_thread, WORKER_STACK_SZ, &desc);
ove_run(); /* starts the scheduler — does not return */
}
Kconfig Options
| Option | Default | Description |
|---|---|---|
CONFIG_OVE_THREAD |
always enabled | Thread management subsystem. Always compiled; cannot be disabled. The scheduler must be running before any thread API is called. |
Header
| Header | Contents |
|---|---|
ove/thread.h |
Thread descriptor struct, priority enum, state enum, runtime stats struct, all 13 thread functions/macros |