Skip to content

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 Timer daemon thread (configurable priority)
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.


Timer

A software timer calls a user-supplied callback after a specified period. Timers run on a shared timer daemon thread — callbacks must be short and non-blocking. Two modes are supported:

  • One-shot: fires once then transitions to IDLE.
  • Periodic: automatically reloads and fires repeatedly at the configured interval.

State Machine

stateDiagram-v2
    [*]     --> IDLE    : 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

    IDLE    --> RUNNING : ove_timer_reset()\n(re-arms from now)
    RUNNING --> RUNNING : ove_timer_reset()\n(reloads deadline from now)

    RUNNING --> RUNNING : ove_timer_set_period()\n(new period takes effect\non next reload)

    IDLE    --> [*]     : ove_timer_destroy()
    RUNNING --> [*]     : ove_timer_destroy()\n(stops first)

Periodic vs One-Shot

graph LR
    subgraph Periodic["Periodic  (period T)"]
        direction LR
        S0(["start"]) -->|T| CB0(["callback\nfire"])
        CB0 -->|"reload"| CB1(["callback\nfire"])
        CB1 -->|"reload"| CB2(["…"])
    end

    subgraph OneShot["One-Shot  (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, allowing a single function to serve multiple timers:

static void on_timer(ove_timer_t timer, void *arg)
{
    struct my_ctx *ctx = arg;
    ctx->ticks++;
}

API

Function Description
ove_timer_init(cfg) Initialise timer subsystem (starts daemon thread)
ove_timer_deinit() Tear down timer subsystem
ove_timer_create(period_ms, mode, cb, arg) Allocate timer; mode is OVE_TIMER_PERIODIC or OVE_TIMER_ONESHOT
ove_timer_destroy(t) Stop and free timer
ove_timer_start(t) Arm timer; begins counting from now
ove_timer_stop(t) Disarm timer without destroying it
ove_timer_reset(t) Reload deadline from now (arm if not already running)
ove_timer_set_period(t, period_ms) Change period; takes effect on next reload

ove_timer_start on an already-running timer is a no-op. Use ove_timer_reset to restart the countdown.

Example: 1 Hz Stats Collection

static ove_timer_t g_stats_timer;

static void stats_cb(ove_timer_t t, void *arg)
{
    struct ove_audio_stats stats;
    ove_audio_graph_get_stats(&g_graph, &stats);
    LOG_INF("cycles=%u underruns=%u avg_us=%u",
            stats.cycles, stats.underruns, stats.avg_process_us);
}

/* Startup */
g_stats_timer = ove_timer_create(1000,              /* 1 Hz */
                                  OVE_TIMER_PERIODIC,
                                  stats_cb, NULL);
ove_timer_start(g_stats_timer);

/* On shutdown */
ove_timer_destroy(g_stats_timer);

WorkQueue

A WorkQueue is a dedicated worker thread that drains a queue of work items. Each item carries a function pointer and an argument; the worker calls each item's handler in FIFO order. An optional delay allows items to be scheduled for future execution.

Work items are value types: callers initialise them on the stack or in static storage and submit them by pointer. An item may be re-submitted from inside its own handler to create a self-rescheduling pattern.

Execution Flow

graph LR
    subgraph Submitters
        ISR["ISR"]
        THR["Any 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_workqueue_submit_from_isr(wq, item)"| FIFO
    THR -->|"ove_workqueue_submit(wq, item)"| FIFO
    THR -->|"ove_workqueue_submit_delayed(wq, item, ms)"| FIFO

    WORKER -->|"item->handler(item)"| HANDLER["Handler\nFunction"]

    style ISR     fill:#a54,stroke:#333,color:#fff
    style THR     fill:#4a9,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()

    IDLE      --> PENDING   : ove_workqueue_submit()\nove_workqueue_submit_delayed()
    IDLE      --> PENDING   : ove_workqueue_submit_from_isr()

    PENDING   --> EXECUTING : worker thread dequeues item

    EXECUTING --> IDLE      : handler returns\n(item is safe to re-use or free)
    EXECUTING --> PENDING   : handler calls submit() on itself\n(self-rescheduling)

    note right of PENDING
        Item must not be
        modified while PENDING
    end note

Once an item is submitted it must not be written until the handler has returned (i.e., it transitions back to IDLE). Check ove_work_is_pending(item) before re-submitting from outside the handler.

API

Function Description
ove_workqueue_init(cfg) Initialise WorkQueue subsystem
ove_workqueue_deinit() Tear down subsystem
ove_workqueue_create(depth, priority, stack_size) Allocate queue and spawn worker thread
ove_workqueue_destroy(wq) Drain queue, stop worker, free resources
ove_work_init(item, handler) Initialise a work item with a handler function
ove_workqueue_submit(wq, item) Enqueue item for immediate execution
ove_workqueue_submit_delayed(wq, item, delay_ms) Enqueue item to run after delay_ms milliseconds
ove_workqueue_submit_from_isr(wq, item, woken) ISR-safe submit (no delay variant)
ove_work_is_pending(item) Returns true if item is queued or currently executing

Example: Deferred File I/O from ISR Context

A DMA completion ISR must not block on file I/O. Instead it submits a work item that runs on the worker thread.

static ove_workqueue_t g_io_wq;

struct save_ctx {
    struct ove_work  work;      /* must be first member */
    uint8_t          buf[256];
    size_t           len;
};

static void save_handler(struct ove_work *item)
{
    struct save_ctx *ctx = (struct save_ctx *)item;
    ove_storage_write("/data/capture.bin", ctx->buf, ctx->len);
}

/* Startup */
g_io_wq = ove_workqueue_create(8,          /* queue depth   */
                                4,          /* thread priority */
                                2048);      /* stack bytes   */

/* Static item — safe to re-use after handler returns */
static struct save_ctx g_save_ctx;
ove_work_init(&g_save_ctx.work, save_handler);

/* DMA RX complete ISR */
void DMA1_Stream0_IRQHandler(void)
{
    if (ove_work_is_pending(&g_save_ctx.work))
        return;  /* previous save still in flight, drop frame */

    memcpy(g_save_ctx.buf, dma_rx_buf, DMA_RX_LEN);
    g_save_ctx.len = DMA_RX_LEN;

    bool woken = false;
    ove_workqueue_submit_from_isr(g_io_wq, &g_save_ctx.work, &woken);
    portYIELD_FROM_ISR(woken);
}

For periodic deferred work, a handler can reschedule itself:

static void periodic_work_handler(struct ove_work *item)
{
    do_background_housekeeping();
    /* re-arm: run again in 500 ms */
    ove_workqueue_submit_delayed(g_io_wq, item, 500);
}

Kconfig Options

Option Default Description
CONFIG_OVE_TIMER y Enable software timer subsystem
CONFIG_OVE_TIMER_DAEMON_PRIORITY 6 Priority of the timer daemon thread
CONFIG_OVE_TIMER_DAEMON_STACK 1024 Stack size (bytes) of the timer daemon thread
CONFIG_OVE_WORKQUEUE n Enable WorkQueue subsystem

Headers

Header Contents
ove/timer.h Timer handle, mode enum, create/destroy/start/stop/reset API
ove/workqueue.h WorkQueue handle, work item struct, submit API