Timers & Deferred Work
oveRTOS provides two complementary primitives for time-based and deferred execution:
| Primitive | Best for | Execution context |
|---|---|---|
| Timer | Periodic or one-shot callbacks at a fixed interval | RTOS timer service (backend-specific) |
| WorkQueue | Deferring work out of ISR context or off the hot path | Dedicated worker thread per queue |
Use a Timer when you need regular time-driven callbacks (polling, statistics, heartbeats). Use a WorkQueue when you need to run heavier work that was triggered from an ISR or a latency-sensitive thread.
Both follow the standard oveRTOS allocation pattern: _init / _deinit work in zero-heap mode with caller-supplied storage, while _create / _destroy are heap-only. The matching OVE_*_DEFINE_STATIC macros in ove/storage.h combine declaration and init at file scope.
Timer
A software timer calls a user-supplied callback after a specified period. Callbacks run in a backend-specific RTOS context (e.g. the FreeRTOS timer service task) — they must be short and non-blocking. Two modes are supported, selected by the one_shot flag at init/create time:
one_shot != 0: fires once then stops automatically.one_shot == 0: periodic — reloads automatically and fires repeatedly.
State Machine
stateDiagram-v2
[*] --> IDLE : ove_timer_init() / ove_timer_create()
IDLE --> RUNNING : ove_timer_start()
RUNNING --> IDLE : ove_timer_stop()
RUNNING --> RUNNING : period elapses\n(periodic mode)\ncallback fires, auto-reload
RUNNING --> IDLE : period elapses\n(one-shot mode)\ncallback fires
RUNNING --> RUNNING : ove_timer_reset()\n(atomic stop+start — kick)
RUNNING --> RUNNING : ove_timer_start()\n(restarts from beginning of period)
IDLE --> [*] : ove_timer_deinit() / ove_timer_destroy()
RUNNING --> [*] : ove_timer_deinit() / ove_timer_destroy()\n(stops first)
Periodic vs One-Shot
graph LR
subgraph Periodic["Periodic (one_shot = 0, period T)"]
direction LR
S0(["start"]) -->|T| CB0(["callback\nfire"])
CB0 -->|"reload"| CB1(["callback\nfire"])
CB1 -->|"reload"| CB2(["…"])
end
subgraph OneShot["One-Shot (one_shot ≠ 0, period T)"]
direction LR
S1(["start"]) -->|T| CB3(["callback\nfire"])
CB3 --> DONE(["IDLE"])
DONE -.->|"ove_timer_start() to re-arm"| S1
end
style CB0 fill:#48b,stroke:#333,color:#fff
style CB1 fill:#48b,stroke:#333,color:#fff
style CB3 fill:#48b,stroke:#333,color:#fff
style DONE fill:#888,stroke:#333,color:#fff
The callback receives a handle to the fired timer and the user_data pointer supplied at init/create, so a single handler can serve multiple timers:
static void on_timer(ove_timer_t timer, void *user_data)
{
struct my_ctx *ctx = user_data;
ctx->ticks++;
}
API
| Function | Signature | Description |
|---|---|---|
ove_timer_init |
int (ove_timer_t *timer, ove_timer_storage_t *storage, ove_timer_fn callback, void *user_data, uint32_t period_ms, int one_shot) |
Initialise a timer from caller-supplied storage in the stopped state. |
ove_timer_deinit |
void (ove_timer_t timer) |
Stop and release the timer. Static storage is not freed. |
ove_timer_create |
int (ove_timer_t *timer, ove_timer_fn callback, void *user_data, uint32_t period_ms, int one_shot) |
Heap-allocate a timer in the stopped state. Requires OVE_HEAP_TIMER. |
ove_timer_destroy |
void (ove_timer_t timer) |
Stop and free a heap-allocated timer. |
ove_timer_start |
int (ove_timer_t timer) |
Arm the timer. If already running, restarts from the beginning of the period. |
ove_timer_stop |
int (ove_timer_t timer) |
Stop the timer without invoking the callback. |
ove_timer_reset |
int (ove_timer_t timer) |
Atomically stop+start — useful for watchdog-style "kick" patterns. |
Static-storage macro: OVE_TIMER_DEFINE_STATIC(name, cb, user_data, period_ms, one_shot) (in ove/storage.h).
Example: 1 Hz Stats Collection
#include <ove/ove.h>
static ove_timer_t g_stats_timer;
static void stats_cb(ove_timer_t t, void *user_data)
{
(void)t;
struct ove_audio_graph_stats stats;
ove_audio_graph_get_stats(g_graph, &stats);
OVE_LOG_INF("cycles=%llu underruns=%u avg_us=%u",
(unsigned long long)stats.cycles,
stats.underruns, stats.avg_process_us);
}
void app_init(void)
{
ove_timer_create(&g_stats_timer, stats_cb, NULL,
1000 /* ms */, 0 /* periodic */);
ove_timer_start(g_stats_timer);
}
/* On shutdown */
ove_timer_destroy(g_stats_timer);
Or, with the static-init macro (works in both heap and zero-heap modes):
OVE_TIMER_DEFINE_STATIC(g_stats_timer, stats_cb, NULL, 1000, 0);
/* … later … */
ove_timer_start(g_stats_timer);
WorkQueue
A WorkQueue is a dedicated worker thread that drains a queue of work items. Each work item carries a handler function pointer; the worker calls each handler in FIFO order. An optional delay allows items to be scheduled for future execution, and pending items can be cancelled before execution begins.
Execution Flow
graph LR
subgraph Submitters
ISR["ISR / Thread"]
end
subgraph WQ["WorkQueue"]
direction LR
FIFO["Internal Queue\n(FIFO of work items)"]
WORKER["Worker Thread\n(priority P)"]
FIFO -->|"dequeue"| WORKER
end
ISR -->|"ove_work_submit(wq, work)"| FIFO
ISR -->|"ove_work_submit_delayed(wq, work, ms)"| FIFO
WORKER -->|"handler(work)"| HANDLER["Handler\nFunction"]
style ISR fill:#a54,stroke:#333,color:#fff
style WORKER fill:#48b,stroke:#333,color:#fff
style HANDLER fill:#48b,stroke:#333,color:#fff
Work Item Lifecycle
stateDiagram-v2
[*] --> IDLE : ove_work_init() / ove_work_init_static()
IDLE --> PENDING : ove_work_submit()\nove_work_submit_delayed()
PENDING --> IDLE : ove_work_cancel()\n(if not yet executing)
PENDING --> EXECUTING : worker thread dequeues item
EXECUTING --> IDLE : handler returns
EXECUTING --> PENDING : handler calls submit() on itself\n(self-rescheduling)
API
| Function | Signature | Description |
|---|---|---|
ove_workqueue_init |
int (ove_workqueue_t *wq, ove_workqueue_storage_t *storage, const char *name, ove_prio_t priority, size_t stack_size, void *stack) |
Initialise a work queue from caller-supplied storage and stack buffer. Spawns the worker thread. |
ove_workqueue_deinit |
void (ove_workqueue_t wq) |
Stop the worker and release the queue. |
ove_workqueue_create |
int (ove_workqueue_t *wq, const char *name, ove_prio_t priority, size_t stack_size) |
Heap-allocate the queue and its stack. Requires OVE_HEAP_WORKQUEUE. |
ove_workqueue_destroy |
void (ove_workqueue_t wq) |
Stop the worker and free a heap-allocated queue. |
ove_work_init_static |
int (ove_work_t *work, ove_work_storage_t *storage, ove_work_fn handler) |
Initialise a work item from caller-supplied storage. |
ove_work_init |
int (ove_work_t *work, ove_work_fn handler) |
Heap-allocate a work item. Requires OVE_HEAP_WORKQUEUE. |
ove_work_free |
void (ove_work_t work) |
Free a heap-allocated work item. Must not be pending. |
ove_work_submit |
int (ove_workqueue_t wq, ove_work_t work) |
Enqueue for immediate execution. |
ove_work_submit_delayed |
int (ove_workqueue_t wq, ove_work_t work, uint32_t delay_ms) |
Enqueue for execution after delay_ms. |
ove_work_cancel |
int (ove_work_t work) |
Remove a pending item before it executes. Returns OVE_ERR_INVAL if not pending. |
Handler signature: typedef void (*ove_work_fn)(ove_work_t work). The handler receives the opaque work handle (not a struct pointer), so any per-item context must be associated externally — typically by deriving the work item from a wrapping OVE_WORK_DEFINE_STATIC plus a lookup table, or by handing the wrapping struct's pointer via a closure variable.
Static-storage macros (in ove/storage.h):
OVE_WORKQUEUE_DEFINE_STATIC(name, stack_sz, wq_name, prio)OVE_WORK_DEFINE_STATIC(name, handler)
Example: Deferred I/O from ISR Context
#include <ove/ove.h>
OVE_WORKQUEUE_DEFINE_STATIC(g_io_wq, 2048, "io_wq", OVE_PRIO_NORMAL);
static void save_handler(ove_work_t work)
{
(void)work;
do_blocking_save();
}
OVE_WORK_DEFINE_STATIC(g_save_work, save_handler);
/* DMA RX complete ISR */
void DMA1_Stream0_IRQHandler(void)
{
ove_work_submit(g_io_wq, g_save_work);
}
For periodic deferred work, a handler can reschedule itself:
static void periodic_work_handler(ove_work_t work)
{
do_background_housekeeping();
ove_work_submit_delayed(g_io_wq, work, 500); /* run again in 500 ms */
}
Kconfig Options
| Option | Default | Description |
|---|---|---|
CONFIG_OVE_TIMER |
n |
Enable software timer subsystem. |
CONFIG_OVE_WORKQUEUE |
n |
Enable WorkQueue subsystem. |
When the corresponding CONFIG_OVE_* is unset, every function in that subsystem becomes a static inline stub returning OVE_ERR_NOT_SUPPORTED.
Headers
| Header | Contents |
|---|---|
ove/timer.h |
Timer handle, ove_timer_fn callback typedef, init/deinit, create/destroy, start/stop/reset. |
ove/workqueue.h |
WorkQueue and work item handles, ove_work_fn typedef, queue and item init/deinit/create/destroy/submit/cancel APIs. |
ove/storage.h |
OVE_TIMER_DEFINE_STATIC, OVE_WORKQUEUE_DEFINE_STATIC, OVE_WORK_DEFINE_STATIC. |