Skip to content

File syn_pt.h

FileList > pt > syn_pt.h

Go to the source code of this file

Protothreads — stackless cooperative coroutines for C. More...

  • #include "../common/syn_compiler.h"
  • #include "../common/syn_defs.h"

Classes

Type Name
struct SYN_PT
Protothread control block.

Public Types

Type Name
enum SYN_PT_Status
Return value from a protothread function.

Macros

Type Name
define PT_BEGIN (pt) /* multi line expression */
Open a protothread body. Must be the first statement in the protothread function, before any PT_* operations.
define PT_BLOCK_CONDITION (pt, task, cond) /* multi line expression */
Sleep task (SYN_TASK_BLOCKED) until explicitly resumed.
define PT_BLOCK_EVENT (pt, task, grp, mask) /* multi line expression */
Block until ANY bit in mask is set in the event group.
define PT_BLOCK_EVENT_WITH_TIMEOUT (pt, task, grp, mask, timeout_ms, out_flags) /* multi line expression */
Block task execution (SYN_TASK_BLOCKED) until ANY bit in mask is set ORtimeout_ms milliseconds elapse.
define PT_DEFER (pt, task) /* multi line expression */
Defer to all ready tasks, regardless of priority.
define PT_DELAY_MS (pt, target, ms) /* multi line expression */
Non-blocking delay for ms milliseconds.
define PT_DELAY_US (pt, target, us) /* multi line expression */
Non-blocking sub-millisecond delay for us microseconds.
define PT_END (pt) /* multi line expression */
Close a protothread body. Returns PT_EXITED and resets the continuation so the thread can be restarted if desired.
define PT_EXIT (pt) /* multi line expression */
Terminate the protothread immediately.
define PT_INIT (pt) ((pt)->lc = 0)
Initialize (or reset) a protothread so it starts from the top.
define PT_IS_IDLE (pt) ((pt)->lc == 0)
define PT_IS_RUNNING (pt) ((pt)->lc != 0)
define PT_RESTART (pt) /* multi line expression */
Reset the protothread and restart from PT_BEGIN on next call.
define PT_SPAWN (pt, child, func) /* multi line expression */
Spawn a child protothread and block until it exits.
define PT_TASK_DELAY_MS (pt, task, ms) [**PT\_DELAY\_MS**](syn__pt_8h.md#define-pt_delay_ms)(pt, &(task)->delay\_until, ms)
Convenience form for use with the scheduler's SYN_Task (milliseconds).
define PT_WAIT_UNTIL (pt, cond) /* multi line expression */
Block until cond evaluates to true.
define PT_WAIT_WHILE (pt, cond) [**PT\_WAIT\_UNTIL**](syn__pt_8h.md#define-pt_wait_until)(pt, !(cond))
Block while cond is true. Dual of PT_WAIT_UNTIL.
define PT_YIELD (pt) /* multi line expression */
Yield control unconditionally. The protothread will resume at this point on the next call.
define PT_YIELD_UNTIL (pt, cond) /* multi line expression */
Yield while cond is true.

Detailed Description

Protothreads provide lightweight, cooperative multitasking without requiring a separate stack per thread. Each protothread is a normal C function that uses macros to save and restore its execution position via the switch/__LINE__ continuation trick (Duff's device).

A protothread costs 2 bytes of RAM (the continuation variable).

** **

  • You cannot use a bare switch statement inside a protothread body (use if/else if chains instead).
  • Local variables are NOT preserved across yield/wait points. Store persistent state in static variables, globals, or a struct passed via the task's user_data pointer.
  • A protothread must reside entirely within a single function.

** **

static SYN_PT pt;
PT_INIT(&pt);

while (1) {
    my_protothread(&pt);
    // ... poll other protothreads ...
}

** **

// See syn_sched.h for the full pattern.
static SYN_PT_Status my_task(SYN_PT *pt, SYN_Task *task) {
    PT_BEGIN(pt);
    // ...
    PT_END(pt);
}

** **

// High-level C code
SYN_PT_Status my_task(SYN_PT *pt) {
    PT_BEGIN(pt);
    PT_WAIT_UNTIL(pt, flag == 1);
    PT_END(pt);
}

// Expands preprocessor output to:
SYN_PT_Status my_task(SYN_PT *pt) {
    char _pt_yield_flag = 1;
    switch (pt->lc) {
    case 0:
        // On resume, switch(pt->lc) jumps directly to case 12:
        pt->lc = 12; case 12:
        if (!(flag == 1)) return PT_WAITING;
    }
    _pt_yield_flag = 0;
    PT_INIT(pt);
    return PT_EXITED;
}

Public Types Documentation

enum SYN_PT_Status

Return value from a protothread function.

enum SYN_PT_Status {
    PT_WAITING = 0,
    PT_YIELDED = 1,
    PT_EXITED = 2,
    PT_ENDED = 3
};


Macro Definition Documentation

define PT_BEGIN

Open a protothread body. Must be the first statement in the protothread function, before any PT_* operations.

#define PT_BEGIN (
    pt
) `/* multi line expression */`

Expands to the head of a switch statement that jumps to the last saved continuation point.


define PT_BLOCK_CONDITION

Sleep task (SYN_TASK_BLOCKED) until explicitly resumed.

#define PT_BLOCK_CONDITION (
    pt,
    task,
    cond
) `/* multi line expression */`

Warning:

This macro does NOT poll cond on each scheduler tick. Once the task yields as BLOCKED, the scheduler skips it entirely. The condition is only re-evaluated when the task is explicitly woken by an ISR or driver calling syn_task_resume(task).

For polled condition blocking, use PT_WAIT_UNTIL(pt, cond) instead. For event-flag-driven blocking, use PT_BLOCK_EVENT(pt, task, grp, mask).

Parameters:

  • pt Protothread.
  • task Pointer to the SYN_Task struct.
  • cond Boolean condition expression to evaluate.

define PT_BLOCK_EVENT

Block until ANY bit in mask is set in the event group.

#define PT_BLOCK_EVENT (
    pt,
    task,
    grp,
    mask
) `/* multi line expression */`

Unlike PT_WAIT_EVENT (which polls every scheduler pass), this macro sets the task state to BLOCKED. The scheduler skips the task entirely until the event fires, saving CPU and enabling proper tickless sleep.

Matched flags are auto-cleared after the task resumes.

Note:

The clear runs after PT_YIELD returns (i.e. after the scheduler has already detected the flags during its scan), so it is safe for the scheduler to use the flags as a wakeup condition.

Parameters:

  • pt Protothread.
  • task Pointer to the SYN_Task struct.
  • grp Pointer to SYN_EventGroup.
  • mask Bitmask of event flags to wait for (any of them).

define PT_BLOCK_EVENT_WITH_TIMEOUT

Block task execution (SYN_TASK_BLOCKED) until ANY bit in mask is set ORtimeout_ms milliseconds elapse.

#define PT_BLOCK_EVENT_WITH_TIMEOUT (
    pt,
    task,
    grp,
    mask,
    timeout_ms,
    out_flags
) `/* multi line expression */`

Combines event-flag blocking with millisecond deadline tracking. The task is skipped by the scheduler until either the event fires or the timeout deadline passes.

Matched event flags are captured into out_flags (if non-NULL) and auto-cleared ONLY if the event actually fired before the timeout. Check *out_flags != 0 to determine if the event fired vs timed out.

Parameters:

  • pt Protothread.
  • task Pointer to the SYN_Task struct.
  • grp Pointer to SYN_EventFlags.
  • mask Bitmask of event flags to wait for.
  • timeout_ms Timeout duration in milliseconds.
  • out_flags Pointer to uint32_t receiving matched flags (or 0 on timeout).

define PT_DEFER

Defer to all ready tasks, regardless of priority.

#define PT_DEFER (
    pt,
    task
) `/* multi line expression */`

Unlike PT_YIELD (which only round-robins among equal-priority tasks), this macro allows any lower-priority ready task to run before this task is scheduled again. The task is skipped for exactly one scheduler pass.

Use this when a high-priority task has no immediate work but doesn't want to commit to a timed delay.

Parameters:

  • pt Protothread.
  • task Pointer to the SYN_Task struct.

define PT_DELAY_MS

Non-blocking delay for ms milliseconds.

#define PT_DELAY_MS (
    pt,
    target,
    ms
) `/* multi line expression */`

Requires a uint32_t variable to store the deadline. When used with the scheduler, pass &(task)->delay_until. For standalone use, pass any persistent uint32_t *.

Note:

The target is reset to 0 (the scheduler's "no deadline pending" sentinel) once the delay completes. A deadline left in place would read as "in the future" again after the tick counter advances 2^31 ms (~24.8 days) past it, freezing the task. A computed deadline of exactly 0 (tick wrap) is nudged to 1 so it cannot alias the sentinel.

Parameters:

  • pt Protothread.
  • target Pointer to a uint32_t that will hold the deadline tick.
  • ms Delay duration in milliseconds.

define PT_DELAY_US

Non-blocking sub-millisecond delay for us microseconds.

#define PT_DELAY_US (
    pt,
    target,
    us
) `/* multi line expression */`

Requires a user-supplied persistent uint32_t variable (e.g., static uint32_t us_target;). Does not modify SYN_Task.delay_until to avoid corrupting the millisecond scheduler timebase.

Parameters:

  • pt Protothread.
  • target Pointer to a uint32_t variable holding the microsecond deadline.
  • us Delay duration in microseconds.

define PT_END

Close a protothread body. Returns PT_EXITED and resets the continuation so the thread can be restarted if desired.

#define PT_END (
    pt
) `/* multi line expression */`


define PT_EXIT

Terminate the protothread immediately.

#define PT_EXIT (
    pt
) `/* multi line expression */`


define PT_INIT

Initialize (or reset) a protothread so it starts from the top.

#define PT_INIT (
    pt
) `((pt)->lc = 0)`


define PT_IS_IDLE

#define PT_IS_IDLE (
    pt
) `((pt)->lc == 0)`

Check if a protothread has not yet started or has been reset.


define PT_IS_RUNNING

#define PT_IS_RUNNING (
    pt
) `((pt)->lc != 0)`

Check if a protothread is still running (has not exited).


define PT_RESTART

Reset the protothread and restart from PT_BEGIN on next call.

#define PT_RESTART (
    pt
) `/* multi line expression */`


define PT_SPAWN

Spawn a child protothread and block until it exits.

#define PT_SPAWN (
    pt,
    child,
    func
) `/* multi line expression */`

Parameters:

  • pt Parent protothread.
  • child Child protothread (SYN_PT struct).
  • func Expression that calls the child's function, e.g. child_func(&child_pt).

Warning:

The func expression is evaluated on EVERY scheduler poll until the child exits. Never pass expressions with side-effects (e.g. counter++, function calls that modify state). Pre-evaluate arguments before calling PT_SPAWN.

Usage:

static SYN_PT child_pt;
PT_SPAWN(pt, &child_pt, child_func(&child_pt));


define PT_TASK_DELAY_MS

Convenience form for use with the scheduler's SYN_Task (milliseconds).

#define PT_TASK_DELAY_MS (
    pt,
    task,
    ms
) `PT_DELAY_MS (pt, &(task)->delay_until, ms)`

Parameters:

  • pt Protothread.
  • task Pointer to the SYN_Task struct.
  • ms Delay duration in milliseconds.

define PT_WAIT_UNTIL

Block until cond evaluates to true.

#define PT_WAIT_UNTIL (
    pt,
    cond
) `/* multi line expression */`

Each time the protothread is polled and cond is false, it returns PT_WAITING and resumes at this point on the next call.


define PT_WAIT_WHILE

Block while cond is true. Dual of PT_WAIT_UNTIL.

#define PT_WAIT_WHILE (
    pt,
    cond
) `PT_WAIT_UNTIL (pt, !(cond))`


define PT_YIELD

Yield control unconditionally. The protothread will resume at this point on the next call.

#define PT_YIELD (
    pt
) `/* multi line expression */`


define PT_YIELD_UNTIL

Yield while cond is true.

#define PT_YIELD_UNTIL (
    pt,
    cond
) `/* multi line expression */`

Like PT_WAIT_WHILE, but returns PT_YIELDED instead of PT_WAITING, signaling the scheduler that this thread actively wants to run.

Warning:

Priority starvation hazard: A high-priority task using PT_YIELD_UNTIL to wait for a resource held by a lower-priority task will spin-yield every tick, starving the lower-priority task from ever releasing the resource. Use PT_WAIT_UNTIL(pt, cond) for inter-task resource contention, and reserve PT_YIELD_UNTIL for sub-tick hardware polling only.



The documentation for this class was generated from the following file src/syntropic/pt/syn_pt.h