Skip to content

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.

ove_main() locals are UB after ove_run()

Anything in ove_main()'s automatic storage becomes dangling once the scheduler starts. Pass long-lived state to workers via static locals, file-scope variables, or heap allocation. See Troubleshooting → ove_main locals for the full rule.

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.
UNKNOWN State could not be determined by the backend.

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, "name", entry, arg, prio, STACK_SZ)
    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 simpler API when the heap is available. Both the backend storage object and the stack are allocated from the RTOS heap. Available only when OVE_HEAP_THREAD is defined (i.e. CONFIG_OVE_ZERO_HEAP is not set).

#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)
{
    ove_thread_create(&worker, "worker", worker_entry, NULL, OVE_PRIO_NORMAL, 2048);
}

Static allocation — ove_thread_init / ove_thread_deinit

Works in both heap and zero-heap mode. 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)
{
    ove_thread_init(&worker, &worker_storage, "worker", worker_entry, NULL,
                    OVE_PRIO_NORMAL, sizeof(worker_stack), worker_stack);
}

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. Parameters: (hname, stack_sz, fn, ctx, prio, tname).

OVE_THREAD_DEFINE_STATIC(worker, 2048, worker_entry, NULL, OVE_PRIO_NORMAL, "worker");

API Reference

Function Signature Description
ove_thread_init int (ove_thread_t *handle, ove_thread_storage_t *storage, const char *name, ove_thread_fn entry, void *arg, ove_prio_t priority, size_t stack_size, void *stack) 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, const char *name, ove_thread_fn entry, void *arg, ove_prio_t priority, size_t stack_size) Heap-allocate and start a thread. Requires OVE_HEAP_THREAD (not declared in zero-heap mode).
ove_thread_destroy int (ove_thread_t handle) Stop and free a thread created with ove_thread_create(). Heap mode only.
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_request_stop void (ove_thread_t handle) Cooperative stop signal. The thread keeps running until it observes ove_thread_should_stop() and exits its entry function.
ove_thread_should_stop bool (ove_thread_t handle) Returns true once ove_thread_request_stop() has been called for the thread. Polled inside the worker's main loop.
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.
ove_thread_list int (struct ove_thread_info *out, size_t max_count, size_t *actual_count) Enumerate all threads. Each entry includes name, state, priority, stack usage/size, CPU usage, and per-state cumulative time. Returns OVE_ERR_NOT_SUPPORTED if unavailable.
ove_sys_get_mem_stats int (struct ove_mem_stats *stats) Query system heap totals: total, free, used, peak_used bytes. Returns OVE_ERR_NOT_SUPPORTED if unavailable.

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)
{
    ove_thread_create(&sensor_thread, "sensor", sensor_entry, NULL,
                      OVE_PRIO_NORMAL, WORKER_STACK_SZ);
    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 Contents
ove/thread.h Priority enum (ove_prio_t), state enum (ove_thread_state_t), runtime stats (struct ove_thread_stats), thread info / state-time / memory stat structs, 16 thread/sys functions and the OVE_THREAD_DEFINE_STATIC macro (in ove/storage.h).