xco

Concurrency for C
git clone https://git.ryansepassi.com/git/xco.git
Log | Files | Refs

commit 192570edb8035ef5a3233906c46490b64b1a013d
parent e2e8f4aa33773708c90d37689849c56cf3be6c24
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue,  5 May 2026 11:31:45 -0700

consistent xco_ prefix

Diffstat:
Mplatform/arm64/xco_platform.c | 16++++++++--------
Mplatform/arm64/xco_platform.h | 10+++++-----
Mtests/test_event.c | 2426++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mtests/test_xco.c | 224++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mxco.c | 802++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mxco.h | 936++++++++++++++++++++++++++++++++++++++++----------------------------------------
6 files changed, 2207 insertions(+), 2207 deletions(-)

diff --git a/platform/arm64/xco_platform.c b/platform/arm64/xco_platform.c @@ -1,7 +1,7 @@ /* - * platform/arm64/xco_platform.c — AArch64 init + switch + trampoline thunk. + * platform/arm64/xco_platform.c — AArch64 init + switch + xco_trampoline thunk. * - * The switch primitive and the trampoline thunk are written as + * The switch primitive and the xco_trampoline thunk are written as * file-scope global asm rather than __attribute__((naked)) functions: * GCC silently ignores `naked` on AArch64, so naked-function inline * asm only works under Clang. File-scope __asm__ is portable across @@ -39,10 +39,10 @@ struct xco_platform_ctx { * they still match the real struct, and verify the byte offsets the * asm hardcodes are still correct. */ -_Static_assert(sizeof(struct xco_platform_ctx) == _XCO_CTX_SIZE, - "_XCO_CTX_SIZE out of sync with struct layout"); -_Static_assert(_Alignof(struct xco_platform_ctx) == _XCO_CTX_ALIGN, - "_XCO_CTX_ALIGN out of sync with struct layout"); +_Static_assert(sizeof(struct xco_platform_ctx) == XCO__CTX_SIZE, + "XCO__CTX_SIZE out of sync with struct layout"); +_Static_assert(_Alignof(struct xco_platform_ctx) == XCO__CTX_ALIGN, + "XCO__CTX_ALIGN out of sync with struct layout"); _Static_assert(sizeof(uintptr_t) == 8, "AArch64"); _Static_assert(offsetof(struct xco_platform_ctx, regs) == 0, ""); _Static_assert(offsetof(struct xco_platform_ctx, fp_regs) == 104, ""); @@ -71,12 +71,12 @@ void xco_platform_init(xco_platform_ctx_t *ctx, ctx->regs[12] = top; /* sp */ } -/* ---- xco_platform_switch and trampoline thunk (file-scope asm) ----- +/* ---- xco_platform_switch and xco_trampoline thunk (file-scope asm) ----- * * xco_platform_switch(from x0, to x1, value x2) -> x0: * saves callee-saved state into *from, restores from *to, and * delivers `value` to the destination as x0 — which is the first-arg - * register on the trampoline thunk's first run, and the return-value + * register on the xco_trampoline thunk's first run, and the return-value * register when resuming a previously-suspended switch. * * xco_platform_trampoline_thunk(): diff --git a/platform/arm64/xco_platform.h b/platform/arm64/xco_platform.h @@ -15,13 +15,13 @@ /* Implementation-detail constants used by xco.c to embed the platform * context inside xco_impl_t without seeing the struct definition. * platform/arm64/xco_platform.c verifies these match the real layout. */ -#define _XCO_CTX_SIZE 176 /* sizeof(struct xco_platform_ctx) */ -#define _XCO_CTX_ALIGN 16 /* _Alignof(struct xco_platform_ctx) */ +#define XCO__CTX_SIZE 176 /* sizeof(struct xco_platform_ctx) */ +#define XCO__CTX_ALIGN 16 /* _Alignof(struct xco_platform_ctx) */ -/* Size and alignment of xco_t._private. Must hold an xco_impl_t +/* Size and alignment of xco_t.priv. Must hold an xco_impl_t * (defined in xco.c): a platform context + ~3 words of bookkeeping. * xco.c verifies sufficiency with _Static_assert. */ -#define XCO_SIZE (_XCO_CTX_SIZE + 48) -#define XCO_ALIGN _XCO_CTX_ALIGN +#define XCO_SIZE (XCO__CTX_SIZE + 48) +#define XCO_ALIGN XCO__CTX_ALIGN #endif /* XCO_PLATFORM_INTERNAL_H */ diff --git a/tests/test_event.c b/tests/test_event.c @@ -1,7 +1,7 @@ /* - * test_event.c — exercises latch_t, select_event_t, runtime_t. + * test_event.c — exercises xco_latch_t, xco_select_event_t, xco_runtime_t. * - * Drives hand-coded xstep state machines through the event substrate + * Drives hand-coded xco_step state machines through the event substrate * — no coroutines required. A "waiter" is the canonical pattern: try * the event, park if not ready, re-try after wake. */ @@ -14,41 +14,41 @@ /* ---- Waiter: blocks on one event, captures its value -------------- */ typedef struct { - xstep_t base; - event_t *e; - runtime_t *rt; - step_waker_t sw; + xco_step_t base; + xco_event_t *e; + xco_runtime_t *rt; + xco_step_waker_t sw; int phase; uintptr_t got; } waiter_t; -static xstep_result_t waiter_step(xstep_t *s, uintptr_t v) { +static xco_step_result_t waiter_step(xco_step_t *s, uintptr_t v) { waiter_t *w = (waiter_t *)s; switch (w->phase) { case 0: { uintptr_t out; - if (event_try(w->e, &out)) { + if (xco_event_try(w->e, &out)) { w->got = out; w->phase = 2; - return (xstep_result_t){out, XSTEP_DEAD}; + return (xco_step_result_t){out, XCO_STEP_DEAD}; } - step_waker_init(&w->sw, w->rt, &w->base); - event_park(w->e, &w->sw.base); + xco_step_waker_init(&w->sw, w->rt, &w->base); + xco_event_park(w->e, &w->sw.base); w->phase = 1; - return (xstep_result_t){0, XSTEP_SUSPENDED}; + return (xco_step_result_t){0, XCO_STEP_SUSPENDED}; } case 1: /* v is the value handed in by the fire site (latch payload, or * for select, the winning index). No re-try needed. */ w->got = v; w->phase = 2; - return (xstep_result_t){v, XSTEP_DEAD}; + return (xco_step_result_t){v, XCO_STEP_DEAD}; } __builtin_unreachable(); } -static void waiter_init(waiter_t *w, runtime_t *rt, event_t *e) { - w->base = (xstep_t){.step = waiter_step, .status = XSTEP_INIT}; +static void waiter_init(waiter_t *w, xco_runtime_t *rt, xco_event_t *e) { + w->base = (xco_step_t){.step = waiter_step, .status = XCO_STEP_INIT}; w->e = e; w->rt = rt; w->phase = 0; @@ -58,100 +58,100 @@ static void waiter_init(waiter_t *w, runtime_t *rt, event_t *e) { /* ---- Latch tests --------------------------------------------------- */ static void test_latch_wake(void) { - runtime_t rt; rt_init(&rt); - latch_t l; latch_init(&l); + xco_runtime_t rt; xco_rt_init(&rt); + xco_latch_t l; xco_latch_init(&l); waiter_t w; waiter_init(&w, &rt, &l.base); - xstep_result_t r = xstep(&w.base, 0); - assert(r.status == XSTEP_SUSPENDED); + xco_step_result_t r = xco_step(&w.base, 0); + assert(r.status == XCO_STEP_SUSPENDED); assert(rt.head == NULL); /* parked, not queued */ assert(l.waiters == &w.sw.base); /* armed on the latch */ - latch_set(&l, 42); + xco_latch_set(&l, 42); assert(l.waiters == NULL); /* drained on fire */ assert(rt.head != NULL); /* fire enqueued the step */ - rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_DEAD); + xco_rt_run(&rt, 0); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); assert(w.got == 42); assert(rt.head == NULL); } static void test_latch_already_set(void) { /* Already-set latch: try succeeds inline, no park, no queueing. */ - runtime_t rt; rt_init(&rt); - latch_t l; latch_init(&l); - latch_set(&l, 7); + xco_runtime_t rt; xco_rt_init(&rt); + xco_latch_t l; xco_latch_init(&l); + xco_latch_set(&l, 7); waiter_t w; waiter_init(&w, &rt, &l.base); - xstep_result_t r = xstep(&w.base, 0); - assert(r.status == XSTEP_DEAD); + xco_step_result_t r = xco_step(&w.base, 0); + assert(r.status == XCO_STEP_DEAD); assert(r.value == 7); assert(w.got == 7); assert(rt.head == NULL); } static void test_latch_multi_waiter(void) { - /* One latch_set wakes every waiter. */ - runtime_t rt; rt_init(&rt); - latch_t l; latch_init(&l); + /* One xco_latch_set wakes every waiter. */ + xco_runtime_t rt; xco_rt_init(&rt); + xco_latch_t l; xco_latch_init(&l); waiter_t a, b, c; waiter_init(&a, &rt, &l.base); waiter_init(&b, &rt, &l.base); waiter_init(&c, &rt, &l.base); - xstep(&a.base, 0); - xstep(&b.base, 0); - xstep(&c.base, 0); - assert(xstep_status(&a.base) == XSTEP_SUSPENDED); - assert(xstep_status(&b.base) == XSTEP_SUSPENDED); - assert(xstep_status(&c.base) == XSTEP_SUSPENDED); + xco_step(&a.base, 0); + xco_step(&b.base, 0); + xco_step(&c.base, 0); + assert(xco_step_status(&a.base) == XCO_STEP_SUSPENDED); + assert(xco_step_status(&b.base) == XCO_STEP_SUSPENDED); + assert(xco_step_status(&c.base) == XCO_STEP_SUSPENDED); - latch_set(&l, 99); - rt_run(&rt, 0); + xco_latch_set(&l, 99); + xco_rt_run(&rt, 0); - assert(xstep_status(&a.base) == XSTEP_DEAD && a.got == 99); - assert(xstep_status(&b.base) == XSTEP_DEAD && b.got == 99); - assert(xstep_status(&c.base) == XSTEP_DEAD && c.got == 99); + assert(xco_step_status(&a.base) == XCO_STEP_DEAD && a.got == 99); + assert(xco_step_status(&b.base) == XCO_STEP_DEAD && b.got == 99); + assert(xco_step_status(&c.base) == XCO_STEP_DEAD && c.got == 99); } static void test_latch_set_idempotent(void) { - latch_t l; latch_init(&l); - latch_set(&l, 1); - latch_set(&l, 2); /* ignored */ + xco_latch_t l; xco_latch_init(&l); + xco_latch_set(&l, 1); + xco_latch_set(&l, 2); /* ignored */ uintptr_t v = 0; - assert(event_try(&l.base, &v)); + assert(xco_event_try(&l.base, &v)); assert(v == 1); } static void test_latch_unpark(void) { /* Manually park then unpark — verify the waker is removed cleanly. */ - runtime_t rt; rt_init(&rt); - latch_t l; latch_init(&l); + xco_runtime_t rt; xco_rt_init(&rt); + xco_latch_t l; xco_latch_init(&l); - step_waker_t sw1, sw2, sw3; + xco_step_waker_t sw1, sw2, sw3; /* Step pointer is just a sentinel here; we never run them. */ - step_waker_init(&sw1, &rt, (xstep_t *)0x1); - step_waker_init(&sw2, &rt, (xstep_t *)0x2); - step_waker_init(&sw3, &rt, (xstep_t *)0x3); - event_park(&l.base, &sw1.base); - event_park(&l.base, &sw2.base); - event_park(&l.base, &sw3.base); + xco_step_waker_init(&sw1, &rt, (xco_step_t *)0x1); + xco_step_waker_init(&sw2, &rt, (xco_step_t *)0x2); + xco_step_waker_init(&sw3, &rt, (xco_step_t *)0x3); + xco_event_park(&l.base, &sw1.base); + xco_event_park(&l.base, &sw2.base); + xco_event_park(&l.base, &sw3.base); /* Remove the middle one. */ - event_unpark(&l.base, &sw2.base); + xco_event_unpark(&l.base, &sw2.base); assert(sw2.base.next == NULL); /* Unpark of an already-removed waker is a no-op. */ - event_unpark(&l.base, &sw2.base); + xco_event_unpark(&l.base, &sw2.base); /* sw1 and sw3 still on the list. */ int seen1 = 0, seen3 = 0; - for (waker_t *w = l.waiters; w; w = w->next) { + for (xco_waker_t *w = l.waiters; w; w = w->next) { if (w == &sw1.base) seen1 = 1; if (w == &sw3.base) seen3 = 1; assert(w != &sw2.base); @@ -159,110 +159,110 @@ static void test_latch_unpark(void) { assert(seen1 && seen3); /* Drain so we don't leave dangling armed wakers. */ - event_unpark(&l.base, &sw1.base); - event_unpark(&l.base, &sw3.base); + xco_event_unpark(&l.base, &sw1.base); + xco_event_unpark(&l.base, &sw3.base); assert(l.waiters == NULL); } /* ---- Select tests -------------------------------------------------- */ static void test_select_winner(void) { - runtime_t rt; rt_init(&rt); - latch_t a, b, c; - latch_init(&a); latch_init(&b); latch_init(&c); + xco_runtime_t rt; xco_rt_init(&rt); + xco_latch_t a, b, c; + xco_latch_init(&a); xco_latch_init(&b); xco_latch_init(&c); - select_event_t s; - select_input_t inputs[3]; - event_t *srcs[3] = {&a.base, &b.base, &c.base}; - select_event_init(&s, inputs, 3, srcs); + xco_select_event_t s; + xco_select_input_t inputs[3]; + xco_event_t *srcs[3] = {&a.base, &b.base, &c.base}; + xco_select_event_init(&s, inputs, 3, srcs); waiter_t w; waiter_init(&w, &rt, &s.done.base); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); assert(a.waiters && b.waiters && c.waiters); /* all armed */ - latch_set(&b, 0xBBB); - rt_run(&rt, 0); + xco_latch_set(&b, 0xBBB); + xco_rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_DEAD); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); assert(w.got == 1); /* b's index */ - /* Losers were disarmed by select_input_fire. */ + /* Losers were disarmed by xco_select_input_fire. */ assert(a.waiters == NULL); assert(c.waiters == NULL); /* Re-trying the winning input yields its actual payload. */ uintptr_t v; - assert(event_try(&b.base, &v)); + assert(xco_event_try(&b.base, &v)); assert(v == 0xBBB); - select_event_deinit(&s); + xco_select_event_deinit(&s); } static void test_select_fast_path(void) { /* Input already set at init: fire immediately, never park anyone. */ - latch_t a, b; - latch_init(&a); latch_init(&b); - latch_set(&a, 0xAAA); + xco_latch_t a, b; + xco_latch_init(&a); xco_latch_init(&b); + xco_latch_set(&a, 0xAAA); - select_event_t s; - select_input_t inputs[2]; - event_t *srcs[2] = {&a.base, &b.base}; - select_event_init(&s, inputs, 2, srcs); + xco_select_event_t s; + xco_select_input_t inputs[2]; + xco_event_t *srcs[2] = {&a.base, &b.base}; + xco_select_event_init(&s, inputs, 2, srcs); uintptr_t v; - assert(event_try(&s.done.base, &v)); + assert(xco_event_try(&s.done.base, &v)); assert(v == 0); /* a wins */ assert(b.waiters == NULL); /* nothing parked on the loser */ - select_event_deinit(&s); + xco_select_event_deinit(&s); } static void test_select_deinit_unparks(void) { /* Selecting then dropping (no input fires) must release input wakers. */ - latch_t a, b; - latch_init(&a); latch_init(&b); + xco_latch_t a, b; + xco_latch_init(&a); xco_latch_init(&b); - select_event_t s; - select_input_t inputs[2]; - event_t *srcs[2] = {&a.base, &b.base}; - select_event_init(&s, inputs, 2, srcs); + xco_select_event_t s; + xco_select_input_t inputs[2]; + xco_event_t *srcs[2] = {&a.base, &b.base}; + xco_select_event_init(&s, inputs, 2, srcs); assert(a.waiters != NULL); assert(b.waiters != NULL); - select_event_deinit(&s); + xco_select_event_deinit(&s); assert(a.waiters == NULL); assert(b.waiters == NULL); } static void test_select_compose(void) { /* select(select(A, B), C): firing A propagates through both layers. */ - runtime_t rt; rt_init(&rt); - latch_t a, b, c; - latch_init(&a); latch_init(&b); latch_init(&c); + xco_runtime_t rt; xco_rt_init(&rt); + xco_latch_t a, b, c; + xco_latch_init(&a); xco_latch_init(&b); xco_latch_init(&c); - select_event_t inner; - select_input_t in_inputs[2]; - event_t *in_srcs[2] = {&a.base, &b.base}; - select_event_init(&inner, in_inputs, 2, in_srcs); + xco_select_event_t inner; + xco_select_input_t in_inputs[2]; + xco_event_t *in_srcs[2] = {&a.base, &b.base}; + xco_select_event_init(&inner, in_inputs, 2, in_srcs); - select_event_t outer; - select_input_t out_inputs[2]; - event_t *out_srcs[2] = {&inner.done.base, &c.base}; - select_event_init(&outer, out_inputs, 2, out_srcs); + xco_select_event_t outer; + xco_select_input_t out_inputs[2]; + xco_event_t *out_srcs[2] = {&inner.done.base, &c.base}; + xco_select_event_init(&outer, out_inputs, 2, out_srcs); waiter_t w; waiter_init(&w, &rt, &outer.done.base); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); - latch_set(&a, 0); - rt_run(&rt, 0); + xco_latch_set(&a, 0); + xco_rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_DEAD); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); assert(w.got == 0); /* outer winner: inner (index 0) */ uintptr_t v; - assert(event_try(&inner.done.base, &v)); + assert(xco_event_try(&inner.done.base, &v)); assert(v == 0); /* inner winner: a (index 0) */ /* The unrelated source c was disarmed when outer fired. */ @@ -270,203 +270,203 @@ static void test_select_compose(void) { /* And b was disarmed when inner fired. */ assert(b.waiters == NULL); - select_event_deinit(&outer); - select_event_deinit(&inner); + xco_select_event_deinit(&outer); + xco_select_event_deinit(&inner); } /* ---- All-of -------------------------------------------------------- */ static void test_allof_basic(void) { /* Three latches: done fires only after all three set. Storage is the - * same select_event_t / select_input_t — only the init call differs. */ - runtime_t rt; rt_init(&rt); - latch_t a, b, c; - latch_init(&a); latch_init(&b); latch_init(&c); - - select_event_t s; - select_input_t inputs[3]; - event_t *srcs[3] = {&a.base, &b.base, &c.base}; - allof_event_init(&s, inputs, 3, srcs); + * same xco_select_event_t / xco_select_input_t — only the init call differs. */ + xco_runtime_t rt; xco_rt_init(&rt); + xco_latch_t a, b, c; + xco_latch_init(&a); xco_latch_init(&b); xco_latch_init(&c); + + xco_select_event_t s; + xco_select_input_t inputs[3]; + xco_event_t *srcs[3] = {&a.base, &b.base, &c.base}; + xco_allof_event_init(&s, inputs, 3, srcs); assert(!s.done.set); assert(a.waiters && b.waiters && c.waiters); waiter_t w; waiter_init(&w, &rt, &s.done.base); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); - latch_set(&a, 0xAAA); - rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); /* still waiting */ + xco_latch_set(&a, 0xAAA); + xco_rt_run(&rt, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); /* still waiting */ assert(!s.done.set); - latch_set(&b, 0xBBB); - rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_latch_set(&b, 0xBBB); + xco_rt_run(&rt, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); assert(!s.done.set); - latch_set(&c, 0xCCC); - rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_DEAD); + xco_latch_set(&c, 0xCCC); + xco_rt_run(&rt, 0); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); assert(w.got == 2); /* closing index = c */ assert(inputs[0].value == 0xAAA); assert(inputs[1].value == 0xBBB); assert(inputs[2].value == 0xCCC); - select_event_deinit(&s); + xco_select_event_deinit(&s); } static void test_allof_fast_path_partial(void) { /* Some ready at init: those are captured inline, no parking. */ - latch_t a, b; - latch_init(&a); latch_init(&b); - latch_set(&a, 0xAAA); + xco_latch_t a, b; + xco_latch_init(&a); xco_latch_init(&b); + xco_latch_set(&a, 0xAAA); - select_event_t s; - select_input_t inputs[2]; - event_t *srcs[2] = {&a.base, &b.base}; - allof_event_init(&s, inputs, 2, srcs); + xco_select_event_t s; + xco_select_input_t inputs[2]; + xco_event_t *srcs[2] = {&a.base, &b.base}; + xco_allof_event_init(&s, inputs, 2, srcs); assert(!s.done.set); /* still need b */ assert(a.waiters == NULL); /* a was inline */ assert(b.waiters == &inputs[1].w); /* b is parked */ assert(inputs[0].value == 0xAAA); - latch_set(&b, 0xBBB); + xco_latch_set(&b, 0xBBB); assert(s.done.set); assert(inputs[1].value == 0xBBB); uintptr_t v; - assert(event_try(&s.done.base, &v)); + assert(xco_event_try(&s.done.base, &v)); assert(v == 1); /* b closed the wait */ - select_event_deinit(&s); + xco_select_event_deinit(&s); } static void test_allof_fast_path_all(void) { /* All ready at init: done fires immediately, no waker is ever parked. */ - latch_t a, b; - latch_init(&a); latch_init(&b); - latch_set(&a, 0xAAA); - latch_set(&b, 0xBBB); + xco_latch_t a, b; + xco_latch_init(&a); xco_latch_init(&b); + xco_latch_set(&a, 0xAAA); + xco_latch_set(&b, 0xBBB); - select_event_t s; - select_input_t inputs[2]; - event_t *srcs[2] = {&a.base, &b.base}; - allof_event_init(&s, inputs, 2, srcs); + xco_select_event_t s; + xco_select_input_t inputs[2]; + xco_event_t *srcs[2] = {&a.base, &b.base}; + xco_allof_event_init(&s, inputs, 2, srcs); assert(s.done.set); uintptr_t v; - assert(event_try(&s.done.base, &v)); + assert(xco_event_try(&s.done.base, &v)); assert(v == 1); /* last input's index */ assert(inputs[0].value == 0xAAA); assert(inputs[1].value == 0xBBB); - select_event_deinit(&s); + xco_select_event_deinit(&s); } static void test_allof_empty(void) { /* n=0: fires immediately with payload 0. */ - select_event_t s; - allof_event_init(&s, NULL, 0, NULL); + xco_select_event_t s; + xco_allof_event_init(&s, NULL, 0, NULL); assert(s.done.set); uintptr_t v; - assert(event_try(&s.done.base, &v)); + assert(xco_event_try(&s.done.base, &v)); assert(v == 0); - select_event_deinit(&s); + xco_select_event_deinit(&s); } static void test_allof_deinit_partial(void) { /* One input fired, one still parked: deinit must release the parked * waker without disturbing the fired one. */ - latch_t a, b; - latch_init(&a); latch_init(&b); + xco_latch_t a, b; + xco_latch_init(&a); xco_latch_init(&b); - select_event_t s; - select_input_t inputs[2]; - event_t *srcs[2] = {&a.base, &b.base}; - allof_event_init(&s, inputs, 2, srcs); + xco_select_event_t s; + xco_select_input_t inputs[2]; + xco_event_t *srcs[2] = {&a.base, &b.base}; + xco_allof_event_init(&s, inputs, 2, srcs); - latch_set(&a, 0xAAA); + xco_latch_set(&a, 0xAAA); assert(!s.done.set); assert(a.waiters == NULL); /* a's waker detached on fire */ assert(b.waiters == &inputs[1].w); /* b still parked */ - select_event_deinit(&s); + xco_select_event_deinit(&s); assert(b.waiters == NULL); /* released */ } static void test_allof_compose_with_select(void) { - /* An allof-mode event is still a select_event_t — feed it into a + /* An allof-mode event is still a xco_select_event_t — feed it into a * select-mode wait. */ - runtime_t rt; rt_init(&rt); - latch_t a, b, c; - latch_init(&a); latch_init(&b); latch_init(&c); + xco_runtime_t rt; xco_rt_init(&rt); + xco_latch_t a, b, c; + xco_latch_init(&a); xco_latch_init(&b); xco_latch_init(&c); - select_event_t all; - select_input_t all_inputs[2]; - event_t *all_srcs[2] = {&a.base, &b.base}; - allof_event_init(&all, all_inputs, 2, all_srcs); + xco_select_event_t all; + xco_select_input_t all_inputs[2]; + xco_event_t *all_srcs[2] = {&a.base, &b.base}; + xco_allof_event_init(&all, all_inputs, 2, all_srcs); - select_event_t sel; - select_input_t sel_inputs[2]; - event_t *sel_srcs[2] = {&all.done.base, &c.base}; - select_event_init(&sel, sel_inputs, 2, sel_srcs); + xco_select_event_t sel; + xco_select_input_t sel_inputs[2]; + xco_event_t *sel_srcs[2] = {&all.done.base, &c.base}; + xco_select_event_init(&sel, sel_inputs, 2, sel_srcs); waiter_t w; waiter_init(&w, &rt, &sel.done.base); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); /* Fire both halves of the allof; the select then sees its first input ready. */ - latch_set(&a, 1); - latch_set(&b, 2); - rt_run(&rt, 0); + xco_latch_set(&a, 1); + xco_latch_set(&b, 2); + xco_rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_DEAD); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); assert(w.got == 0); /* allof side won the select */ assert(all.done.set); assert(c.waiters == NULL); /* select disarmed the loser */ - select_event_deinit(&sel); - select_event_deinit(&all); + xco_select_event_deinit(&sel); + xco_select_event_deinit(&all); } /* ---- Channel ------------------------------------------------------- */ /* Sender state machine: try inline; if blocked, park with value. */ typedef struct { - xstep_t base; - chan_t *c; - runtime_t *rt; - chan_send_waker_t csw; + xco_step_t base; + xco_chan_t *c; + xco_runtime_t *rt; + xco_chan_send_waker_t csw; uintptr_t value; int phase; bool done; } sender_t; -static xstep_result_t sender_step(xstep_t *s, uintptr_t v) { +static xco_step_result_t sender_step(xco_step_t *s, uintptr_t v) { sender_t *snd = (sender_t *)s; (void)v; switch (snd->phase) { case 0: - if (chan_try_send(snd->c, snd->value)) { + if (xco_chan_try_send(snd->c, snd->value)) { snd->done = true; snd->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } - chan_send_waker_init(&snd->csw, snd->rt, &snd->base, snd->value); - chan_park_send(snd->c, &snd->csw); + xco_chan_send_waker_init(&snd->csw, snd->rt, &snd->base, snd->value); + xco_chan_park_send(snd->c, &snd->csw); snd->phase = 1; - return (xstep_result_t){0, XSTEP_SUSPENDED}; + return (xco_step_result_t){0, XCO_STEP_SUSPENDED}; case 1: snd->done = true; snd->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } __builtin_unreachable(); } -static void sender_init(sender_t *snd, runtime_t *rt, chan_t *c, uintptr_t value) { - snd->base = (xstep_t){.step = sender_step, .status = XSTEP_INIT}; +static void sender_init(sender_t *snd, xco_runtime_t *rt, xco_chan_t *c, uintptr_t value) { + snd->base = (xco_step_t){.step = sender_step, .status = XCO_STEP_INIT}; snd->c = c; snd->rt = rt; snd->value = value; @@ -476,141 +476,141 @@ static void sender_init(sender_t *snd, runtime_t *rt, chan_t *c, uintptr_t value static void test_chan_send_blocks_until_recv(void) { /* Sender arrives first, parks; receiver pulls and both finish. */ - runtime_t rt; rt_init(&rt); - chan_t c; chan_init(&c); + xco_runtime_t rt; xco_rt_init(&rt); + xco_chan_t c; xco_chan_init(&c); sender_t snd; sender_init(&snd, &rt, &c, 0xDEADBEEF); - xstep(&snd.base, 0); - assert(xstep_status(&snd.base) == XSTEP_SUSPENDED); + xco_step(&snd.base, 0); + assert(xco_step_status(&snd.base) == XCO_STEP_SUSPENDED); assert(c.send_head == &snd.csw.sw.base); assert(c.send_tail == &snd.csw.sw.base); waiter_t r; waiter_init(&r, &rt, &c.recv); - xstep_result_t rr = xstep(&r.base, 0); - assert(rr.status == XSTEP_DEAD); + xco_step_result_t rr = xco_step(&r.base, 0); + assert(rr.status == XCO_STEP_DEAD); assert(rr.value == 0xDEADBEEF); assert(r.got == 0xDEADBEEF); assert(c.send_head == NULL); /* Sender's resumption is queued by the recv-side fire. */ - rt_run(&rt, 0); - assert(xstep_status(&snd.base) == XSTEP_DEAD); + xco_rt_run(&rt, 0); + assert(xco_step_status(&snd.base) == XCO_STEP_DEAD); assert(snd.done); } static void test_chan_recv_blocks_until_send(void) { /* Receiver arrives first, parks; sender delivers inline. */ - runtime_t rt; rt_init(&rt); - chan_t c; chan_init(&c); + xco_runtime_t rt; xco_rt_init(&rt); + xco_chan_t c; xco_chan_init(&c); waiter_t r; waiter_init(&r, &rt, &c.recv); - xstep(&r.base, 0); - assert(xstep_status(&r.base) == XSTEP_SUSPENDED); + xco_step(&r.base, 0); + assert(xco_step_status(&r.base) == XCO_STEP_SUSPENDED); assert(c.recv_head == &r.sw.base); - bool delivered = chan_try_send(&c, 0xCAFE); + bool delivered = xco_chan_try_send(&c, 0xCAFE); assert(delivered); assert(c.recv_head == NULL); - rt_run(&rt, 0); - assert(xstep_status(&r.base) == XSTEP_DEAD); + xco_rt_run(&rt, 0); + assert(xco_step_status(&r.base) == XCO_STEP_DEAD); assert(r.got == 0xCAFE); } static void test_chan_try_send_no_recv(void) { /* No receiver parked: try_send fails without modifying state. */ - chan_t c; chan_init(&c); - assert(!chan_try_send(&c, 1)); + xco_chan_t c; xco_chan_init(&c); + assert(!xco_chan_try_send(&c, 1)); assert(c.send_head == NULL && c.recv_head == NULL); } static void test_chan_fifo(void) { /* Three senders park; three receives pull values in arrival order. */ - runtime_t rt; rt_init(&rt); - chan_t c; chan_init(&c); + xco_runtime_t rt; xco_rt_init(&rt); + xco_chan_t c; xco_chan_init(&c); sender_t s[3]; for (int i = 0; i < 3; i++) { sender_init(&s[i], &rt, &c, (uintptr_t)(100 + i)); - xstep(&s[i].base, 0); - assert(xstep_status(&s[i].base) == XSTEP_SUSPENDED); + xco_step(&s[i].base, 0); + assert(xco_step_status(&s[i].base) == XCO_STEP_SUSPENDED); } assert(c.send_head == &s[0].csw.sw.base); assert(c.send_tail == &s[2].csw.sw.base); for (int i = 0; i < 3; i++) { uintptr_t v; - assert(event_try(&c.recv, &v)); + assert(xco_event_try(&c.recv, &v)); assert(v == (uintptr_t)(100 + i)); } assert(c.send_head == NULL); - rt_run(&rt, 0); + xco_rt_run(&rt, 0); for (int i = 0; i < 3; i++) { - assert(xstep_status(&s[i].base) == XSTEP_DEAD); + assert(xco_step_status(&s[i].base) == XCO_STEP_DEAD); assert(s[i].done); } } static void test_chan_unpark_send(void) { /* Cancel a parked sender — list is repaired; other waiters intact. */ - runtime_t rt; rt_init(&rt); - chan_t c; chan_init(&c); + xco_runtime_t rt; xco_rt_init(&rt); + xco_chan_t c; xco_chan_init(&c); sender_t a, b, d; sender_init(&a, &rt, &c, 1); sender_init(&b, &rt, &c, 2); sender_init(&d, &rt, &c, 3); - xstep(&a.base, 0); xstep(&b.base, 0); xstep(&d.base, 0); + xco_step(&a.base, 0); xco_step(&b.base, 0); xco_step(&d.base, 0); assert(c.send_head == &a.csw.sw.base); assert(c.send_tail == &d.csw.sw.base); - chan_unpark_send(&c, &b.csw); /* middle */ + xco_chan_unpark_send(&c, &b.csw); /* middle */ /* Idempotent: removing again is a no-op. */ - chan_unpark_send(&c, &b.csw); + xco_chan_unpark_send(&c, &b.csw); /* Order preserved: a, then d. */ uintptr_t v; - assert(event_try(&c.recv, &v) && v == 1); - assert(event_try(&c.recv, &v) && v == 3); + assert(xco_event_try(&c.recv, &v) && v == 1); + assert(xco_event_try(&c.recv, &v) && v == 3); assert(c.send_head == NULL); /* a and d resume; b stays SUSPENDED (its waker was unparked * without firing). Drain so it doesn't dangle. */ - rt_run(&rt, 0); - assert(xstep_status(&a.base) == XSTEP_DEAD); - assert(xstep_status(&d.base) == XSTEP_DEAD); - assert(xstep_status(&b.base) == XSTEP_SUSPENDED); + xco_rt_run(&rt, 0); + assert(xco_step_status(&a.base) == XCO_STEP_DEAD); + assert(xco_step_status(&d.base) == XCO_STEP_DEAD); + assert(xco_step_status(&b.base) == XCO_STEP_SUSPENDED); } static void test_chan_select_recv(void) { - /* select(chan_recv, latch). Sender delivers; select fires with - * chan_recv's index, and the value is captured in inputs[winner].value. */ - runtime_t rt; rt_init(&rt); - chan_t c; chan_init(&c); - latch_t l; latch_init(&l); + /* select(xco_chan_recv, latch). Sender delivers; select fires with + * xco_chan_recv's index, and the value is captured in inputs[winner].value. */ + xco_runtime_t rt; xco_rt_init(&rt); + xco_chan_t c; xco_chan_init(&c); + xco_latch_t l; xco_latch_init(&l); - select_event_t sel; - select_input_t inputs[2]; - event_t *srcs[2] = {&c.recv, &l.base}; - select_event_init(&sel, inputs, 2, srcs); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_event_t *srcs[2] = {&c.recv, &l.base}; + xco_select_event_init(&sel, inputs, 2, srcs); waiter_t w; waiter_init(&w, &rt, &sel.done.base); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); assert(c.recv_head != NULL); /* select's input waker parked here */ - bool delivered = chan_try_send(&c, 0xABCDEF); + bool delivered = xco_chan_try_send(&c, 0xABCDEF); assert(delivered); - rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_DEAD); - assert(w.got == 0); /* chan_recv won (index 0) */ + xco_rt_run(&rt, 0); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); + assert(w.got == 0); /* xco_chan_recv won (index 0) */ assert(inputs[0].value == 0xABCDEF); /* captured value */ /* Loser was disarmed; deinit is safe (no-op since fired). */ assert(l.waiters == NULL); - select_event_deinit(&sel); + xco_select_event_deinit(&sel); } static void test_chan_recv_fifo(void) { @@ -618,28 +618,28 @@ static void test_chan_recv_fifo(void) { * The two waitlists are mutually exclusive (try-then-park is * atomic in single-threaded use), so the channel state at any * moment has at most one side queued. */ - runtime_t rt; rt_init(&rt); - chan_t c; chan_init(&c); + xco_runtime_t rt; xco_rt_init(&rt); + xco_chan_t c; xco_chan_init(&c); waiter_t r[3]; for (int i = 0; i < 3; i++) { waiter_init(&r[i], &rt, &c.recv); - xstep(&r[i].base, 0); - assert(xstep_status(&r[i].base) == XSTEP_SUSPENDED); + xco_step(&r[i].base, 0); + assert(xco_step_status(&r[i].base) == XCO_STEP_SUSPENDED); } assert(c.recv_head == &r[0].sw.base); assert(c.recv_tail == &r[2].sw.base); assert(c.send_head == NULL); /* never both sides */ for (int i = 0; i < 3; i++) { - bool delivered = chan_try_send(&c, (uintptr_t)(200 + i)); + bool delivered = xco_chan_try_send(&c, (uintptr_t)(200 + i)); assert(delivered); } assert(c.recv_head == NULL); - rt_run(&rt, 0); + xco_rt_run(&rt, 0); for (int i = 0; i < 3; i++) { - assert(xstep_status(&r[i].base) == XSTEP_DEAD); + assert(xco_step_status(&r[i].base) == XCO_STEP_DEAD); assert(r[i].got == (uintptr_t)(200 + i)); } } @@ -648,295 +648,295 @@ static void test_chan_recv_fifo(void) { static void test_chan_send_op_inline(void) { /* Receiver already parked: op_init delivers immediately, done set. */ - runtime_t rt; rt_init(&rt); - chan_t c; chan_init(&c); + xco_runtime_t rt; xco_rt_init(&rt); + xco_chan_t c; xco_chan_init(&c); waiter_t r; waiter_init(&r, &rt, &c.recv); - xstep(&r.base, 0); - assert(xstep_status(&r.base) == XSTEP_SUSPENDED); + xco_step(&r.base, 0); + assert(xco_step_status(&r.base) == XCO_STEP_SUSPENDED); - chan_send_op_t op; - chan_send_op_init(&op, &c, 0xFEED); + xco_chan_send_op_t op; + xco_chan_send_op_init(&op, &c, 0xFEED); assert(op.done.set); /* delivered inline */ assert(c.send_head == NULL); /* nothing parked */ - rt_run(&rt, 0); - assert(xstep_status(&r.base) == XSTEP_DEAD); + xco_rt_run(&rt, 0); + assert(xco_step_status(&r.base) == XCO_STEP_DEAD); assert(r.got == 0xFEED); - chan_send_op_deinit(&op); + xco_chan_send_op_deinit(&op); } static void test_chan_send_op_blocks(void) { /* No receiver: op parks. Receiver arrives later, op.done fires. */ - chan_t c; chan_init(&c); + xco_chan_t c; xco_chan_init(&c); - chan_send_op_t op; - chan_send_op_init(&op, &c, 0xBEAD); + xco_chan_send_op_t op; + xco_chan_send_op_init(&op, &c, 0xBEAD); assert(!op.done.set); assert(c.send_head == &op.csw.sw.base); /* Recv pulls value and fires op's waker → sets op.done. */ uintptr_t v; - assert(event_try(&c.recv, &v)); + assert(xco_event_try(&c.recv, &v)); assert(v == 0xBEAD); assert(op.done.set); assert(c.send_head == NULL); - chan_send_op_deinit(&op); + xco_chan_send_op_deinit(&op); } static void test_chan_select_send(void) { /* select(send_op, latch). Receiver pulls; send wins. */ - runtime_t rt; rt_init(&rt); - chan_t c; chan_init(&c); - latch_t timeout; latch_init(&timeout); + xco_runtime_t rt; xco_rt_init(&rt); + xco_chan_t c; xco_chan_init(&c); + xco_latch_t timeout; xco_latch_init(&timeout); - chan_send_op_t op; - chan_send_op_init(&op, &c, 0x5EED); + xco_chan_send_op_t op; + xco_chan_send_op_init(&op, &c, 0x5EED); assert(!op.done.set); /* parked, no recv */ - select_event_t sel; - select_input_t inputs[2]; - event_t *srcs[2] = {&op.done.base, &timeout.base}; - select_event_init(&sel, inputs, 2, srcs); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_event_t *srcs[2] = {&op.done.base, &timeout.base}; + xco_select_event_init(&sel, inputs, 2, srcs); waiter_t w; waiter_init(&w, &rt, &sel.done.base); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); /* A receiver arrives and pulls. op.done fires → select fires. */ uintptr_t v; - assert(event_try(&c.recv, &v)); + assert(xco_event_try(&c.recv, &v)); assert(v == 0x5EED); - rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_DEAD); + xco_rt_run(&rt, 0); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); assert(w.got == 0); /* send op won */ assert(timeout.waiters == NULL); /* loser disarmed */ - select_event_deinit(&sel); - chan_send_op_deinit(&op); + xco_select_event_deinit(&sel); + xco_chan_send_op_deinit(&op); } static void test_chan_select_send_loses(void) { /* select(send_op, latch). Latch fires first; send is canceled * cleanly via op_deinit (chan side disarmed, no dangling waker). */ - runtime_t rt; rt_init(&rt); - chan_t c; chan_init(&c); - latch_t l; latch_init(&l); + xco_runtime_t rt; xco_rt_init(&rt); + xco_chan_t c; xco_chan_init(&c); + xco_latch_t l; xco_latch_init(&l); - chan_send_op_t op; - chan_send_op_init(&op, &c, 0xABBA); + xco_chan_send_op_t op; + xco_chan_send_op_init(&op, &c, 0xABBA); assert(c.send_head == &op.csw.sw.base); - select_event_t sel; - select_input_t inputs[2]; - event_t *srcs[2] = {&op.done.base, &l.base}; - select_event_init(&sel, inputs, 2, srcs); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_event_t *srcs[2] = {&op.done.base, &l.base}; + xco_select_event_init(&sel, inputs, 2, srcs); waiter_t w; waiter_init(&w, &rt, &sel.done.base); - xstep(&w.base, 0); + xco_step(&w.base, 0); - latch_set(&l, 0xDEAD); - rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_DEAD); + xco_latch_set(&l, 0xDEAD); + xco_rt_run(&rt, 0); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); assert(w.got == 1); /* latch (index 1) won */ assert(inputs[1].value == 0xDEAD); /* send didn't happen — op still parked on chan. deinit unparks. */ assert(c.send_head == &op.csw.sw.base); - select_event_deinit(&sel); - chan_send_op_deinit(&op); + xco_select_event_deinit(&sel); + xco_chan_send_op_deinit(&op); assert(c.send_head == NULL); } static void test_chan_select_recv_fast_path(void) { /* Sender already parked when select arms: fast path captures value. */ - runtime_t rt; rt_init(&rt); - chan_t c; chan_init(&c); + xco_runtime_t rt; xco_rt_init(&rt); + xco_chan_t c; xco_chan_init(&c); sender_t snd; sender_init(&snd, &rt, &c, 0x12345); - xstep(&snd.base, 0); - assert(xstep_status(&snd.base) == XSTEP_SUSPENDED); + xco_step(&snd.base, 0); + assert(xco_step_status(&snd.base) == XCO_STEP_SUSPENDED); - latch_t l; latch_init(&l); - select_event_t sel; - select_input_t inputs[2]; - event_t *srcs[2] = {&c.recv, &l.base}; - select_event_init(&sel, inputs, 2, srcs); + xco_latch_t l; xco_latch_init(&l); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_event_t *srcs[2] = {&c.recv, &l.base}; + xco_select_event_init(&sel, inputs, 2, srcs); /* Fast path fired immediately. */ uintptr_t winner; - assert(event_try(&sel.done.base, &winner)); + assert(xco_event_try(&sel.done.base, &winner)); assert(winner == 0); assert(inputs[0].value == 0x12345); - rt_run(&rt, 0); - assert(xstep_status(&snd.base) == XSTEP_DEAD); - select_event_deinit(&sel); + xco_rt_run(&rt, 0); + assert(xco_step_status(&snd.base) == XCO_STEP_DEAD); + xco_select_event_deinit(&sel); } /* ---- Cancellation -------------------------------------------------- */ static void test_cancel_basic(void) { /* The alias is a thin rename over latch: not-set, set, idempotent set, - * and event_try yields the latch's payload of 0. */ - cancel_t c; cancel_init(&c); - assert(!cancel_is_set(&c)); - cancel_set(&c); - assert(cancel_is_set(&c)); - cancel_set(&c); /* idempotent */ - assert(cancel_is_set(&c)); + * and xco_event_try yields the latch's payload of 0. */ + xco_cancel_t c; xco_cancel_init(&c); + assert(!xco_cancel_is_set(&c)); + xco_cancel_set(&c); + assert(xco_cancel_is_set(&c)); + xco_cancel_set(&c); /* idempotent */ + assert(xco_cancel_is_set(&c)); uintptr_t v = 0xBADD; - assert(event_try(cancel_event(&c), &v)); + assert(xco_event_try(xco_cancel_event(&c), &v)); assert(v == 0); } static void test_wait_or_cancel_ev_wins(void) { - /* Event resolves first: waiter sees WAIT_OK and inputs[0].value + /* Event resolves first: waiter sees XCO_WAIT_OK and inputs[0].value * carries the event's payload. The cancel side is disarmed. */ - runtime_t rt; rt_init(&rt); - latch_t l; latch_init(&l); - cancel_t c; cancel_init(&c); + xco_runtime_t rt; xco_rt_init(&rt); + xco_latch_t l; xco_latch_init(&l); + xco_cancel_t c; xco_cancel_init(&c); - select_event_t sel; - select_input_t inputs[2]; - wait_or_cancel(&sel, inputs, &l.base, &c); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_wait_or_cancel(&sel, inputs, &l.base, &c); waiter_t w; waiter_init(&w, &rt, &sel.done.base); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); assert(l.waiters && c.waiters); /* both armed */ - latch_set(&l, 0xF00D); - rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_DEAD); - assert(w.got == WAIT_OK); - assert(inputs[WAIT_OK].value == 0xF00D); + xco_latch_set(&l, 0xF00D); + xco_rt_run(&rt, 0); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); + assert(w.got == XCO_WAIT_OK); + assert(inputs[XCO_WAIT_OK].value == 0xF00D); assert(c.waiters == NULL); /* cancel disarmed */ - select_event_deinit(&sel); + xco_select_event_deinit(&sel); } static void test_wait_or_cancel_cancel_wins(void) { - /* Cancel fires while parked: waiter sees WAIT_CANCELLED, ev disarmed. */ - runtime_t rt; rt_init(&rt); - latch_t l; latch_init(&l); - cancel_t c; cancel_init(&c); + /* Cancel fires while parked: waiter sees XCO_WAIT_CANCELLED, ev disarmed. */ + xco_runtime_t rt; xco_rt_init(&rt); + xco_latch_t l; xco_latch_init(&l); + xco_cancel_t c; xco_cancel_init(&c); - select_event_t sel; - select_input_t inputs[2]; - wait_or_cancel(&sel, inputs, &l.base, &c); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_wait_or_cancel(&sel, inputs, &l.base, &c); waiter_t w; waiter_init(&w, &rt, &sel.done.base); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); - cancel_set(&c); - rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_DEAD); - assert(w.got == WAIT_CANCELLED); + xco_cancel_set(&c); + xco_rt_run(&rt, 0); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); + assert(w.got == XCO_WAIT_CANCELLED); assert(l.waiters == NULL); /* ev disarmed */ - select_event_deinit(&sel); + xco_select_event_deinit(&sel); } static void test_wait_or_cancel_already_cancelled(void) { - /* Pre-set cancel: select fast-path fires WAIT_CANCELLED at init time; + /* Pre-set cancel: select fast-path fires XCO_WAIT_CANCELLED at init time; * ev is never parked, so deinit has nothing to disarm. */ - latch_t l; latch_init(&l); - cancel_t c; cancel_init(&c); - cancel_set(&c); + xco_latch_t l; xco_latch_init(&l); + xco_cancel_t c; xco_cancel_init(&c); + xco_cancel_set(&c); - select_event_t sel; - select_input_t inputs[2]; - wait_or_cancel(&sel, inputs, &l.base, &c); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_wait_or_cancel(&sel, inputs, &l.base, &c); uintptr_t v; - assert(event_try(&sel.done.base, &v)); - assert(v == WAIT_CANCELLED); + assert(xco_event_try(&sel.done.base, &v)); + assert(v == XCO_WAIT_CANCELLED); assert(l.waiters == NULL); /* never parked */ - select_event_deinit(&sel); + xco_select_event_deinit(&sel); } static void test_wait_or_cancel_ev_precedes_cancel(void) { /* Both ready at init: ev wins (it's checked first in the fast path). * This is the right semantic — if the work has already resolved, * a concurrent cancel doesn't retroactively undo it. */ - latch_t l; latch_init(&l); - cancel_t c; cancel_init(&c); - latch_set(&l, 0x600D); - cancel_set(&c); + xco_latch_t l; xco_latch_init(&l); + xco_cancel_t c; xco_cancel_init(&c); + xco_latch_set(&l, 0x600D); + xco_cancel_set(&c); - select_event_t sel; - select_input_t inputs[2]; - wait_or_cancel(&sel, inputs, &l.base, &c); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_wait_or_cancel(&sel, inputs, &l.base, &c); uintptr_t v; - assert(event_try(&sel.done.base, &v)); - assert(v == WAIT_OK); - assert(inputs[WAIT_OK].value == 0x600D); + assert(xco_event_try(&sel.done.base, &v)); + assert(v == XCO_WAIT_OK); + assert(inputs[XCO_WAIT_OK].value == 0x600D); - select_event_deinit(&sel); + xco_select_event_deinit(&sel); } static void test_wait_or_cancel_chan_recv(void) { - /* Waiter blocks on chan_recv via wait_or_cancel; cancel fires while + /* Waiter blocks on xco_chan_recv via xco_wait_or_cancel; cancel fires while * parked. After resume + deinit, the chan's recv list must be empty * so a future send doesn't deliver to a freed waker. */ - runtime_t rt; rt_init(&rt); - chan_t ch; chan_init(&ch); - cancel_t c; cancel_init(&c); + xco_runtime_t rt; xco_rt_init(&rt); + xco_chan_t ch; xco_chan_init(&ch); + xco_cancel_t c; xco_cancel_init(&c); - select_event_t sel; - select_input_t inputs[2]; - wait_or_cancel(&sel, inputs, &ch.recv, &c); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_wait_or_cancel(&sel, inputs, &ch.recv, &c); assert(ch.recv_head == &inputs[0].w); /* select's input parked */ waiter_t w; waiter_init(&w, &rt, &sel.done.base); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); - cancel_set(&c); - rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_DEAD); - assert(w.got == WAIT_CANCELLED); - assert(ch.recv_head == NULL); /* select_input_fire disarmed */ + xco_cancel_set(&c); + xco_rt_run(&rt, 0); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); + assert(w.got == XCO_WAIT_CANCELLED); + assert(ch.recv_head == NULL); /* xco_select_input_fire disarmed */ - select_event_deinit(&sel); + xco_select_event_deinit(&sel); /* No stale waiter lingering: try_send fails, doesn't fire anything. */ - assert(!chan_try_send(&ch, 1)); + assert(!xco_chan_try_send(&ch, 1)); } static void test_wait_or_cancel_send_op(void) { /* Selectable send under cancel: cancel wins, the send_op is still - * parked on the chan — chan_send_op_deinit must release it so the + * parked on the chan — xco_chan_send_op_deinit must release it so the * sender's value is not silently dropped from the chan's list. */ - chan_t ch; chan_init(&ch); - cancel_t c; cancel_init(&c); + xco_chan_t ch; xco_chan_init(&ch); + xco_cancel_t c; xco_cancel_init(&c); - chan_send_op_t op; - chan_send_op_init(&op, &ch, 0xABBA); + xco_chan_send_op_t op; + xco_chan_send_op_init(&op, &ch, 0xABBA); assert(!op.done.set); assert(ch.send_head == &op.csw.sw.base); - select_event_t sel; - select_input_t inputs[2]; - wait_or_cancel(&sel, inputs, &op.done.base, &c); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_wait_or_cancel(&sel, inputs, &op.done.base, &c); - cancel_set(&c); /* fires sel synchronously */ + xco_cancel_set(&c); /* fires sel synchronously */ uintptr_t v; - assert(event_try(&sel.done.base, &v)); - assert(v == WAIT_CANCELLED); + assert(xco_event_try(&sel.done.base, &v)); + assert(v == XCO_WAIT_CANCELLED); /* Send didn't happen — op still parked on the chan. The select * winning doesn't drain the chan's send list; deinit does. */ assert(ch.send_head == &op.csw.sw.base); - select_event_deinit(&sel); - chan_send_op_deinit(&op); + xco_select_event_deinit(&sel); + xco_chan_send_op_deinit(&op); assert(ch.send_head == NULL); } @@ -945,132 +945,132 @@ static void test_wait_or_cancel_send_op(void) { static void test_timer_basic(void) { /* Insert one timer; advance past its deadline; fire payload is the * deadline value. */ - pairing_heap_t h; pairing_heap_init(&h); - timer_t t; - timer_init(&t, &h.base, 100); - assert(!timer_fired(&t)); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); + xco_timer_t t; + xco_timer_init(&t, &h.base, 100); + assert(!xco_timer_fired(&t)); assert(t.in_heap); /* Not yet expired: advance is a no-op. */ - timers_advance(&h.base, 50); - assert(!timer_fired(&t)); + xco_timers_advance(&h.base, 50); + assert(!xco_timer_fired(&t)); /* Expired: fires. */ - timers_advance(&h.base, 100); - assert(timer_fired(&t)); + xco_timers_advance(&h.base, 100); + assert(xco_timer_fired(&t)); assert(!t.in_heap); uintptr_t v; - assert(event_try(timer_event(&t), &v)); + assert(xco_event_try(xco_timer_event(&t), &v)); assert(v == 100); /* Idempotent deinit (already fired). */ - timer_deinit(&t); + xco_timer_deinit(&t); } static void test_timer_peek(void) { - pairing_heap_t h; pairing_heap_init(&h); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); uint64_t out = 0xDEAD; - assert(!timers_peek(&h.base, &out)); + assert(!xco_timers_peek(&h.base, &out)); assert(out == 0xDEAD); /* untouched on empty */ - timer_t a, b, c; - timer_init(&a, &h.base, 300); - timer_init(&b, &h.base, 100); - timer_init(&c, &h.base, 200); + xco_timer_t a, b, c; + xco_timer_init(&a, &h.base, 300); + xco_timer_init(&b, &h.base, 100); + xco_timer_init(&c, &h.base, 200); - assert(timers_peek(&h.base, &out)); + assert(xco_timers_peek(&h.base, &out)); assert(out == 100); /* earliest deadline */ - timers_advance(&h.base, 250); + xco_timers_advance(&h.base, 250); /* b and c fired; a remains. */ - assert(timer_fired(&b)); - assert(timer_fired(&c)); - assert(!timer_fired(&a)); - assert(timers_peek(&h.base, &out)); + assert(xco_timer_fired(&b)); + assert(xco_timer_fired(&c)); + assert(!xco_timer_fired(&a)); + assert(xco_timers_peek(&h.base, &out)); assert(out == 300); - timer_deinit(&a); - timer_deinit(&b); - timer_deinit(&c); + xco_timer_deinit(&a); + xco_timer_deinit(&b); + xco_timer_deinit(&c); } static void test_timer_cancel(void) { /* Cancel before fire; later advance must not fire it. */ - pairing_heap_t h; pairing_heap_init(&h); - timer_t a, b, c; - timer_init(&a, &h.base, 100); - timer_init(&b, &h.base, 200); - timer_init(&c, &h.base, 300); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); + xco_timer_t a, b, c; + xco_timer_init(&a, &h.base, 100); + xco_timer_init(&b, &h.base, 200); + xco_timer_init(&c, &h.base, 300); - timer_deinit(&b); /* cancel middle */ + xco_timer_deinit(&b); /* cancel middle */ assert(!b.in_heap); - assert(!timer_fired(&b)); + assert(!xco_timer_fired(&b)); - timers_advance(&h.base, 1000); - assert(timer_fired(&a)); - assert(!timer_fired(&b)); /* cancelled — never fired */ - assert(timer_fired(&c)); + xco_timers_advance(&h.base, 1000); + assert(xco_timer_fired(&a)); + assert(!xco_timer_fired(&b)); /* cancelled — never fired */ + assert(xco_timer_fired(&c)); - timer_deinit(&a); - timer_deinit(&c); + xco_timer_deinit(&a); + xco_timer_deinit(&c); } static void test_timer_park_wake(void) { /* A waiter parked on a timer wakes when the timer fires. */ - runtime_t rt; rt_init(&rt); - pairing_heap_t h; pairing_heap_init(&h); - timer_t t; - timer_init(&t, &h.base, 500); - - waiter_t w; waiter_init(&w, &rt, timer_event(&t)); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_runtime_t rt; xco_rt_init(&rt); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); + xco_timer_t t; + xco_timer_init(&t, &h.base, 500); + + waiter_t w; waiter_init(&w, &rt, xco_timer_event(&t)); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); assert(t.done.waiters == &w.sw.base); - timers_advance(&h.base, 500); + xco_timers_advance(&h.base, 500); /* fire enqueued the step. */ assert(rt.head != NULL); - rt_run(&rt, 500); - assert(xstep_status(&w.base) == XSTEP_DEAD); + xco_rt_run(&rt, 500); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); assert(w.got == 500); /* deadline as payload */ - timer_deinit(&t); + xco_timer_deinit(&t); } static void test_timer_select(void) { /* Compose a timer into a select. */ - runtime_t rt; rt_init(&rt); - pairing_heap_t h; pairing_heap_init(&h); - timer_t t; timer_init(&t, &h.base, 200); - latch_t l; latch_init(&l); + xco_runtime_t rt; xco_rt_init(&rt); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); + xco_timer_t t; xco_timer_init(&t, &h.base, 200); + xco_latch_t l; xco_latch_init(&l); - select_event_t sel; - select_input_t inputs[2]; - event_t *srcs[2] = {timer_event(&t), &l.base}; - select_event_init(&sel, inputs, 2, srcs); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_event_t *srcs[2] = {xco_timer_event(&t), &l.base}; + xco_select_event_init(&sel, inputs, 2, srcs); waiter_t w; waiter_init(&w, &rt, &sel.done.base); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); - timers_advance(&h.base, 200); - rt_run(&rt, 200); - assert(xstep_status(&w.base) == XSTEP_DEAD); + xco_timers_advance(&h.base, 200); + xco_rt_run(&rt, 200); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); assert(w.got == 0); /* timer (input 0) won */ assert(inputs[0].value == 200); /* deadline captured */ assert(l.waiters == NULL); /* loser disarmed */ - select_event_deinit(&sel); - timer_deinit(&t); + xco_select_event_deinit(&sel); + xco_timer_deinit(&t); } static void test_timer_pairing_heap_order(void) { /* Insert many timers with non-sorted deadlines; advance must pop * them in deadline order. Stress the heap structure. */ - pairing_heap_t h; pairing_heap_init(&h); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); enum { N = 32 }; - timer_t ts[N]; + xco_timer_t ts[N]; /* A scrambled sequence — interleave halves so the insert order * exercises the heap's restructuring. */ uint64_t deadlines[N] = { @@ -1080,194 +1080,194 @@ static void test_timer_pairing_heap_order(void) { 19, 6, 21, 4, 26, 15, 23, 18, }; - for (int i = 0; i < N; i++) timer_init(&ts[i], &h.base, deadlines[i]); + for (int i = 0; i < N; i++) xco_timer_init(&ts[i], &h.base, deadlines[i]); /* Advance one tick at a time; verify exactly one timer fires per * matching deadline (since deadlines 1..32 are unique). */ int fired = 0; for (uint64_t now = 1; now <= N; now++) { - timers_advance(&h.base, now); + xco_timers_advance(&h.base, now); int fired_now = 0; for (int i = 0; i < N; i++) { - if (timer_fired(&ts[i]) && ts[i].deadline == now) fired_now++; + if (xco_timer_fired(&ts[i]) && ts[i].deadline == now) fired_now++; } assert(fired_now == 1); fired++; } assert(fired == N); - for (int i = 0; i < N; i++) assert(timer_fired(&ts[i])); + for (int i = 0; i < N; i++) assert(xco_timer_fired(&ts[i])); } static void test_timer_cancel_stress(void) { /* Insert N timers; cancel every other one; advance and verify only * the survivors fire, in order. Exercises non-root cancel paths. */ - pairing_heap_t h; pairing_heap_init(&h); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); enum { N = 16 }; - timer_t ts[N]; + xco_timer_t ts[N]; /* Deadlines: 1..N. Inserted in order so the heap shape is a long * single-spine; cancels hit non-root nodes. */ - for (int i = 0; i < N; i++) timer_init(&ts[i], &h.base, (uint64_t)(i + 1)); + for (int i = 0; i < N; i++) xco_timer_init(&ts[i], &h.base, (uint64_t)(i + 1)); /* Cancel even indices (deadlines 2,4,6,...). */ - for (int i = 0; i < N; i += 2) timer_deinit(&ts[i]); + for (int i = 0; i < N; i += 2) xco_timer_deinit(&ts[i]); - timers_advance(&h.base, N); + xco_timers_advance(&h.base, N); for (int i = 0; i < N; i++) { - if (i % 2 == 0) assert(!timer_fired(&ts[i])); - else assert( timer_fired(&ts[i])); + if (i % 2 == 0) assert(!xco_timer_fired(&ts[i])); + else assert( xco_timer_fired(&ts[i])); } - for (int i = 1; i < N; i += 2) timer_deinit(&ts[i]); + for (int i = 1; i < N; i += 2) xco_timer_deinit(&ts[i]); } static void test_timer_deinit_idempotent(void) { /* Deinit after fire is a no-op; deinit twice (without fire) is too. */ - pairing_heap_t h; pairing_heap_init(&h); - timer_t a, b; - timer_init(&a, &h.base, 10); - timer_init(&b, &h.base, 20); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); + xco_timer_t a, b; + xco_timer_init(&a, &h.base, 10); + xco_timer_init(&b, &h.base, 20); - timers_advance(&h.base, 10); - assert(timer_fired(&a) && !timer_fired(&b)); - timer_deinit(&a); /* already fired */ - timer_deinit(&a); /* twice — still no-op */ + xco_timers_advance(&h.base, 10); + assert(xco_timer_fired(&a) && !xco_timer_fired(&b)); + xco_timer_deinit(&a); /* already fired */ + xco_timer_deinit(&a); /* twice — still no-op */ - timer_deinit(&b); /* cancel before fire */ - timer_deinit(&b); /* twice — must not corrupt */ - timers_advance(&h.base, 1000); - assert(!timer_fired(&b)); /* never fired */ + xco_timer_deinit(&b); /* cancel before fire */ + xco_timer_deinit(&b); /* twice — must not corrupt */ + xco_timers_advance(&h.base, 1000); + assert(!xco_timer_fired(&b)); /* never fired */ } /* ---- Timeout ------------------------------------------------------- */ static void test_timeout_fires(void) { /* Bare timeout: advance past deadline, cancel becomes set. */ - pairing_heap_t h; pairing_heap_init(&h); - timeout_t to; - timeout_init(&to, &h.base, 100); - assert(!cancel_is_set(&to.cancel)); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); + xco_timeout_t to; + xco_timeout_init(&to, &h.base, 100); + assert(!xco_cancel_is_set(&to.cancel)); - timers_advance(&h.base, 100); - assert(cancel_is_set(&to.cancel)); /* bridge fired */ - assert(timer_fired(&to.timer)); + xco_timers_advance(&h.base, 100); + assert(xco_cancel_is_set(&to.cancel)); /* bridge fired */ + assert(xco_timer_fired(&to.timer)); - timeout_deinit(&to); /* idempotent */ + xco_timeout_deinit(&to); /* idempotent */ } static void test_timeout_deinit_before_fire(void) { /* Deinit a timeout before its deadline: cancel must remain unset * and the timer must be removed from the source so a later advance * doesn't fire freed memory. */ - pairing_heap_t h; pairing_heap_init(&h); - timeout_t to; - timeout_init(&to, &h.base, 100); - timeout_deinit(&to); - assert(!cancel_is_set(&to.cancel)); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); + xco_timeout_t to; + xco_timeout_init(&to, &h.base, 100); + xco_timeout_deinit(&to); + assert(!xco_cancel_is_set(&to.cancel)); /* Advance past the original deadline: nothing left to fire. */ - timers_advance(&h.base, 1000); - assert(!cancel_is_set(&to.cancel)); + xco_timers_advance(&h.base, 1000); + assert(!xco_cancel_is_set(&to.cancel)); } static void test_timeout_with_wait_or_cancel(void) { /* Canonical pattern: a waiter blocks on (ev, timeout.cancel). The - * timeout fires first; waiter resumes with WAIT_CANCELLED. */ - runtime_t rt; rt_init(&rt); - pairing_heap_t h; pairing_heap_init(&h); - rt_attach_timers(&rt, &h.base); + * timeout fires first; waiter resumes with XCO_WAIT_CANCELLED. */ + xco_runtime_t rt; xco_rt_init(&rt); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); + xco_rt_attach_timers(&rt, &h.base); - latch_t ev; latch_init(&ev); - timeout_t to; - timeout_init(&to, &h.base, 50); + xco_latch_t ev; xco_latch_init(&ev); + xco_timeout_t to; + xco_timeout_init(&to, &h.base, 50); - select_event_t sel; - select_input_t inputs[2]; - wait_or_cancel(&sel, inputs, &ev.base, &to.cancel); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_wait_or_cancel(&sel, inputs, &ev.base, &to.cancel); waiter_t w; waiter_init(&w, &rt, &sel.done.base); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); - /* rt_run advances the timer source itself: now=50 fires the timer, + /* xco_rt_run advances the timer source itself: now=50 fires the timer, * which fires the bridge, which sets cancel, which fires sel, - * which enqueues the waiter — all in one rt_run call. */ - rt_run(&rt, 50); - assert(xstep_status(&w.base) == XSTEP_DEAD); - assert(w.got == WAIT_CANCELLED); + * which enqueues the waiter — all in one xco_rt_run call. */ + xco_rt_run(&rt, 50); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); + assert(w.got == XCO_WAIT_CANCELLED); - select_event_deinit(&sel); - timeout_deinit(&to); + xco_select_event_deinit(&sel); + xco_timeout_deinit(&to); } static void test_timeout_ev_wins(void) { /* Event wins the race; timeout should be deinit'd cleanly without * having fired. */ - runtime_t rt; rt_init(&rt); - pairing_heap_t h; pairing_heap_init(&h); - rt_attach_timers(&rt, &h.base); + xco_runtime_t rt; xco_rt_init(&rt); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); + xco_rt_attach_timers(&rt, &h.base); - latch_t ev; latch_init(&ev); - timeout_t to; - timeout_init(&to, &h.base, 1000); + xco_latch_t ev; xco_latch_init(&ev); + xco_timeout_t to; + xco_timeout_init(&to, &h.base, 1000); - select_event_t sel; - select_input_t inputs[2]; - wait_or_cancel(&sel, inputs, &ev.base, &to.cancel); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_wait_or_cancel(&sel, inputs, &ev.base, &to.cancel); waiter_t w; waiter_init(&w, &rt, &sel.done.base); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); - - latch_set(&ev, 0xF00D); - rt_run(&rt, 50); /* now < deadline; timer untouched */ - assert(xstep_status(&w.base) == XSTEP_DEAD); - assert(w.got == WAIT_OK); - assert(inputs[WAIT_OK].value == 0xF00D); - assert(!cancel_is_set(&to.cancel)); - - select_event_deinit(&sel); - timeout_deinit(&to); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); + + xco_latch_set(&ev, 0xF00D); + xco_rt_run(&rt, 50); /* now < deadline; timer untouched */ + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); + assert(w.got == XCO_WAIT_OK); + assert(inputs[XCO_WAIT_OK].value == 0xF00D); + assert(!xco_cancel_is_set(&to.cancel)); + + xco_select_event_deinit(&sel); + xco_timeout_deinit(&to); /* Subsequent advance must not fire anything (timer was removed). */ - rt_run(&rt, 10000); - assert(!cancel_is_set(&to.cancel)); + xco_rt_run(&rt, 10000); + assert(!xco_cancel_is_set(&to.cancel)); } -/* ---- rt_run + timer integration ------------------------------------ */ +/* ---- xco_rt_run + timer integration ------------------------------------ */ static void test_rt_run_advances_timers(void) { - /* A waiter parked on a timer; rt_run with now=deadline advances the + /* A waiter parked on a timer; xco_rt_run with now=deadline advances the * source, fires the timer, drains the waker — all in one call. */ - runtime_t rt; rt_init(&rt); - pairing_heap_t h; pairing_heap_init(&h); - rt_attach_timers(&rt, &h.base); - - timer_t t; timer_init(&t, &h.base, 42); - waiter_t w; waiter_init(&w, &rt, timer_event(&t)); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_runtime_t rt; xco_rt_init(&rt); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); + xco_rt_attach_timers(&rt, &h.base); + + xco_timer_t t; xco_timer_init(&t, &h.base, 42); + waiter_t w; waiter_init(&w, &rt, xco_timer_event(&t)); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); assert(rt.head == NULL); /* parked, not queued */ - rt_run(&rt, 42); - assert(xstep_status(&w.base) == XSTEP_DEAD); + xco_rt_run(&rt, 42); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); assert(w.got == 42); assert(rt.head == NULL); - timer_deinit(&t); + xco_timer_deinit(&t); } static void test_rt_run_no_timers(void) { - /* Without an attached timer source, rt_run(rt, now) is the pure + /* Without an attached timer source, xco_rt_run(rt, now) is the pure * drainer regardless of `now`. */ - runtime_t rt; rt_init(&rt); - latch_t l; latch_init(&l); + xco_runtime_t rt; xco_rt_init(&rt); + xco_latch_t l; xco_latch_init(&l); waiter_t w; waiter_init(&w, &rt, &l.base); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); - latch_set(&l, 7); - rt_run(&rt, 99999); /* now ignored */ - assert(xstep_status(&w.base) == XSTEP_DEAD); + xco_latch_set(&l, 7); + xco_rt_run(&rt, 99999); /* now ignored */ + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); assert(w.got == 7); } @@ -1277,38 +1277,38 @@ static void test_rt_run_no_timers(void) { * but specialized for the "permit handed at fire time" semantics — the * resume is itself the proof of acquisition. */ typedef struct { - xstep_t base; - semaphore_t *sem; - runtime_t *rt; - step_waker_t sw; + xco_step_t base; + xco_semaphore_t *sem; + xco_runtime_t *rt; + xco_step_waker_t sw; int phase; bool got; } sem_acquirer_t; -static xstep_result_t sem_acquirer_step(xstep_t *s, uintptr_t v) { +static xco_step_result_t sem_acquirer_step(xco_step_t *s, uintptr_t v) { sem_acquirer_t *a = (sem_acquirer_t *)s; (void)v; switch (a->phase) { case 0: - if (event_try(semaphore_event(a->sem), NULL)) { + if (xco_event_try(xco_semaphore_event(a->sem), NULL)) { a->got = true; a->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } - step_waker_init(&a->sw, a->rt, &a->base); - event_park(semaphore_event(a->sem), &a->sw.base); + xco_step_waker_init(&a->sw, a->rt, &a->base); + xco_event_park(xco_semaphore_event(a->sem), &a->sw.base); a->phase = 1; - return (xstep_result_t){0, XSTEP_SUSPENDED}; + return (xco_step_result_t){0, XCO_STEP_SUSPENDED}; case 1: a->got = true; a->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } __builtin_unreachable(); } -static void sem_acquirer_init(sem_acquirer_t *a, runtime_t *rt, semaphore_t *s) { - a->base = (xstep_t){.step = sem_acquirer_step, .status = XSTEP_INIT}; +static void sem_acquirer_init(sem_acquirer_t *a, xco_runtime_t *rt, xco_semaphore_t *s) { + a->base = (xco_step_t){.step = sem_acquirer_step, .status = XCO_STEP_INIT}; a->sem = s; a->rt = rt; a->phase = 0; @@ -1317,59 +1317,59 @@ static void sem_acquirer_init(sem_acquirer_t *a, runtime_t *rt, semaphore_t *s) static void test_semaphore_inline_acquire(void) { /* permits > 0: try succeeds and decrements. */ - semaphore_t s; semaphore_init(&s, 2); - assert(event_try(semaphore_event(&s), NULL)); + xco_semaphore_t s; xco_semaphore_init(&s, 2); + assert(xco_event_try(xco_semaphore_event(&s), NULL)); assert(s.permits == 1); - assert(event_try(semaphore_event(&s), NULL)); + assert(xco_event_try(xco_semaphore_event(&s), NULL)); assert(s.permits == 0); - assert(!event_try(semaphore_event(&s), NULL)); + assert(!xco_event_try(xco_semaphore_event(&s), NULL)); assert(s.permits == 0); } static void test_semaphore_park_then_release(void) { /* Empty sem: acquirer parks; release wakes it with a permit. */ - runtime_t rt; rt_init(&rt); - semaphore_t s; semaphore_init(&s, 0); + xco_runtime_t rt; xco_rt_init(&rt); + xco_semaphore_t s; xco_semaphore_init(&s, 0); sem_acquirer_t a; sem_acquirer_init(&a, &rt, &s); - xstep(&a.base, 0); - assert(xstep_status(&a.base) == XSTEP_SUSPENDED); + xco_step(&a.base, 0); + assert(xco_step_status(&a.base) == XCO_STEP_SUSPENDED); assert(s.head == &a.sw.base); - semaphore_release(&s, 1); + xco_semaphore_release(&s, 1); /* Release prefers parked waiters: permit went straight to a, count stays 0. */ assert(s.permits == 0); assert(s.head == NULL); - rt_run(&rt, 0); - assert(xstep_status(&a.base) == XSTEP_DEAD); + xco_rt_run(&rt, 0); + assert(xco_step_status(&a.base) == XCO_STEP_DEAD); assert(a.got); } static void test_semaphore_fifo(void) { /* Three parked acquirers; release(2) wakes the first two in order; * the third stays parked. release(1) again wakes it. */ - runtime_t rt; rt_init(&rt); - semaphore_t s; semaphore_init(&s, 0); + xco_runtime_t rt; xco_rt_init(&rt); + xco_semaphore_t s; xco_semaphore_init(&s, 0); sem_acquirer_t a[3]; for (int i = 0; i < 3; i++) { sem_acquirer_init(&a[i], &rt, &s); - xstep(&a[i].base, 0); + xco_step(&a[i].base, 0); } assert(s.head == &a[0].sw.base); assert(s.tail == &a[2].sw.base); - semaphore_release(&s, 2); - rt_run(&rt, 0); - assert(xstep_status(&a[0].base) == XSTEP_DEAD); - assert(xstep_status(&a[1].base) == XSTEP_DEAD); - assert(xstep_status(&a[2].base) == XSTEP_SUSPENDED); + xco_semaphore_release(&s, 2); + xco_rt_run(&rt, 0); + assert(xco_step_status(&a[0].base) == XCO_STEP_DEAD); + assert(xco_step_status(&a[1].base) == XCO_STEP_DEAD); + assert(xco_step_status(&a[2].base) == XCO_STEP_SUSPENDED); assert(s.head == &a[2].sw.base); - semaphore_release(&s, 1); - rt_run(&rt, 0); - assert(xstep_status(&a[2].base) == XSTEP_DEAD); + xco_semaphore_release(&s, 1); + xco_rt_run(&rt, 0); + assert(xco_step_status(&a[2].base) == XCO_STEP_DEAD); assert(s.head == NULL); assert(s.permits == 0); } @@ -1377,66 +1377,66 @@ static void test_semaphore_fifo(void) { static void test_semaphore_release_overflow_to_count(void) { /* release(n) where n > waiters: hand to all waiters first, then add * the leftover to permits. */ - runtime_t rt; rt_init(&rt); - semaphore_t s; semaphore_init(&s, 0); + xco_runtime_t rt; xco_rt_init(&rt); + xco_semaphore_t s; xco_semaphore_init(&s, 0); sem_acquirer_t a; sem_acquirer_init(&a, &rt, &s); - xstep(&a.base, 0); + xco_step(&a.base, 0); - semaphore_release(&s, 5); + xco_semaphore_release(&s, 5); /* a got 1; 4 leftover sat as permits. */ assert(s.permits == 4); - rt_run(&rt, 0); + xco_rt_run(&rt, 0); assert(a.got); assert(s.permits == 4); } static void test_semaphore_select_acquire(void) { - /* Compose a sem acquire with a cancel via wait_or_cancel: cancel wins. */ - runtime_t rt; rt_init(&rt); - semaphore_t s; semaphore_init(&s, 0); - cancel_t c; cancel_init(&c); + /* Compose a sem acquire with a cancel via xco_wait_or_cancel: cancel wins. */ + xco_runtime_t rt; xco_rt_init(&rt); + xco_semaphore_t s; xco_semaphore_init(&s, 0); + xco_cancel_t c; xco_cancel_init(&c); - select_event_t sel; - select_input_t inputs[2]; - wait_or_cancel(&sel, inputs, semaphore_event(&s), &c); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_wait_or_cancel(&sel, inputs, xco_semaphore_event(&s), &c); waiter_t w; waiter_init(&w, &rt, &sel.done.base); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); assert(s.head != NULL); /* sem-side waker parked */ - cancel_set(&c); - rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_DEAD); - assert(w.got == WAIT_CANCELLED); - assert(s.head == NULL); /* select_input_fire detached us */ + xco_cancel_set(&c); + xco_rt_run(&rt, 0); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); + assert(w.got == XCO_WAIT_CANCELLED); + assert(s.head == NULL); /* xco_select_input_fire detached us */ - select_event_deinit(&sel); + xco_select_event_deinit(&sel); /* No leak: a future release does not over-grant past the count. */ - semaphore_release(&s, 1); + xco_semaphore_release(&s, 1); assert(s.permits == 1); } static void test_semaphore_binary_mutex(void) { /* Binary semaphore as mutex: init permits=1; first take succeeds inline, * second parks, release on the way out wakes the waiter. */ - runtime_t rt; rt_init(&rt); - semaphore_t mu; semaphore_init(&mu, 1); + xco_runtime_t rt; xco_rt_init(&rt); + xco_semaphore_t mu; xco_semaphore_init(&mu, 1); /* Holder takes it inline. */ - assert(event_try(semaphore_event(&mu), NULL)); + assert(xco_event_try(xco_semaphore_event(&mu), NULL)); assert(mu.permits == 0); /* Contender parks. */ sem_acquirer_t b; sem_acquirer_init(&b, &rt, &mu); - xstep(&b.base, 0); - assert(xstep_status(&b.base) == XSTEP_SUSPENDED); + xco_step(&b.base, 0); + assert(xco_step_status(&b.base) == XCO_STEP_SUSPENDED); /* Holder releases on exit. */ - semaphore_release(&mu, 1); - rt_run(&rt, 0); - assert(xstep_status(&b.base) == XSTEP_DEAD); + xco_semaphore_release(&mu, 1); + xco_rt_run(&rt, 0); + assert(xco_step_status(&b.base) == XCO_STEP_DEAD); assert(b.got); } @@ -1444,39 +1444,39 @@ static void test_semaphore_binary_mutex(void) { /* Queue sender: same shape as the chan sender_t. */ typedef struct { - xstep_t base; - queue_t *q; - runtime_t *rt; - queue_send_waker_t qsw; + xco_step_t base; + xco_queue_t *q; + xco_runtime_t *rt; + xco_queue_send_waker_t qsw; uintptr_t value; int phase; bool done; } queue_sender_t; -static xstep_result_t queue_sender_step(xstep_t *s, uintptr_t v) { +static xco_step_result_t queue_sender_step(xco_step_t *s, uintptr_t v) { queue_sender_t *snd = (queue_sender_t *)s; (void)v; switch (snd->phase) { case 0: - if (queue_try_send(snd->q, snd->value)) { + if (xco_queue_try_send(snd->q, snd->value)) { snd->done = true; snd->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } - queue_send_waker_init(&snd->qsw, snd->rt, &snd->base, snd->value); - queue_park_send(snd->q, &snd->qsw); + xco_queue_send_waker_init(&snd->qsw, snd->rt, &snd->base, snd->value); + xco_queue_park_send(snd->q, &snd->qsw); snd->phase = 1; - return (xstep_result_t){0, XSTEP_SUSPENDED}; + return (xco_step_result_t){0, XCO_STEP_SUSPENDED}; case 1: snd->done = true; snd->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } __builtin_unreachable(); } -static void queue_sender_init(queue_sender_t *s, runtime_t *rt, queue_t *q, uintptr_t value) { - s->base = (xstep_t){.step = queue_sender_step, .status = XSTEP_INIT}; +static void queue_sender_init(queue_sender_t *s, xco_runtime_t *rt, xco_queue_t *q, uintptr_t value) { + s->base = (xco_step_t){.step = queue_sender_step, .status = XCO_STEP_INIT}; s->q = q; s->rt = rt; s->value = value; @@ -1486,186 +1486,186 @@ static void queue_sender_init(queue_sender_t *s, runtime_t *rt, queue_t *q, uint static void test_queue_block_buffered(void) { /* cap=3, BLOCK: three sends fill the buffer; three recvs drain in order. */ - runtime_t rt; rt_init(&rt); + xco_runtime_t rt; xco_rt_init(&rt); uintptr_t buf[3]; - queue_t q; queue_init(&q, buf, 3, QUEUE_BLOCK); + xco_queue_t q; xco_queue_init(&q, buf, 3, XCO_QUEUE_BLOCK); - assert(queue_try_send(&q, 10)); - assert(queue_try_send(&q, 20)); - assert(queue_try_send(&q, 30)); + assert(xco_queue_try_send(&q, 10)); + assert(xco_queue_try_send(&q, 20)); + assert(xco_queue_try_send(&q, 30)); assert(q.len == 3); /* Buffer full: try_send fails. */ - assert(!queue_try_send(&q, 40)); + assert(!xco_queue_try_send(&q, 40)); uintptr_t v; - assert(event_try(queue_recv_event(&q), &v) && v == 10); - assert(event_try(queue_recv_event(&q), &v) && v == 20); - assert(event_try(queue_recv_event(&q), &v) && v == 30); - assert(!event_try(queue_recv_event(&q), &v)); /* empty */ + assert(xco_event_try(xco_queue_recv_event(&q), &v) && v == 10); + assert(xco_event_try(xco_queue_recv_event(&q), &v) && v == 20); + assert(xco_event_try(xco_queue_recv_event(&q), &v) && v == 30); + assert(!xco_event_try(xco_queue_recv_event(&q), &v)); /* empty */ assert(q.len == 0); (void)rt; } static void test_queue_block_sender_parks(void) { /* Buffer fills, next sender parks; a recv unblocks it. */ - runtime_t rt; rt_init(&rt); + xco_runtime_t rt; xco_rt_init(&rt); uintptr_t buf[2]; - queue_t q; queue_init(&q, buf, 2, QUEUE_BLOCK); + xco_queue_t q; xco_queue_init(&q, buf, 2, XCO_QUEUE_BLOCK); - assert(queue_try_send(&q, 1)); - assert(queue_try_send(&q, 2)); + assert(xco_queue_try_send(&q, 1)); + assert(xco_queue_try_send(&q, 2)); queue_sender_t s; queue_sender_init(&s, &rt, &q, 3); - xstep(&s.base, 0); - assert(xstep_status(&s.base) == XSTEP_SUSPENDED); + xco_step(&s.base, 0); + assert(xco_step_status(&s.base) == XCO_STEP_SUSPENDED); assert(q.send_head == &s.qsw.sw.base); /* Recv pops 1; 3 should slot into the buffer and the parked sender wakes. */ uintptr_t v; - assert(event_try(queue_recv_event(&q), &v) && v == 1); - rt_run(&rt, 0); - assert(xstep_status(&s.base) == XSTEP_DEAD); + assert(xco_event_try(xco_queue_recv_event(&q), &v) && v == 1); + xco_rt_run(&rt, 0); + assert(xco_step_status(&s.base) == XCO_STEP_DEAD); assert(s.done); assert(q.send_head == NULL); /* Buffer should now hold 2, 3. */ - assert(event_try(queue_recv_event(&q), &v) && v == 2); - assert(event_try(queue_recv_event(&q), &v) && v == 3); + assert(xco_event_try(xco_queue_recv_event(&q), &v) && v == 2); + assert(xco_event_try(xco_queue_recv_event(&q), &v) && v == 3); assert(q.len == 0); } static void test_queue_drop_newest(void) { /* When full, try_send returns true but silently discards. */ uintptr_t buf[2]; - queue_t q; queue_init(&q, buf, 2, QUEUE_DROP_NEWEST); + xco_queue_t q; xco_queue_init(&q, buf, 2, XCO_QUEUE_DROP_NEWEST); - assert(queue_try_send(&q, 1)); - assert(queue_try_send(&q, 2)); - assert(queue_try_send(&q, 3)); /* full → drop 3 */ - assert(queue_try_send(&q, 4)); /* still full → drop 4 */ + assert(xco_queue_try_send(&q, 1)); + assert(xco_queue_try_send(&q, 2)); + assert(xco_queue_try_send(&q, 3)); /* full → drop 3 */ + assert(xco_queue_try_send(&q, 4)); /* still full → drop 4 */ uintptr_t v; - assert(event_try(queue_recv_event(&q), &v) && v == 1); - assert(event_try(queue_recv_event(&q), &v) && v == 2); - assert(!event_try(queue_recv_event(&q), &v)); + assert(xco_event_try(xco_queue_recv_event(&q), &v) && v == 1); + assert(xco_event_try(xco_queue_recv_event(&q), &v) && v == 2); + assert(!xco_event_try(xco_queue_recv_event(&q), &v)); } static void test_queue_drop_oldest(void) { /* When full, try_send returns true and evicts head, pushes new tail. */ uintptr_t buf[2]; - queue_t q; queue_init(&q, buf, 2, QUEUE_DROP_OLDEST); + xco_queue_t q; xco_queue_init(&q, buf, 2, XCO_QUEUE_DROP_OLDEST); - assert(queue_try_send(&q, 1)); - assert(queue_try_send(&q, 2)); - assert(queue_try_send(&q, 3)); /* full → evict 1, buffer = [2, 3] */ - assert(queue_try_send(&q, 4)); /* full → evict 2, buffer = [3, 4] */ + assert(xco_queue_try_send(&q, 1)); + assert(xco_queue_try_send(&q, 2)); + assert(xco_queue_try_send(&q, 3)); /* full → evict 1, buffer = [2, 3] */ + assert(xco_queue_try_send(&q, 4)); /* full → evict 2, buffer = [3, 4] */ uintptr_t v; - assert(event_try(queue_recv_event(&q), &v) && v == 3); - assert(event_try(queue_recv_event(&q), &v) && v == 4); - assert(!event_try(queue_recv_event(&q), &v)); + assert(xco_event_try(xco_queue_recv_event(&q), &v) && v == 3); + assert(xco_event_try(xco_queue_recv_event(&q), &v) && v == 4); + assert(!xco_event_try(xco_queue_recv_event(&q), &v)); } static void test_queue_direct_handoff(void) { /* Receiver parked; try_send hands off directly, bypassing the buffer. * Works the same regardless of policy — exercise BLOCK here. */ - runtime_t rt; rt_init(&rt); + xco_runtime_t rt; xco_rt_init(&rt); uintptr_t buf[2]; - queue_t q; queue_init(&q, buf, 2, QUEUE_BLOCK); + xco_queue_t q; xco_queue_init(&q, buf, 2, XCO_QUEUE_BLOCK); - waiter_t r; waiter_init(&r, &rt, queue_recv_event(&q)); - xstep(&r.base, 0); - assert(xstep_status(&r.base) == XSTEP_SUSPENDED); + waiter_t r; waiter_init(&r, &rt, xco_queue_recv_event(&q)); + xco_step(&r.base, 0); + assert(xco_step_status(&r.base) == XCO_STEP_SUSPENDED); assert(q.recv_head == &r.sw.base); - assert(queue_try_send(&q, 0xCAFE)); + assert(xco_queue_try_send(&q, 0xCAFE)); assert(q.len == 0); /* didn't touch the buffer */ assert(q.recv_head == NULL); - rt_run(&rt, 0); - assert(xstep_status(&r.base) == XSTEP_DEAD); + xco_rt_run(&rt, 0); + assert(xco_step_status(&r.base) == XCO_STEP_DEAD); assert(r.got == 0xCAFE); } static void test_queue_recv_blocks_then_drains_buffered(void) { /* Receivers wait when empty. After buffer warms up, recvs pop FIFO * — no value-skipping when both sides race past one another. */ - runtime_t rt; rt_init(&rt); + xco_runtime_t rt; xco_rt_init(&rt); uintptr_t buf[2]; - queue_t q; queue_init(&q, buf, 2, QUEUE_BLOCK); + xco_queue_t q; xco_queue_init(&q, buf, 2, XCO_QUEUE_BLOCK); /* 2 receivers parked. */ waiter_t r1, r2; - waiter_init(&r1, &rt, queue_recv_event(&q)); - waiter_init(&r2, &rt, queue_recv_event(&q)); - xstep(&r1.base, 0); - xstep(&r2.base, 0); + waiter_init(&r1, &rt, xco_queue_recv_event(&q)); + waiter_init(&r2, &rt, xco_queue_recv_event(&q)); + xco_step(&r1.base, 0); + xco_step(&r2.base, 0); /* Two sends → direct handoff to the two parked receivers, not buffered. */ - assert(queue_try_send(&q, 100)); - assert(queue_try_send(&q, 200)); + assert(xco_queue_try_send(&q, 100)); + assert(xco_queue_try_send(&q, 200)); assert(q.len == 0); - rt_run(&rt, 0); - assert(xstep_status(&r1.base) == XSTEP_DEAD && r1.got == 100); - assert(xstep_status(&r2.base) == XSTEP_DEAD && r2.got == 200); + xco_rt_run(&rt, 0); + assert(xco_step_status(&r1.base) == XCO_STEP_DEAD && r1.got == 100); + assert(xco_step_status(&r2.base) == XCO_STEP_DEAD && r2.got == 200); } static void test_queue_select_recv(void) { /* Queue recv composes into select. */ - runtime_t rt; rt_init(&rt); + xco_runtime_t rt; xco_rt_init(&rt); uintptr_t buf[1]; - queue_t q; queue_init(&q, buf, 1, QUEUE_BLOCK); - latch_t l; latch_init(&l); + xco_queue_t q; xco_queue_init(&q, buf, 1, XCO_QUEUE_BLOCK); + xco_latch_t l; xco_latch_init(&l); - select_event_t sel; - select_input_t inputs[2]; - event_t *srcs[2] = {queue_recv_event(&q), &l.base}; - select_event_init(&sel, inputs, 2, srcs); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_event_t *srcs[2] = {xco_queue_recv_event(&q), &l.base}; + xco_select_event_init(&sel, inputs, 2, srcs); waiter_t w; waiter_init(&w, &rt, &sel.done.base); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); - assert(queue_try_send(&q, 0xBEEF)); - rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_DEAD); + assert(xco_queue_try_send(&q, 0xBEEF)); + xco_rt_run(&rt, 0); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); assert(w.got == 0); assert(inputs[0].value == 0xBEEF); - select_event_deinit(&sel); + xco_select_event_deinit(&sel); } static void test_queue_unpark_send(void) { /* Cancel a parked sender; remaining FIFO order intact. */ - runtime_t rt; rt_init(&rt); + xco_runtime_t rt; xco_rt_init(&rt); uintptr_t buf[1]; - queue_t q; queue_init(&q, buf, 1, QUEUE_BLOCK); + xco_queue_t q; xco_queue_init(&q, buf, 1, XCO_QUEUE_BLOCK); - assert(queue_try_send(&q, 1)); /* fills buffer */ + assert(xco_queue_try_send(&q, 1)); /* fills buffer */ queue_sender_t a, b, d; queue_sender_init(&a, &rt, &q, 2); queue_sender_init(&b, &rt, &q, 3); queue_sender_init(&d, &rt, &q, 4); - xstep(&a.base, 0); - xstep(&b.base, 0); - xstep(&d.base, 0); + xco_step(&a.base, 0); + xco_step(&b.base, 0); + xco_step(&d.base, 0); - queue_unpark_send(&q, &b.qsw); - queue_unpark_send(&q, &b.qsw); /* idempotent */ + xco_queue_unpark_send(&q, &b.qsw); + xco_queue_unpark_send(&q, &b.qsw); /* idempotent */ /* Drain: 1, then a's 2 (slots into buffer when 1 popped), then d's 4. */ uintptr_t v; - assert(event_try(queue_recv_event(&q), &v) && v == 1); - rt_run(&rt, 0); - assert(xstep_status(&a.base) == XSTEP_DEAD); - assert(event_try(queue_recv_event(&q), &v) && v == 2); - rt_run(&rt, 0); - assert(xstep_status(&d.base) == XSTEP_DEAD); - assert(event_try(queue_recv_event(&q), &v) && v == 4); + assert(xco_event_try(xco_queue_recv_event(&q), &v) && v == 1); + xco_rt_run(&rt, 0); + assert(xco_step_status(&a.base) == XCO_STEP_DEAD); + assert(xco_event_try(xco_queue_recv_event(&q), &v) && v == 2); + xco_rt_run(&rt, 0); + assert(xco_step_status(&d.base) == XCO_STEP_DEAD); + assert(xco_event_try(xco_queue_recv_event(&q), &v) && v == 4); /* b never sent its value, stayed SUSPENDED. */ - assert(xstep_status(&b.base) == XSTEP_SUSPENDED); + assert(xco_step_status(&b.base) == XCO_STEP_SUSPENDED); } /* ---- Broadcast ----------------------------------------------------- */ @@ -1673,396 +1673,396 @@ static void test_queue_unpark_send(void) { /* Subscriber: parks on the broadcast, captures fire payload, re-parks * on resume. n_seen counts how many publishes it received. */ typedef struct { - xstep_t base; - broadcast_t *b; - runtime_t *rt; - step_waker_t sw; + xco_step_t base; + xco_broadcast_t *b; + xco_runtime_t *rt; + xco_step_waker_t sw; uintptr_t last; int n_seen; int target; /* unsubscribe after this many */ } bcast_sub_t; -static xstep_result_t bcast_sub_step(xstep_t *s, uintptr_t v) { +static xco_step_result_t bcast_sub_step(xco_step_t *s, uintptr_t v) { bcast_sub_t *b = (bcast_sub_t *)s; if (b->n_seen > 0) { /* Resume from a fire — capture and decide whether to re-park. */ b->last = v; } if (b->n_seen >= b->target) { - return (xstep_result_t){b->last, XSTEP_DEAD}; + return (xco_step_result_t){b->last, XCO_STEP_DEAD}; } - /* Re-park: the runtime returned sw fully detached, so event_park + /* Re-park: the runtime returned sw fully detached, so xco_event_park * works without re-init. (Init was done once in bcast_sub_init.) */ - event_park(broadcast_event(b->b), &b->sw.base); + xco_event_park(xco_broadcast_event(b->b), &b->sw.base); b->n_seen++; - return (xstep_result_t){0, XSTEP_SUSPENDED}; + return (xco_step_result_t){0, XCO_STEP_SUSPENDED}; } -static void bcast_sub_init(bcast_sub_t *s, runtime_t *rt, broadcast_t *b, int target) { - s->base = (xstep_t){.step = bcast_sub_step, .status = XSTEP_INIT}; +static void bcast_sub_init(bcast_sub_t *s, xco_runtime_t *rt, xco_broadcast_t *b, int target) { + s->base = (xco_step_t){.step = bcast_sub_step, .status = XCO_STEP_INIT}; s->b = b; s->rt = rt; s->last = 0; s->n_seen = 0; s->target = target; - step_waker_init(&s->sw, rt, &s->base); + xco_step_waker_init(&s->sw, rt, &s->base); } static void test_broadcast_try_always_false(void) { /* No "ready now" state; subscribers always wait for the next publish. */ - broadcast_t b; broadcast_init(&b); - assert(!event_try(broadcast_event(&b), NULL)); - broadcast_publish(&b, 7); + xco_broadcast_t b; xco_broadcast_init(&b); + assert(!xco_event_try(xco_broadcast_event(&b), NULL)); + xco_broadcast_publish(&b, 7); /* Even after a publish, try is still false — value is read via accessor. */ - assert(!event_try(broadcast_event(&b), NULL)); - assert(broadcast_has_value(&b)); - assert(broadcast_value(&b) == 7); + assert(!xco_event_try(xco_broadcast_event(&b), NULL)); + assert(xco_broadcast_has_value(&b)); + assert(xco_broadcast_value(&b) == 7); } static void test_broadcast_publish_wakes_all(void) { /* Three subscribers parked; one publish wakes all with the value. */ - runtime_t rt; rt_init(&rt); - broadcast_t b; broadcast_init(&b); + xco_runtime_t rt; xco_rt_init(&rt); + xco_broadcast_t b; xco_broadcast_init(&b); bcast_sub_t s[3]; for (int i = 0; i < 3; i++) { bcast_sub_init(&s[i], &rt, &b, 1); /* one publish then exit */ - xstep(&s[i].base, 0); - assert(xstep_status(&s[i].base) == XSTEP_SUSPENDED); + xco_step(&s[i].base, 0); + assert(xco_step_status(&s[i].base) == XCO_STEP_SUSPENDED); } - broadcast_publish(&b, 0xBEEF); + xco_broadcast_publish(&b, 0xBEEF); assert(b.waiters == NULL); /* drained */ - rt_run(&rt, 0); + xco_rt_run(&rt, 0); for (int i = 0; i < 3; i++) { - assert(xstep_status(&s[i].base) == XSTEP_DEAD); + assert(xco_step_status(&s[i].base) == XCO_STEP_DEAD); assert(s[i].last == 0xBEEF); } } static void test_broadcast_rearm(void) { /* Subscriber parks for two publishes; receives both values in order. */ - runtime_t rt; rt_init(&rt); - broadcast_t b; broadcast_init(&b); + xco_runtime_t rt; xco_rt_init(&rt); + xco_broadcast_t b; xco_broadcast_init(&b); bcast_sub_t s; bcast_sub_init(&s, &rt, &b, 2); - xstep(&s.base, 0); + xco_step(&s.base, 0); - broadcast_publish(&b, 11); - rt_run(&rt, 0); - assert(xstep_status(&s.base) == XSTEP_SUSPENDED); /* re-armed */ + xco_broadcast_publish(&b, 11); + xco_rt_run(&rt, 0); + assert(xco_step_status(&s.base) == XCO_STEP_SUSPENDED); /* re-armed */ assert(s.last == 11); - broadcast_publish(&b, 22); - rt_run(&rt, 0); - assert(xstep_status(&s.base) == XSTEP_DEAD); + xco_broadcast_publish(&b, 22); + xco_rt_run(&rt, 0); + assert(xco_step_status(&s.base) == XCO_STEP_DEAD); assert(s.last == 22); } static void test_broadcast_missed_publish(void) { /* Subscriber not parked at publish time misses it; the next publish * wakes them with that value (no replay, no coalescing into one). */ - runtime_t rt; rt_init(&rt); - broadcast_t b; broadcast_init(&b); + xco_runtime_t rt; xco_rt_init(&rt); + xco_broadcast_t b; xco_broadcast_init(&b); /* Publish before anyone subscribes — value is in the slot but no one wakes. */ - broadcast_publish(&b, 1); - assert(broadcast_value(&b) == 1); + xco_broadcast_publish(&b, 1); + assert(xco_broadcast_value(&b) == 1); bcast_sub_t s; bcast_sub_init(&s, &rt, &b, 1); - xstep(&s.base, 0); - assert(xstep_status(&s.base) == XSTEP_SUSPENDED); /* didn't see the prior */ + xco_step(&s.base, 0); + assert(xco_step_status(&s.base) == XCO_STEP_SUSPENDED); /* didn't see the prior */ - broadcast_publish(&b, 2); - rt_run(&rt, 0); - assert(xstep_status(&s.base) == XSTEP_DEAD); + xco_broadcast_publish(&b, 2); + xco_rt_run(&rt, 0); + assert(xco_step_status(&s.base) == XCO_STEP_DEAD); assert(s.last == 2); } static void test_broadcast_unpark(void) { - /* event_unpark removes a subscriber cleanly. */ - runtime_t rt; rt_init(&rt); - broadcast_t b; broadcast_init(&b); + /* xco_event_unpark removes a subscriber cleanly. */ + xco_runtime_t rt; xco_rt_init(&rt); + xco_broadcast_t b; xco_broadcast_init(&b); - step_waker_t sw1, sw2, sw3; - step_waker_init(&sw1, &rt, (xstep_t *)0x1); - step_waker_init(&sw2, &rt, (xstep_t *)0x2); - step_waker_init(&sw3, &rt, (xstep_t *)0x3); - event_park(broadcast_event(&b), &sw1.base); - event_park(broadcast_event(&b), &sw2.base); - event_park(broadcast_event(&b), &sw3.base); + xco_step_waker_t sw1, sw2, sw3; + xco_step_waker_init(&sw1, &rt, (xco_step_t *)0x1); + xco_step_waker_init(&sw2, &rt, (xco_step_t *)0x2); + xco_step_waker_init(&sw3, &rt, (xco_step_t *)0x3); + xco_event_park(xco_broadcast_event(&b), &sw1.base); + xco_event_park(xco_broadcast_event(&b), &sw2.base); + xco_event_park(xco_broadcast_event(&b), &sw3.base); - event_unpark(broadcast_event(&b), &sw2.base); - event_unpark(broadcast_event(&b), &sw2.base); /* idempotent */ + xco_event_unpark(xco_broadcast_event(&b), &sw2.base); + xco_event_unpark(xco_broadcast_event(&b), &sw2.base); /* idempotent */ int seen1 = 0, seen3 = 0; - for (waker_t *w = b.waiters; w; w = w->next) { + for (xco_waker_t *w = b.waiters; w; w = w->next) { if (w == &sw1.base) seen1 = 1; if (w == &sw3.base) seen3 = 1; assert(w != &sw2.base); } assert(seen1 && seen3); - event_unpark(broadcast_event(&b), &sw1.base); - event_unpark(broadcast_event(&b), &sw3.base); + xco_event_unpark(xco_broadcast_event(&b), &sw1.base); + xco_event_unpark(xco_broadcast_event(&b), &sw3.base); } -/* ---- Task (state-machine xstep) ------------------------------------ */ +/* ---- Task (state-machine xco_step) ------------------------------------ */ /* A countdown SM that completes after N steps, returning a final value. - * On every step it checks task->cancel via event_try and bails early - * (winding down to XSTEP_DEAD) if it's set. Demonstrates the cooperation + * On every step it checks task->cancel via xco_event_try and bails early + * (winding down to XCO_STEP_DEAD) if it's set. Demonstrates the cooperation * pattern: cancel is a signal, the SM owns the unwind. */ typedef struct { - xstep_t base; - task_t *task; + xco_step_t base; + xco_task_t *task; int remaining; uintptr_t final; } taskbody_t; -static xstep_result_t taskbody_step(xstep_t *s, uintptr_t v) { +static xco_step_result_t taskbody_step(xco_step_t *s, uintptr_t v) { taskbody_t *cd = (taskbody_t *)s; (void)v; - if (task_is_cancelled(cd->task)) { - task_done(cd->task, 0); /* cooperative unwind */ - return (xstep_result_t){0, XSTEP_DEAD}; + if (xco_task_is_cancelled(cd->task)) { + xco_task_done(cd->task, 0); /* cooperative unwind */ + return (xco_step_result_t){0, XCO_STEP_DEAD}; } if (cd->remaining-- == 0) { - task_done(cd->task, cd->final); - return (xstep_result_t){cd->final, XSTEP_DEAD}; + xco_task_done(cd->task, cd->final); + return (xco_step_result_t){cd->final, XCO_STEP_DEAD}; } - return (xstep_result_t){0, XSTEP_SUSPENDED}; + return (xco_step_result_t){0, XCO_STEP_SUSPENDED}; } -static void taskbody_init(taskbody_t *cd, task_t *t, int n, uintptr_t final) { - cd->base = (xstep_t){.step = taskbody_step, .status = XSTEP_INIT}; +static void taskbody_init(taskbody_t *cd, xco_task_t *t, int n, uintptr_t final) { + cd->base = (xco_step_t){.step = taskbody_step, .status = XCO_STEP_INIT}; cd->task = t; cd->remaining = n; cd->final = final; } static void test_task_state_machine_join(void) { - /* Drive a countdown SM as a task; join via task_done_event. */ - runtime_t rt; rt_init(&rt); + /* Drive a countdown SM as a task; join via xco_task_done_event. */ + xco_runtime_t rt; xco_rt_init(&rt); taskbody_t cd; - task_t t; + xco_task_t t; taskbody_init(&cd, &t, 3, 0xAAAA); - task_init(&t, &cd.base); + xco_task_init(&t, &cd.base); - assert(!task_finished(&t)); + assert(!xco_task_finished(&t)); /* Pump it manually. */ - while (xstep_status(&cd.base) != XSTEP_DEAD) { - xstep(&cd.base, 0); + while (xco_step_status(&cd.base) != XCO_STEP_DEAD) { + xco_step(&cd.base, 0); } - assert(task_finished(&t)); + assert(xco_task_finished(&t)); /* A latched join: try the done event for the return value. */ uintptr_t v; - assert(event_try(task_done_event(&t), &v)); + assert(xco_event_try(xco_task_done_event(&t), &v)); assert(v == 0xAAAA); } static void test_task_state_machine_cancel(void) { /* Cancel mid-flight; SM observes and unwinds. Joiner sees done with * the cooperative-unwind sentinel value (here, 0). */ - runtime_t rt; rt_init(&rt); + xco_runtime_t rt; xco_rt_init(&rt); taskbody_t cd; - task_t t; + xco_task_t t; taskbody_init(&cd, &t, 100, 0xAAAA); - task_init(&t, &cd.base); + xco_task_init(&t, &cd.base); - /* A second xstep waiting for completion via wait_or_cancel-style + /* A second xco_step waiting for completion via xco_wait_or_cancel-style * shape — here, just a direct waiter on the done event. */ - waiter_t joiner; waiter_init(&joiner, &rt, task_done_event(&t)); - xstep(&joiner.base, 0); - assert(xstep_status(&joiner.base) == XSTEP_SUSPENDED); + waiter_t joiner; waiter_init(&joiner, &rt, xco_task_done_event(&t)); + xco_step(&joiner.base, 0); + assert(xco_step_status(&joiner.base) == XCO_STEP_SUSPENDED); /* Step the task a few times. */ - xstep(&cd.base, 0); - xstep(&cd.base, 0); - assert(!task_finished(&t)); + xco_step(&cd.base, 0); + xco_step(&cd.base, 0); + assert(!xco_task_finished(&t)); - cancel_set(task_cancel(&t)); + xco_cancel_set(xco_task_cancel(&t)); /* Next step observes cancel and dies. */ - xstep(&cd.base, 0); - assert(xstep_status(&cd.base) == XSTEP_DEAD); - assert(task_finished(&t)); + xco_step(&cd.base, 0); + assert(xco_step_status(&cd.base) == XCO_STEP_DEAD); + assert(xco_task_finished(&t)); /* Joiner wakes with the done payload (0 from the cooperative unwind). */ - rt_run(&rt, 0); - assert(xstep_status(&joiner.base) == XSTEP_DEAD); + xco_rt_run(&rt, 0); + assert(xco_step_status(&joiner.base) == XCO_STEP_DEAD); assert(joiner.got == 0); } /* ---- Countdown ----------------------------------------------------- */ static void test_countdown_basic(void) { - countdown_t cd; countdown_init(&cd, 3); - assert(!countdown_fired(&cd)); - countdown_done(&cd); - countdown_done(&cd); - assert(!countdown_fired(&cd)); - countdown_done(&cd); - assert(countdown_fired(&cd)); + xco_countdown_t cd; xco_countdown_init(&cd, 3); + assert(!xco_countdown_fired(&cd)); + xco_countdown_done(&cd); + xco_countdown_done(&cd); + assert(!xco_countdown_fired(&cd)); + xco_countdown_done(&cd); + assert(xco_countdown_fired(&cd)); uintptr_t v = 0xBAD; - assert(event_try(countdown_event(&cd), &v)); + assert(xco_event_try(xco_countdown_event(&cd), &v)); assert(v == 0); } static void test_countdown_zero_init(void) { /* n=0: latch fires immediately. */ - countdown_t cd; countdown_init(&cd, 0); - assert(countdown_fired(&cd)); + xco_countdown_t cd; xco_countdown_init(&cd, 0); + assert(xco_countdown_fired(&cd)); } static void test_countdown_add(void) { - countdown_t cd; countdown_init(&cd, 1); - countdown_add(&cd, 2); - countdown_done(&cd); - countdown_done(&cd); - assert(!countdown_fired(&cd)); - countdown_done(&cd); - assert(countdown_fired(&cd)); + xco_countdown_t cd; xco_countdown_init(&cd, 1); + xco_countdown_add(&cd, 2); + xco_countdown_done(&cd); + xco_countdown_done(&cd); + assert(!xco_countdown_fired(&cd)); + xco_countdown_done(&cd); + assert(xco_countdown_fired(&cd)); } static void test_countdown_park_wake(void) { - runtime_t rt; rt_init(&rt); - countdown_t cd; countdown_init(&cd, 2); + xco_runtime_t rt; xco_rt_init(&rt); + xco_countdown_t cd; xco_countdown_init(&cd, 2); - waiter_t w; waiter_init(&w, &rt, countdown_event(&cd)); - xstep(&w.base, 0); - assert(xstep_status(&w.base) == XSTEP_SUSPENDED); + waiter_t w; waiter_init(&w, &rt, xco_countdown_event(&cd)); + xco_step(&w.base, 0); + assert(xco_step_status(&w.base) == XCO_STEP_SUSPENDED); - countdown_done(&cd); + xco_countdown_done(&cd); assert(rt.head == NULL); - countdown_done(&cd); - rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_DEAD); + xco_countdown_done(&cd); + xco_rt_run(&rt, 0); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); assert(w.got == 0); } /* ---- Notify -------------------------------------------------------- */ static void test_notify_try_always_false(void) { - notify_t n; notify_init(&n); - assert(!event_try(notify_event(&n), NULL)); - notify_one(&n); /* empty: no-op */ - notify_all(&n); /* empty: no-op */ - assert(!event_try(notify_event(&n), NULL)); + xco_notify_t n; xco_notify_init(&n); + assert(!xco_event_try(xco_notify_event(&n), NULL)); + xco_notify_one(&n); /* empty: no-op */ + xco_notify_all(&n); /* empty: no-op */ + assert(!xco_event_try(xco_notify_event(&n), NULL)); } static void test_notify_one_fifo(void) { - runtime_t rt; rt_init(&rt); - notify_t n; notify_init(&n); + xco_runtime_t rt; xco_rt_init(&rt); + xco_notify_t n; xco_notify_init(&n); waiter_t a, b, c; - waiter_init(&a, &rt, notify_event(&n)); - waiter_init(&b, &rt, notify_event(&n)); - waiter_init(&c, &rt, notify_event(&n)); - xstep(&a.base, 0); xstep(&b.base, 0); xstep(&c.base, 0); - - notify_one(&n); - rt_run(&rt, 0); - assert(xstep_status(&a.base) == XSTEP_DEAD); - assert(xstep_status(&b.base) == XSTEP_SUSPENDED); - - notify_one(&n); - rt_run(&rt, 0); - assert(xstep_status(&b.base) == XSTEP_DEAD); - assert(xstep_status(&c.base) == XSTEP_SUSPENDED); - - notify_one(&n); - rt_run(&rt, 0); - assert(xstep_status(&c.base) == XSTEP_DEAD); + waiter_init(&a, &rt, xco_notify_event(&n)); + waiter_init(&b, &rt, xco_notify_event(&n)); + waiter_init(&c, &rt, xco_notify_event(&n)); + xco_step(&a.base, 0); xco_step(&b.base, 0); xco_step(&c.base, 0); + + xco_notify_one(&n); + xco_rt_run(&rt, 0); + assert(xco_step_status(&a.base) == XCO_STEP_DEAD); + assert(xco_step_status(&b.base) == XCO_STEP_SUSPENDED); + + xco_notify_one(&n); + xco_rt_run(&rt, 0); + assert(xco_step_status(&b.base) == XCO_STEP_DEAD); + assert(xco_step_status(&c.base) == XCO_STEP_SUSPENDED); + + xco_notify_one(&n); + xco_rt_run(&rt, 0); + assert(xco_step_status(&c.base) == XCO_STEP_DEAD); assert(n.head == NULL); - notify_one(&n); /* empty: no-op */ + xco_notify_one(&n); /* empty: no-op */ assert(rt.head == NULL); } static void test_notify_all(void) { - runtime_t rt; rt_init(&rt); - notify_t n; notify_init(&n); + xco_runtime_t rt; xco_rt_init(&rt); + xco_notify_t n; xco_notify_init(&n); waiter_t a, b, c; - waiter_init(&a, &rt, notify_event(&n)); - waiter_init(&b, &rt, notify_event(&n)); - waiter_init(&c, &rt, notify_event(&n)); - xstep(&a.base, 0); xstep(&b.base, 0); xstep(&c.base, 0); + waiter_init(&a, &rt, xco_notify_event(&n)); + waiter_init(&b, &rt, xco_notify_event(&n)); + waiter_init(&c, &rt, xco_notify_event(&n)); + xco_step(&a.base, 0); xco_step(&b.base, 0); xco_step(&c.base, 0); - notify_all(&n); + xco_notify_all(&n); assert(n.head == NULL); - rt_run(&rt, 0); - assert(xstep_status(&a.base) == XSTEP_DEAD); - assert(xstep_status(&b.base) == XSTEP_DEAD); - assert(xstep_status(&c.base) == XSTEP_DEAD); + xco_rt_run(&rt, 0); + assert(xco_step_status(&a.base) == XCO_STEP_DEAD); + assert(xco_step_status(&b.base) == XCO_STEP_DEAD); + assert(xco_step_status(&c.base) == XCO_STEP_DEAD); } static void test_notify_select(void) { - runtime_t rt; rt_init(&rt); - notify_t n; notify_init(&n); - latch_t l; latch_init(&l); + xco_runtime_t rt; xco_rt_init(&rt); + xco_notify_t n; xco_notify_init(&n); + xco_latch_t l; xco_latch_init(&l); - select_event_t sel; - select_input_t inputs[2]; - event_t *srcs[2] = {notify_event(&n), &l.base}; - select_event_init(&sel, inputs, 2, srcs); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_event_t *srcs[2] = {xco_notify_event(&n), &l.base}; + xco_select_event_init(&sel, inputs, 2, srcs); waiter_t w; waiter_init(&w, &rt, &sel.done.base); - xstep(&w.base, 0); + xco_step(&w.base, 0); - notify_one(&n); - rt_run(&rt, 0); - assert(xstep_status(&w.base) == XSTEP_DEAD); + xco_notify_one(&n); + xco_rt_run(&rt, 0); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); assert(w.got == 0); assert(l.waiters == NULL); - select_event_deinit(&sel); + xco_select_event_deinit(&sel); } static void test_notify_unpark(void) { - runtime_t rt; rt_init(&rt); - notify_t n; notify_init(&n); + xco_runtime_t rt; xco_rt_init(&rt); + xco_notify_t n; xco_notify_init(&n); - step_waker_t s1, s2, s3; - step_waker_init(&s1, &rt, (xstep_t *)0x1); - step_waker_init(&s2, &rt, (xstep_t *)0x2); - step_waker_init(&s3, &rt, (xstep_t *)0x3); - event_park(notify_event(&n), &s1.base); - event_park(notify_event(&n), &s2.base); - event_park(notify_event(&n), &s3.base); + xco_step_waker_t s1, s2, s3; + xco_step_waker_init(&s1, &rt, (xco_step_t *)0x1); + xco_step_waker_init(&s2, &rt, (xco_step_t *)0x2); + xco_step_waker_init(&s3, &rt, (xco_step_t *)0x3); + xco_event_park(xco_notify_event(&n), &s1.base); + xco_event_park(xco_notify_event(&n), &s2.base); + xco_event_park(xco_notify_event(&n), &s3.base); - event_unpark(notify_event(&n), &s2.base); - event_unpark(notify_event(&n), &s2.base); /* idempotent */ + xco_event_unpark(xco_notify_event(&n), &s2.base); + xco_event_unpark(xco_notify_event(&n), &s2.base); /* idempotent */ /* FIFO head is s1; pop s1, then s3 remains. */ - notify_one(&n); + xco_notify_one(&n); assert(n.head == &s3.base); - event_unpark(notify_event(&n), &s3.base); + xco_event_unpark(xco_notify_event(&n), &s3.base); assert(n.head == NULL); } /* ---- Mutex --------------------------------------------------------- */ static void test_mutex_basic(void) { - runtime_t rt; rt_init(&rt); - mutex_t mu; mutex_init(&mu); + xco_runtime_t rt; xco_rt_init(&rt); + xco_mutex_t mu; xco_mutex_init(&mu); - assert(event_try(mutex_event(&mu), NULL)); - assert(!event_try(mutex_event(&mu), NULL)); + assert(xco_event_try(xco_mutex_event(&mu), NULL)); + assert(!xco_event_try(xco_mutex_event(&mu), NULL)); sem_acquirer_t b; sem_acquirer_init(&b, &rt, &mu); - xstep(&b.base, 0); - assert(xstep_status(&b.base) == XSTEP_SUSPENDED); + xco_step(&b.base, 0); + assert(xco_step_status(&b.base) == XCO_STEP_SUSPENDED); - mutex_release(&mu); - rt_run(&rt, 0); - assert(xstep_status(&b.base) == XSTEP_DEAD); + xco_mutex_release(&mu); + xco_rt_run(&rt, 0); + assert(xco_step_status(&b.base) == XCO_STEP_DEAD); assert(b.got); } @@ -2071,201 +2071,201 @@ static void test_mutex_basic(void) { static void test_queue_send_op_inline(void) { /* BLOCK with room: init delivers immediately, payload 1. */ uintptr_t buf[2]; - queue_t q; queue_init(&q, buf, 2, QUEUE_BLOCK); + xco_queue_t q; xco_queue_init(&q, buf, 2, XCO_QUEUE_BLOCK); - queue_send_op_t op; - queue_send_op_init(&op, &q, 0xAA); + xco_queue_send_op_t op; + xco_queue_send_op_init(&op, &q, 0xAA); assert(op.done.set); uintptr_t v; - assert(event_try(&op.done.base, &v)); + assert(xco_event_try(&op.done.base, &v)); assert(v == 1); assert(q.len == 1); - queue_send_op_deinit(&op); + xco_queue_send_op_deinit(&op); } static void test_queue_send_op_blocks(void) { /* BLOCK full: parks. Recv frees a slot, sender drains, op fires. */ uintptr_t buf[1]; - queue_t q; queue_init(&q, buf, 1, QUEUE_BLOCK); - assert(queue_try_send(&q, 1)); + xco_queue_t q; xco_queue_init(&q, buf, 1, XCO_QUEUE_BLOCK); + assert(xco_queue_try_send(&q, 1)); - queue_send_op_t op; - queue_send_op_init(&op, &q, 2); + xco_queue_send_op_t op; + xco_queue_send_op_init(&op, &q, 2); assert(!op.done.set); assert(q.send_head == &op.qsw.sw.base); uintptr_t v; - assert(event_try(queue_recv_event(&q), &v) && v == 1); + assert(xco_event_try(xco_queue_recv_event(&q), &v) && v == 1); assert(op.done.set); uintptr_t pv; - assert(event_try(&op.done.base, &pv)); + assert(xco_event_try(&op.done.base, &pv)); assert(pv == 1); - assert(event_try(queue_recv_event(&q), &v) && v == 2); + assert(xco_event_try(xco_queue_recv_event(&q), &v) && v == 2); - queue_send_op_deinit(&op); + xco_queue_send_op_deinit(&op); } static void test_queue_send_op_drop_inline(void) { /* DROP_NEWEST: op resolves inline regardless of fullness. */ uintptr_t buf[1]; - queue_t q; queue_init(&q, buf, 1, QUEUE_DROP_NEWEST); - assert(queue_try_send(&q, 1)); + xco_queue_t q; xco_queue_init(&q, buf, 1, XCO_QUEUE_DROP_NEWEST); + assert(xco_queue_try_send(&q, 1)); - queue_send_op_t op; - queue_send_op_init(&op, &q, 2); /* dropped */ + xco_queue_send_op_t op; + xco_queue_send_op_init(&op, &q, 2); /* dropped */ assert(op.done.set); uintptr_t v; - assert(event_try(&op.done.base, &v)); + assert(xco_event_try(&op.done.base, &v)); assert(v == 1); - queue_send_op_deinit(&op); + xco_queue_send_op_deinit(&op); } static void test_queue_send_op_select_loses(void) { - runtime_t rt; rt_init(&rt); + xco_runtime_t rt; xco_rt_init(&rt); uintptr_t buf[1]; - queue_t q; queue_init(&q, buf, 1, QUEUE_BLOCK); - latch_t l; latch_init(&l); - assert(queue_try_send(&q, 1)); + xco_queue_t q; xco_queue_init(&q, buf, 1, XCO_QUEUE_BLOCK); + xco_latch_t l; xco_latch_init(&l); + assert(xco_queue_try_send(&q, 1)); - queue_send_op_t op; - queue_send_op_init(&op, &q, 2); + xco_queue_send_op_t op; + xco_queue_send_op_init(&op, &q, 2); assert(q.send_head == &op.qsw.sw.base); - select_event_t sel; - select_input_t inputs[2]; - event_t *srcs[2] = {&op.done.base, &l.base}; - select_event_init(&sel, inputs, 2, srcs); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_event_t *srcs[2] = {&op.done.base, &l.base}; + xco_select_event_init(&sel, inputs, 2, srcs); waiter_t w; waiter_init(&w, &rt, &sel.done.base); - xstep(&w.base, 0); + xco_step(&w.base, 0); - latch_set(&l, 0xCAFE); - rt_run(&rt, 0); + xco_latch_set(&l, 0xCAFE); + xco_rt_run(&rt, 0); assert(w.got == 1); assert(q.send_head == &op.qsw.sw.base); - select_event_deinit(&sel); - queue_send_op_deinit(&op); + xco_select_event_deinit(&sel); + xco_queue_send_op_deinit(&op); assert(q.send_head == NULL); } /* ---- Chan close ---------------------------------------------------- */ typedef struct { - xstep_t base; - chan_t *c; - runtime_t *rt; - step_waker_t sw; + xco_step_t base; + xco_chan_t *c; + xco_runtime_t *rt; + xco_step_waker_t sw; int phase; - recv_status_t status; + xco_recv_status_t status; uintptr_t value; } chan_rx_t; -static xstep_result_t chan_rx_step(xstep_t *s, uintptr_t v) { +static xco_step_result_t chan_rx_step(xco_step_t *s, uintptr_t v) { chan_rx_t *r = (chan_rx_t *)s; (void)v; switch (r->phase) { case 0: { - recv_status_t st = chan_recv(r->c, &r->value); - if (st != RECV_EMPTY) { + xco_recv_status_t st = xco_chan_recv(r->c, &r->value); + if (st != XCO_RECV_EMPTY) { r->status = st; r->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } - step_waker_init(&r->sw, r->rt, &r->base); - event_park(&r->c->recv, &r->sw.base); + xco_step_waker_init(&r->sw, r->rt, &r->base); + xco_event_park(&r->c->recv, &r->sw.base); r->phase = 1; - return (xstep_result_t){0, XSTEP_SUSPENDED}; + return (xco_step_result_t){0, XCO_STEP_SUSPENDED}; } case 1: - r->status = chan_recv(r->c, &r->value); + r->status = xco_chan_recv(r->c, &r->value); r->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } __builtin_unreachable(); } -static void chan_rx_init(chan_rx_t *r, runtime_t *rt, chan_t *c) { - r->base = (xstep_t){.step = chan_rx_step, .status = XSTEP_INIT}; +static void chan_rx_init(chan_rx_t *r, xco_runtime_t *rt, xco_chan_t *c) { + r->base = (xco_step_t){.step = chan_rx_step, .status = XCO_STEP_INIT}; r->c = c; r->rt = rt; r->phase = 0; - r->status = RECV_EMPTY; + r->status = XCO_RECV_EMPTY; r->value = 0; } static void test_chan_close_empty(void) { - chan_t c; chan_init(&c); - assert(!chan_is_closed(&c)); - chan_close(&c); - assert(chan_is_closed(&c)); - chan_close(&c); /* idempotent */ + xco_chan_t c; xco_chan_init(&c); + assert(!xco_chan_is_closed(&c)); + xco_chan_close(&c); + assert(xco_chan_is_closed(&c)); + xco_chan_close(&c); /* idempotent */ uintptr_t v; - assert(chan_recv(&c, &v) == RECV_CLOSED); + assert(xco_chan_recv(&c, &v) == XCO_RECV_CLOSED); } static void test_chan_close_wakes_receiver(void) { - runtime_t rt; rt_init(&rt); - chan_t c; chan_init(&c); + xco_runtime_t rt; xco_rt_init(&rt); + xco_chan_t c; xco_chan_init(&c); chan_rx_t r; chan_rx_init(&r, &rt, &c); - xstep(&r.base, 0); - assert(xstep_status(&r.base) == XSTEP_SUSPENDED); + xco_step(&r.base, 0); + assert(xco_step_status(&r.base) == XCO_STEP_SUSPENDED); assert(c.recv_head == &r.sw.base); - chan_close(&c); + xco_chan_close(&c); assert(c.recv_head == NULL); - rt_run(&rt, 0); - assert(xstep_status(&r.base) == XSTEP_DEAD); - assert(r.status == RECV_CLOSED); + xco_rt_run(&rt, 0); + assert(xco_step_status(&r.base) == XCO_STEP_DEAD); + assert(r.status == XCO_RECV_CLOSED); } typedef struct { - xstep_t base; - chan_t *c; - runtime_t *rt; - chan_send_waker_t csw; + xco_step_t base; + xco_chan_t *c; + xco_runtime_t *rt; + xco_chan_send_waker_t csw; uintptr_t value; int phase; bool done; bool delivered; } chan_tx_t; -static xstep_result_t chan_tx_step(xstep_t *s, uintptr_t v) { +static xco_step_result_t chan_tx_step(xco_step_t *s, uintptr_t v) { chan_tx_t *snd = (chan_tx_t *)s; (void)v; switch (snd->phase) { case 0: - if (chan_try_send(snd->c, snd->value)) { + if (xco_chan_try_send(snd->c, snd->value)) { snd->done = true; snd->delivered = true; snd->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } - if (chan_is_closed(snd->c)) { + if (xco_chan_is_closed(snd->c)) { snd->done = true; snd->delivered = false; snd->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } - chan_send_waker_init(&snd->csw, snd->rt, &snd->base, snd->value); - chan_park_send(snd->c, &snd->csw); + xco_chan_send_waker_init(&snd->csw, snd->rt, &snd->base, snd->value); + xco_chan_park_send(snd->c, &snd->csw); snd->phase = 1; - return (xstep_result_t){0, XSTEP_SUSPENDED}; + return (xco_step_result_t){0, XCO_STEP_SUSPENDED}; case 1: snd->done = true; snd->delivered = snd->csw.delivered; snd->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } __builtin_unreachable(); } -static void chan_tx_init(chan_tx_t *snd, runtime_t *rt, chan_t *c, uintptr_t value) { - snd->base = (xstep_t){.step = chan_tx_step, .status = XSTEP_INIT}; +static void chan_tx_init(chan_tx_t *snd, xco_runtime_t *rt, xco_chan_t *c, uintptr_t value) { + snd->base = (xco_step_t){.step = chan_tx_step, .status = XCO_STEP_INIT}; snd->c = c; snd->rt = rt; snd->value = value; @@ -2275,198 +2275,198 @@ static void chan_tx_init(chan_tx_t *snd, runtime_t *rt, chan_t *c, uintptr_t val } static void test_chan_close_drains_senders(void) { - runtime_t rt; rt_init(&rt); - chan_t c; chan_init(&c); + xco_runtime_t rt; xco_rt_init(&rt); + xco_chan_t c; xco_chan_init(&c); chan_tx_t s[3]; for (int i = 0; i < 3; i++) { chan_tx_init(&s[i], &rt, &c, (uintptr_t)(100 + i)); - xstep(&s[i].base, 0); + xco_step(&s[i].base, 0); } assert(c.send_head == &s[0].csw.sw.base); - chan_close(&c); + xco_chan_close(&c); assert(c.send_head == NULL); - rt_run(&rt, 0); + xco_rt_run(&rt, 0); for (int i = 0; i < 3; i++) { - assert(xstep_status(&s[i].base) == XSTEP_DEAD); + assert(xco_step_status(&s[i].base) == XCO_STEP_DEAD); assert(s[i].done); assert(!s[i].delivered); } } static void test_chan_try_send_after_close(void) { - chan_t c; chan_init(&c); - chan_close(&c); - assert(!chan_try_send(&c, 1)); + xco_chan_t c; xco_chan_init(&c); + xco_chan_close(&c); + assert(!xco_chan_try_send(&c, 1)); } static void test_chan_send_op_close_inline(void) { - chan_t c; chan_init(&c); - chan_close(&c); + xco_chan_t c; xco_chan_init(&c); + xco_chan_close(&c); - chan_send_op_t op; - chan_send_op_init(&op, &c, 0xABBA); + xco_chan_send_op_t op; + xco_chan_send_op_init(&op, &c, 0xABBA); assert(op.done.set); uintptr_t v; - assert(event_try(&op.done.base, &v)); + assert(xco_event_try(&op.done.base, &v)); assert(v == 0); /* delivered=false */ - chan_send_op_deinit(&op); + xco_chan_send_op_deinit(&op); } static void test_chan_send_op_close_drain(void) { - chan_t c; chan_init(&c); + xco_chan_t c; xco_chan_init(&c); - chan_send_op_t op; - chan_send_op_init(&op, &c, 0xABBA); + xco_chan_send_op_t op; + xco_chan_send_op_init(&op, &c, 0xABBA); assert(!op.done.set); - chan_close(&c); + xco_chan_close(&c); assert(op.done.set); uintptr_t v; - assert(event_try(&op.done.base, &v)); + assert(xco_event_try(&op.done.base, &v)); assert(v == 0); - chan_send_op_deinit(&op); + xco_chan_send_op_deinit(&op); } static void test_chan_send_op_delivered_payload(void) { - chan_t c; chan_init(&c); + xco_chan_t c; xco_chan_init(&c); - chan_send_op_t op; - chan_send_op_init(&op, &c, 0xFEED); + xco_chan_send_op_t op; + xco_chan_send_op_init(&op, &c, 0xFEED); assert(!op.done.set); uintptr_t v; - assert(chan_recv(&c, &v) == RECV_GOT); + assert(xco_chan_recv(&c, &v) == XCO_RECV_GOT); assert(v == 0xFEED); assert(op.done.set); uintptr_t pv; - assert(event_try(&op.done.base, &pv)); + assert(xco_event_try(&op.done.base, &pv)); assert(pv == 1); - chan_send_op_deinit(&op); + xco_chan_send_op_deinit(&op); } /* ---- Queue close --------------------------------------------------- */ typedef struct { - xstep_t base; - queue_t *q; - runtime_t *rt; - step_waker_t sw; + xco_step_t base; + xco_queue_t *q; + xco_runtime_t *rt; + xco_step_waker_t sw; int phase; - recv_status_t status; + xco_recv_status_t status; uintptr_t value; } queue_rx_t; -static xstep_result_t queue_rx_step(xstep_t *s, uintptr_t v) { +static xco_step_result_t queue_rx_step(xco_step_t *s, uintptr_t v) { queue_rx_t *r = (queue_rx_t *)s; (void)v; switch (r->phase) { case 0: { - recv_status_t st = queue_recv(r->q, &r->value); - if (st != RECV_EMPTY) { + xco_recv_status_t st = xco_queue_recv(r->q, &r->value); + if (st != XCO_RECV_EMPTY) { r->status = st; r->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } - step_waker_init(&r->sw, r->rt, &r->base); - event_park(queue_recv_event(r->q), &r->sw.base); + xco_step_waker_init(&r->sw, r->rt, &r->base); + xco_event_park(xco_queue_recv_event(r->q), &r->sw.base); r->phase = 1; - return (xstep_result_t){0, XSTEP_SUSPENDED}; + return (xco_step_result_t){0, XCO_STEP_SUSPENDED}; } case 1: - r->status = queue_recv(r->q, &r->value); + r->status = xco_queue_recv(r->q, &r->value); r->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } __builtin_unreachable(); } -static void queue_rx_init(queue_rx_t *r, runtime_t *rt, queue_t *q) { - r->base = (xstep_t){.step = queue_rx_step, .status = XSTEP_INIT}; +static void queue_rx_init(queue_rx_t *r, xco_runtime_t *rt, xco_queue_t *q) { + r->base = (xco_step_t){.step = queue_rx_step, .status = XCO_STEP_INIT}; r->q = q; r->rt = rt; r->phase = 0; - r->status = RECV_EMPTY; + r->status = XCO_RECV_EMPTY; r->value = 0; } static void test_queue_close_drains_buffered_then_eof(void) { uintptr_t buf[3]; - queue_t q; queue_init(&q, buf, 3, QUEUE_BLOCK); - assert(queue_try_send(&q, 1)); - assert(queue_try_send(&q, 2)); + xco_queue_t q; xco_queue_init(&q, buf, 3, XCO_QUEUE_BLOCK); + assert(xco_queue_try_send(&q, 1)); + assert(xco_queue_try_send(&q, 2)); - queue_close(&q); - assert(queue_is_closed(&q)); + xco_queue_close(&q); + assert(xco_queue_is_closed(&q)); uintptr_t v; - assert(queue_recv(&q, &v) == RECV_GOT && v == 1); - assert(queue_recv(&q, &v) == RECV_GOT && v == 2); - assert(queue_recv(&q, &v) == RECV_CLOSED); - queue_close(&q); /* idempotent */ - assert(queue_recv(&q, &v) == RECV_CLOSED); + assert(xco_queue_recv(&q, &v) == XCO_RECV_GOT && v == 1); + assert(xco_queue_recv(&q, &v) == XCO_RECV_GOT && v == 2); + assert(xco_queue_recv(&q, &v) == XCO_RECV_CLOSED); + xco_queue_close(&q); /* idempotent */ + assert(xco_queue_recv(&q, &v) == XCO_RECV_CLOSED); } static void test_queue_close_wakes_receiver(void) { - runtime_t rt; rt_init(&rt); + xco_runtime_t rt; xco_rt_init(&rt); uintptr_t buf[1]; - queue_t q; queue_init(&q, buf, 1, QUEUE_BLOCK); + xco_queue_t q; xco_queue_init(&q, buf, 1, XCO_QUEUE_BLOCK); queue_rx_t r; queue_rx_init(&r, &rt, &q); - xstep(&r.base, 0); - assert(xstep_status(&r.base) == XSTEP_SUSPENDED); + xco_step(&r.base, 0); + assert(xco_step_status(&r.base) == XCO_STEP_SUSPENDED); - queue_close(&q); - rt_run(&rt, 0); - assert(xstep_status(&r.base) == XSTEP_DEAD); - assert(r.status == RECV_CLOSED); + xco_queue_close(&q); + xco_rt_run(&rt, 0); + assert(xco_step_status(&r.base) == XCO_STEP_DEAD); + assert(r.status == XCO_RECV_CLOSED); } typedef struct { - xstep_t base; - queue_t *q; - runtime_t *rt; - queue_send_waker_t qsw; + xco_step_t base; + xco_queue_t *q; + xco_runtime_t *rt; + xco_queue_send_waker_t qsw; uintptr_t value; int phase; bool done; bool delivered; } queue_tx_t; -static xstep_result_t queue_tx_step(xstep_t *s, uintptr_t v) { +static xco_step_result_t queue_tx_step(xco_step_t *s, uintptr_t v) { queue_tx_t *snd = (queue_tx_t *)s; (void)v; switch (snd->phase) { case 0: - if (queue_try_send(snd->q, snd->value)) { + if (xco_queue_try_send(snd->q, snd->value)) { snd->done = true; snd->delivered = true; snd->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } - if (queue_is_closed(snd->q)) { + if (xco_queue_is_closed(snd->q)) { snd->done = true; snd->delivered = false; snd->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } - queue_send_waker_init(&snd->qsw, snd->rt, &snd->base, snd->value); - queue_park_send(snd->q, &snd->qsw); + xco_queue_send_waker_init(&snd->qsw, snd->rt, &snd->base, snd->value); + xco_queue_park_send(snd->q, &snd->qsw); snd->phase = 1; - return (xstep_result_t){0, XSTEP_SUSPENDED}; + return (xco_step_result_t){0, XCO_STEP_SUSPENDED}; case 1: snd->done = true; snd->delivered = snd->qsw.delivered; snd->phase = 2; - return (xstep_result_t){0, XSTEP_DEAD}; + return (xco_step_result_t){0, XCO_STEP_DEAD}; } __builtin_unreachable(); } -static void queue_tx_init(queue_tx_t *s, runtime_t *rt, queue_t *q, uintptr_t v) { - s->base = (xstep_t){.step = queue_tx_step, .status = XSTEP_INIT}; +static void queue_tx_init(queue_tx_t *s, xco_runtime_t *rt, xco_queue_t *q, uintptr_t v) { + s->base = (xco_step_t){.step = queue_tx_step, .status = XCO_STEP_INIT}; s->q = q; s->rt = rt; s->value = v; @@ -2476,180 +2476,180 @@ static void queue_tx_init(queue_tx_t *s, runtime_t *rt, queue_t *q, uintptr_t v) } static void test_queue_close_drains_senders(void) { - runtime_t rt; rt_init(&rt); + xco_runtime_t rt; xco_rt_init(&rt); uintptr_t buf[1]; - queue_t q; queue_init(&q, buf, 1, QUEUE_BLOCK); - assert(queue_try_send(&q, 1)); + xco_queue_t q; xco_queue_init(&q, buf, 1, XCO_QUEUE_BLOCK); + assert(xco_queue_try_send(&q, 1)); queue_tx_t s[2]; for (int i = 0; i < 2; i++) { queue_tx_init(&s[i], &rt, &q, (uintptr_t)(100 + i)); - xstep(&s[i].base, 0); - assert(xstep_status(&s[i].base) == XSTEP_SUSPENDED); + xco_step(&s[i].base, 0); + assert(xco_step_status(&s[i].base) == XCO_STEP_SUSPENDED); } - queue_close(&q); - rt_run(&rt, 0); + xco_queue_close(&q); + xco_rt_run(&rt, 0); for (int i = 0; i < 2; i++) { - assert(xstep_status(&s[i].base) == XSTEP_DEAD); + assert(xco_step_status(&s[i].base) == XCO_STEP_DEAD); assert(!s[i].delivered); } } static void test_queue_try_send_after_close_block(void) { uintptr_t buf[1]; - queue_t q; queue_init(&q, buf, 1, QUEUE_BLOCK); - queue_close(&q); - assert(!queue_try_send(&q, 42)); + xco_queue_t q; xco_queue_init(&q, buf, 1, XCO_QUEUE_BLOCK); + xco_queue_close(&q); + assert(!xco_queue_try_send(&q, 42)); } static void test_queue_try_send_after_close_drop(void) { uintptr_t buf[1]; - queue_t q1; queue_init(&q1, buf, 1, QUEUE_DROP_NEWEST); - queue_close(&q1); - assert(queue_try_send(&q1, 42)); + xco_queue_t q1; xco_queue_init(&q1, buf, 1, XCO_QUEUE_DROP_NEWEST); + xco_queue_close(&q1); + assert(xco_queue_try_send(&q1, 42)); uintptr_t buf2[1]; - queue_t q2; queue_init(&q2, buf2, 1, QUEUE_DROP_OLDEST); - queue_close(&q2); - assert(queue_try_send(&q2, 42)); + xco_queue_t q2; xco_queue_init(&q2, buf2, 1, XCO_QUEUE_DROP_OLDEST); + xco_queue_close(&q2); + assert(xco_queue_try_send(&q2, 42)); } static void test_queue_send_op_close_drain(void) { uintptr_t buf[1]; - queue_t q; queue_init(&q, buf, 1, QUEUE_BLOCK); - assert(queue_try_send(&q, 1)); + xco_queue_t q; xco_queue_init(&q, buf, 1, XCO_QUEUE_BLOCK); + assert(xco_queue_try_send(&q, 1)); - queue_send_op_t op; - queue_send_op_init(&op, &q, 2); + xco_queue_send_op_t op; + xco_queue_send_op_init(&op, &q, 2); assert(!op.done.set); - queue_close(&q); + xco_queue_close(&q); assert(op.done.set); uintptr_t v; - assert(event_try(&op.done.base, &v)); + assert(xco_event_try(&op.done.base, &v)); assert(v == 0); - queue_send_op_deinit(&op); + xco_queue_send_op_deinit(&op); } /* ---- Ticker -------------------------------------------------------- */ typedef struct { - xstep_t base; - ticker_t *t; - runtime_t *rt; - step_waker_t sw; + xco_step_t base; + xco_ticker_t *t; + xco_runtime_t *rt; + xco_step_waker_t sw; int n_seen; int target; uint64_t last; } ticker_sub_t; -static xstep_result_t ticker_sub_step(xstep_t *s, uintptr_t v) { +static xco_step_result_t ticker_sub_step(xco_step_t *s, uintptr_t v) { ticker_sub_t *sub = (ticker_sub_t *)s; if (sub->n_seen > 0) sub->last = (uint64_t)v; - if (sub->n_seen >= sub->target) return (xstep_result_t){v, XSTEP_DEAD}; - event_park(ticker_event(sub->t), &sub->sw.base); + if (sub->n_seen >= sub->target) return (xco_step_result_t){v, XCO_STEP_DEAD}; + xco_event_park(xco_ticker_event(sub->t), &sub->sw.base); sub->n_seen++; - return (xstep_result_t){0, XSTEP_SUSPENDED}; + return (xco_step_result_t){0, XCO_STEP_SUSPENDED}; } -static void ticker_sub_init(ticker_sub_t *s, runtime_t *rt, ticker_t *t, int target) { - s->base = (xstep_t){.step = ticker_sub_step, .status = XSTEP_INIT}; +static void ticker_sub_init(ticker_sub_t *s, xco_runtime_t *rt, xco_ticker_t *t, int target) { + s->base = (xco_step_t){.step = ticker_sub_step, .status = XCO_STEP_INIT}; s->t = t; s->rt = rt; s->n_seen = 0; s->target = target; s->last = 0; - step_waker_init(&s->sw, rt, &s->base); + xco_step_waker_init(&s->sw, rt, &s->base); } static void test_ticker_single_tick(void) { - runtime_t rt; rt_init(&rt); - pairing_heap_t h; pairing_heap_init(&h); - rt_attach_timers(&rt, &h.base); + xco_runtime_t rt; xco_rt_init(&rt); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); + xco_rt_attach_timers(&rt, &h.base); - ticker_t ti; - ticker_init(&ti, &h.base, 100, 100); + xco_ticker_t ti; + xco_ticker_init(&ti, &h.base, 100, 100); ticker_sub_t s; ticker_sub_init(&s, &rt, &ti, 1); - xstep(&s.base, 0); - assert(xstep_status(&s.base) == XSTEP_SUSPENDED); + xco_step(&s.base, 0); + assert(xco_step_status(&s.base) == XCO_STEP_SUSPENDED); - rt_run(&rt, 100); - assert(xstep_status(&s.base) == XSTEP_DEAD); + xco_rt_run(&rt, 100); + assert(xco_step_status(&s.base) == XCO_STEP_DEAD); assert(s.last == 100); - ticker_deinit(&ti); + xco_ticker_deinit(&ti); } static void test_ticker_multiple_ticks(void) { - runtime_t rt; rt_init(&rt); - pairing_heap_t h; pairing_heap_init(&h); - rt_attach_timers(&rt, &h.base); + xco_runtime_t rt; xco_rt_init(&rt); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); + xco_rt_attach_timers(&rt, &h.base); - ticker_t ti; - ticker_init(&ti, &h.base, 50, 50); + xco_ticker_t ti; + xco_ticker_init(&ti, &h.base, 50, 50); ticker_sub_t s; ticker_sub_init(&s, &rt, &ti, 2); - xstep(&s.base, 0); + xco_step(&s.base, 0); - rt_run(&rt, 50); - assert(xstep_status(&s.base) == XSTEP_SUSPENDED); + xco_rt_run(&rt, 50); + assert(xco_step_status(&s.base) == XCO_STEP_SUSPENDED); assert(s.last == 50); - rt_run(&rt, 100); - assert(xstep_status(&s.base) == XSTEP_DEAD); + xco_rt_run(&rt, 100); + assert(xco_step_status(&s.base) == XCO_STEP_DEAD); assert(s.last == 100); /* fired = 50 + period */ - ticker_deinit(&ti); + xco_ticker_deinit(&ti); } static void test_ticker_deinit_before_fire(void) { - runtime_t rt; rt_init(&rt); - pairing_heap_t h; pairing_heap_init(&h); - rt_attach_timers(&rt, &h.base); + xco_runtime_t rt; xco_rt_init(&rt); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); + xco_rt_attach_timers(&rt, &h.base); - ticker_t ti; - ticker_init(&ti, &h.base, 100, 100); + xco_ticker_t ti; + xco_ticker_init(&ti, &h.base, 100, 100); assert(ti.timer.in_heap); - ticker_deinit(&ti); + xco_ticker_deinit(&ti); assert(!ti.timer.in_heap); uint64_t out; - assert(!timers_peek(&h.base, &out)); - rt_run(&rt, 10000); + assert(!xco_timers_peek(&h.base, &out)); + xco_rt_run(&rt, 10000); } static void test_ticker_select(void) { - runtime_t rt; rt_init(&rt); - pairing_heap_t h; pairing_heap_init(&h); - rt_attach_timers(&rt, &h.base); + xco_runtime_t rt; xco_rt_init(&rt); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); + xco_rt_attach_timers(&rt, &h.base); - ticker_t ti; ticker_init(&ti, &h.base, 100, 100); - cancel_t c; cancel_init(&c); + xco_ticker_t ti; xco_ticker_init(&ti, &h.base, 100, 100); + xco_cancel_t c; xco_cancel_init(&c); - select_event_t sel; - select_input_t inputs[2]; - wait_or_cancel(&sel, inputs, ticker_event(&ti), &c); + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_wait_or_cancel(&sel, inputs, xco_ticker_event(&ti), &c); waiter_t w; waiter_init(&w, &rt, &sel.done.base); - xstep(&w.base, 0); + xco_step(&w.base, 0); - rt_run(&rt, 100); - assert(xstep_status(&w.base) == XSTEP_DEAD); - assert(w.got == WAIT_OK); - assert(inputs[WAIT_OK].value == 100); + xco_rt_run(&rt, 100); + assert(xco_step_status(&w.base) == XCO_STEP_DEAD); + assert(w.got == XCO_WAIT_OK); + assert(inputs[XCO_WAIT_OK].value == 100); - select_event_deinit(&sel); - ticker_deinit(&ti); + xco_select_event_deinit(&sel); + xco_ticker_deinit(&ti); } static void test_ticker_try_always_false(void) { - pairing_heap_t h; pairing_heap_init(&h); - ticker_t ti; ticker_init(&ti, &h.base, 100, 100); - assert(!event_try(ticker_event(&ti), NULL)); - ticker_deinit(&ti); + xco_pairing_heap_t h; xco_pairing_heap_init(&h); + xco_ticker_t ti; xco_ticker_init(&ti, &h.base, 100, 100); + assert(!xco_event_try(xco_ticker_event(&ti), NULL)); + xco_ticker_deinit(&ti); } /* ---- Task group ---------------------------------------------------- */ @@ -2657,106 +2657,106 @@ static void test_ticker_try_always_false(void) { static void test_task_group_empty_join(void) { /* An empty (no-attach) group's join is not fired — fan-in semantics * require at least one attach. */ - task_group_t g; task_group_init(&g); - assert(!event_try(task_group_join_event(&g), NULL)); + xco_task_group_t g; xco_task_group_init(&g); + assert(!xco_event_try(xco_task_group_join_event(&g), NULL)); } static void test_task_group_join_after_all_finish(void) { - runtime_t rt; rt_init(&rt); (void)rt; + xco_runtime_t rt; xco_rt_init(&rt); (void)rt; taskbody_t a_body, b_body; - task_t a, b; + xco_task_t a, b; taskbody_init(&a_body, &a, 1, 0xA); - task_init(&a, &a_body.base); + xco_task_init(&a, &a_body.base); taskbody_init(&b_body, &b, 1, 0xB); - task_init(&b, &b_body.base); + xco_task_init(&b, &b_body.base); - task_group_t g; task_group_init(&g); - group_attach_t sa, sb; - task_group_attach(&g, &a, &sa); - task_group_attach(&g, &b, &sb); - assert(!event_try(task_group_join_event(&g), NULL)); + xco_task_group_t g; xco_task_group_init(&g); + xco_group_attach_t sa, sb; + xco_task_group_attach(&g, &a, &sa); + xco_task_group_attach(&g, &b, &sb); + assert(!xco_event_try(xco_task_group_join_event(&g), NULL)); - while (xstep_status(&a_body.base) != XSTEP_DEAD) xstep(&a_body.base, 0); - assert(task_finished(&a)); - assert(!event_try(task_group_join_event(&g), NULL)); + while (xco_step_status(&a_body.base) != XCO_STEP_DEAD) xco_step(&a_body.base, 0); + assert(xco_task_finished(&a)); + assert(!xco_event_try(xco_task_group_join_event(&g), NULL)); - while (xstep_status(&b_body.base) != XSTEP_DEAD) xstep(&b_body.base, 0); - assert(task_finished(&b)); + while (xco_step_status(&b_body.base) != XCO_STEP_DEAD) xco_step(&b_body.base, 0); + assert(xco_task_finished(&b)); uintptr_t v; - assert(event_try(task_group_join_event(&g), &v)); + assert(xco_event_try(xco_task_group_join_event(&g), &v)); assert(v == 0); } static void test_task_group_cancel_fanout(void) { - runtime_t rt; rt_init(&rt); (void)rt; + xco_runtime_t rt; xco_rt_init(&rt); (void)rt; taskbody_t a_body, b_body; - task_t a, b; + xco_task_t a, b; taskbody_init(&a_body, &a, 100, 0xA); - task_init(&a, &a_body.base); + xco_task_init(&a, &a_body.base); taskbody_init(&b_body, &b, 100, 0xB); - task_init(&b, &b_body.base); + xco_task_init(&b, &b_body.base); - task_group_t g; task_group_init(&g); - group_attach_t sa, sb; - task_group_attach(&g, &a, &sa); - task_group_attach(&g, &b, &sb); + xco_task_group_t g; xco_task_group_init(&g); + xco_group_attach_t sa, sb; + xco_task_group_attach(&g, &a, &sa); + xco_task_group_attach(&g, &b, &sb); - task_group_cancel(&g); - assert(task_is_cancelled(&a)); - assert(task_is_cancelled(&b)); - assert(cancel_is_set(task_group_cancel_handle(&g))); + xco_task_group_cancel(&g); + assert(xco_task_is_cancelled(&a)); + assert(xco_task_is_cancelled(&b)); + assert(xco_cancel_is_set(xco_task_group_cancel_handle(&g))); - while (xstep_status(&a_body.base) != XSTEP_DEAD) xstep(&a_body.base, 0); - while (xstep_status(&b_body.base) != XSTEP_DEAD) xstep(&b_body.base, 0); + while (xco_step_status(&a_body.base) != XCO_STEP_DEAD) xco_step(&a_body.base, 0); + while (xco_step_status(&b_body.base) != XCO_STEP_DEAD) xco_step(&b_body.base, 0); - assert(event_try(task_group_join_event(&g), NULL)); + assert(xco_event_try(xco_task_group_join_event(&g), NULL)); } static void test_task_group_finished_detaches(void) { taskbody_t a_body, b_body; - task_t a, b; + xco_task_t a, b; taskbody_init(&a_body, &a, 1, 0xA); - task_init(&a, &a_body.base); + xco_task_init(&a, &a_body.base); taskbody_init(&b_body, &b, 100, 0xB); - task_init(&b, &b_body.base); + xco_task_init(&b, &b_body.base); - task_group_t g; task_group_init(&g); - group_attach_t sa, sb; - task_group_attach(&g, &a, &sa); - task_group_attach(&g, &b, &sb); + xco_task_group_t g; xco_task_group_init(&g); + xco_group_attach_t sa, sb; + xco_task_group_attach(&g, &a, &sa); + xco_task_group_attach(&g, &b, &sb); assert(g.head == &sa && g.tail == &sb); - while (xstep_status(&a_body.base) != XSTEP_DEAD) xstep(&a_body.base, 0); + while (xco_step_status(&a_body.base) != XCO_STEP_DEAD) xco_step(&a_body.base, 0); /* a's bridge has fired → sa is detached. */ assert(g.head == &sb && g.tail == &sb); assert(sa.next == NULL && sa.prev == NULL); /* Cancel only touches still-attached b. */ - task_group_cancel(&g); - assert(task_is_cancelled(&b)); + xco_task_group_cancel(&g); + assert(xco_task_is_cancelled(&b)); } /* ---- Runtime test -------------------------------------------------- */ static void test_runtime_drains(void) { - /* rt_run keeps going until quiescent — including steps queued + /* xco_rt_run keeps going until quiescent — including steps queued * during another step's resumption. Three waiters on one latch - * all reach DEAD in a single rt_run. */ - runtime_t rt; rt_init(&rt); - latch_t l; latch_init(&l); + * all reach DEAD in a single xco_rt_run. */ + xco_runtime_t rt; xco_rt_init(&rt); + xco_latch_t l; xco_latch_init(&l); waiter_t w[3]; for (int i = 0; i < 3; i++) waiter_init(&w[i], &rt, &l.base); - for (int i = 0; i < 3; i++) xstep(&w[i].base, 0); + for (int i = 0; i < 3; i++) xco_step(&w[i].base, 0); - latch_set(&l, 0xC0DE); - rt_run(&rt, 0); + xco_latch_set(&l, 0xC0DE); + xco_rt_run(&rt, 0); assert(rt.head == NULL); for (int i = 0; i < 3; i++) { - assert(xstep_status(&w[i].base) == XSTEP_DEAD); + assert(xco_step_status(&w[i].base) == XCO_STEP_DEAD); assert(w[i].got == 0xC0DE); } } diff --git a/tests/test_xco.c b/tests/test_xco.c @@ -3,7 +3,7 @@ * * Exercises: status transitions, value channel in both directions, * multiple suspends, nested resumes, xco_self. Also drives a hand- - * coded xstep_t state machine through the same generic interface as + * coded xco_step_t state machine through the same generic interface as * a coroutine, to verify the unification. */ @@ -40,43 +40,43 @@ static uintptr_t counter(uintptr_t n) { /* Hand-coded state-machine implementation of the same contract: * first input n, then yield n+1, n+2, n+3, return n+4. No stack - * switch — suspension is just returning XSTEP_SUSPENDED. */ + * switch — suspension is just returning XCO_STEP_SUSPENDED. */ typedef struct { - xstep_t base; + xco_step_t base; int phase; uintptr_t n; } counter_sm_t; -static xstep_result_t counter_sm_step(xstep_t *s, uintptr_t v) { +static xco_step_result_t counter_sm_step(xco_step_t *s, uintptr_t v) { counter_sm_t *p = (counter_sm_t *)s; switch (p->phase++) { - case 0: p->n = v; return (xstep_result_t){v + 1, XSTEP_SUSPENDED}; - case 1: return (xstep_result_t){p->n + 2, XSTEP_SUSPENDED}; - case 2: return (xstep_result_t){p->n + 3, XSTEP_SUSPENDED}; - case 3: return (xstep_result_t){p->n + 4, XSTEP_DEAD}; + case 0: p->n = v; return (xco_step_result_t){v + 1, XCO_STEP_SUSPENDED}; + case 1: return (xco_step_result_t){p->n + 2, XCO_STEP_SUSPENDED}; + case 2: return (xco_step_result_t){p->n + 3, XCO_STEP_SUSPENDED}; + case 3: return (xco_step_result_t){p->n + 4, XCO_STEP_DEAD}; } __builtin_unreachable(); } static void counter_sm_init(counter_sm_t *p) { - p->base = (xstep_t){.step = counter_sm_step, .status = XSTEP_INIT}; + p->base = (xco_step_t){.step = counter_sm_step, .status = XCO_STEP_INIT}; p->phase = 0; p->n = 0; } -/* Generic driver: takes any xstep_t implementing the counter contract +/* Generic driver: takes any xco_step_t implementing the counter contract * and walks it to completion. Knows nothing about coroutines. */ -static void drive_counter(xstep_t *s) { - assert(xstep_status(s) == XSTEP_INIT); - xstep_result_t r = xstep(s, 10); - assert(r.status == XSTEP_SUSPENDED && r.value == 11); - r = xstep(s, 100); - assert(r.status == XSTEP_SUSPENDED && r.value == 12); - r = xstep(s, 200); - assert(r.status == XSTEP_SUSPENDED && r.value == 13); - r = xstep(s, 300); - assert(r.status == XSTEP_DEAD && r.value == 14); - assert(xstep_status(s) == XSTEP_DEAD); +static void drive_counter(xco_step_t *s) { + assert(xco_step_status(s) == XCO_STEP_INIT); + xco_step_result_t r = xco_step(s, 10); + assert(r.status == XCO_STEP_SUSPENDED && r.value == 11); + r = xco_step(s, 100); + assert(r.status == XCO_STEP_SUSPENDED && r.value == 12); + r = xco_step(s, 200); + assert(r.status == XCO_STEP_SUSPENDED && r.value == 13); + r = xco_step(s, 300); + assert(r.status == XCO_STEP_DEAD && r.value == 14); + assert(xco_step_status(s) == XCO_STEP_DEAD); } static xco_t inner; @@ -84,19 +84,19 @@ static xco_t inner; /* Spawns `inner` and pumps it once, demonstrating nested resumes. */ static uintptr_t outer(uintptr_t arg) { (void)arg; - xstep_result_t r = xco_spawn(&inner, counter, stack_b, STACK_BYTES, 10); - assert(r.status == XSTEP_SUSPENDED && r.value == 11); + xco_step_result_t r = xco_spawn(&inner, counter, stack_b, STACK_BYTES, 10); + assert(r.status == XCO_STEP_SUSPENDED && r.value == 11); /* Yield the inner coroutine's first value back to our resumer. */ uintptr_t v = xco_suspend(r.value); assert(v == 42); - /* Drive inner to completion via the generic xstep — same surface + /* Drive inner to completion via the generic xco_step — same surface * used for hand-coded state machines. */ - r = xstep(&inner.base, 100); - assert(r.status == XSTEP_SUSPENDED && r.value == 12); - r = xstep(&inner.base, 200); - assert(r.status == XSTEP_SUSPENDED && r.value == 13); - r = xstep(&inner.base, 300); - assert(r.status == XSTEP_DEAD && r.value == 14); + r = xco_step(&inner.base, 100); + assert(r.status == XCO_STEP_SUSPENDED && r.value == 12); + r = xco_step(&inner.base, 200); + assert(r.status == XCO_STEP_SUSPENDED && r.value == 13); + r = xco_step(&inner.base, 300); + assert(r.status == XCO_STEP_DEAD && r.value == 14); return 999; } @@ -111,7 +111,7 @@ static int trace_len; typedef struct { int id; int n; - runtime_t *rt; + xco_runtime_t *rt; } yielder_args_t; static uintptr_t yielder(uintptr_t arg) { @@ -124,7 +124,7 @@ static uintptr_t yielder(uintptr_t arg) { } static void test_xco_yield_alternates(void) { - runtime_t rt; rt_init(&rt); + xco_runtime_t rt; xco_rt_init(&rt); trace_len = 0; yielder_args_t a1 = {.id = 1, .n = 3, .rt = &rt}; @@ -134,16 +134,16 @@ static void test_xco_yield_alternates(void) { /* Spawn each: first step records the id, then yields back to caller * via the runtime ready queue. After spawn, both are SUSPENDED and * enqueued. */ - xstep_result_t r1 = xco_spawn(&c1, yielder, stack_y1, STACK_BYTES, (uintptr_t)&a1); - assert(r1.status == XSTEP_SUSPENDED); - xstep_result_t r2 = xco_spawn(&c2, yielder, stack_y2, STACK_BYTES, (uintptr_t)&a2); - assert(r2.status == XSTEP_SUSPENDED); + xco_step_result_t r1 = xco_spawn(&c1, yielder, stack_y1, STACK_BYTES, (uintptr_t)&a1); + assert(r1.status == XCO_STEP_SUSPENDED); + xco_step_result_t r2 = xco_spawn(&c2, yielder, stack_y2, STACK_BYTES, (uintptr_t)&a2); + assert(r2.status == XCO_STEP_SUSPENDED); /* Drain the runtime: each yielder runs another step, yields again, * until both reach DEAD. */ - rt_run(&rt, 0); - assert(xstep_status(&c1.base) == XSTEP_DEAD); - assert(xstep_status(&c2.base) == XSTEP_DEAD); + xco_rt_run(&rt, 0); + assert(xco_step_status(&c1.base) == XCO_STEP_DEAD); + assert(xco_step_status(&c2.base) == XCO_STEP_DEAD); /* Trace alternates: 1 2 1 2 1 2 (FIFO ready-queue ordering). */ assert(trace_len == 6); @@ -151,57 +151,57 @@ static void test_xco_yield_alternates(void) { for (int i = 0; i < 6; i++) assert(trace[i] == expect[i]); } -/* ---- xco_task ------------------------------------------------------ */ +/* ---- xco_cotask ------------------------------------------------------ */ /* Body returns immediately with arg + 1. The cleanest possible task — - * verifies trampoline wires task_done with the return value. */ -static uintptr_t task_body_simple(task_t *self, uintptr_t arg) { + * verifies xco_trampoline wires xco_task_done with the return value. */ +static uintptr_t task_body_simple(xco_task_t *self, uintptr_t arg) { (void)self; return arg + 1; } static void test_xco_task_join_inline(void) { - /* Task runs to completion synchronously inside xco_task_spawn; - * task.done is set by the trampoline. */ - xco_task_t xt; - xstep_result_t r = xco_task_spawn(&xt, task_body_simple, + /* Task runs to completion synchronously inside xco_cotask_spawn; + * task.done is set by the xco_trampoline. */ + xco_cotask_t xt; + xco_step_result_t r = xco_cotask_spawn(&xt, task_body_simple, stack_t1, STACK_BYTES, 41); - assert(r.status == XSTEP_DEAD); + assert(r.status == XCO_STEP_DEAD); assert(r.value == 42); - assert(task_finished(&xt.task)); + assert(xco_task_finished(&xt.task)); uintptr_t v; - assert(event_try(task_done_event(&xt.task), &v)); + assert(xco_event_try(xco_task_done_event(&xt.task), &v)); assert(v == 42); } /* Body that suspends on a latch then returns. Uses xco_await — the * try-park-suspend dance compresses to one call. */ -static latch_t task_gate; -static uintptr_t task_body_gated(task_t *self, uintptr_t arg) { +static xco_latch_t task_gate; +static uintptr_t task_body_gated(xco_task_t *self, uintptr_t arg) { (void)self; - runtime_t *rt = (runtime_t *)arg; + xco_runtime_t *rt = (xco_runtime_t *)arg; return xco_await(rt, &task_gate.base) + 100; } static void test_xco_task_join_via_runtime(void) { - /* Task suspends inside body; gate fires, body returns, trampoline - * sets task.done. Verifying via task_done_event read after rt_run. */ - runtime_t rt; rt_init(&rt); - latch_init(&task_gate); + /* Task suspends inside body; gate fires, body returns, xco_trampoline + * sets task.done. Verifying via xco_task_done_event read after xco_rt_run. */ + xco_runtime_t rt; xco_rt_init(&rt); + xco_latch_init(&task_gate); - xco_task_t xt; - xstep_result_t r = xco_task_spawn(&xt, task_body_gated, + xco_cotask_t xt; + xco_step_result_t r = xco_cotask_spawn(&xt, task_body_gated, stack_t2, STACK_BYTES, (uintptr_t)&rt); - assert(r.status == XSTEP_SUSPENDED); - assert(!task_finished(&xt.task)); + assert(r.status == XCO_STEP_SUSPENDED); + assert(!xco_task_finished(&xt.task)); - latch_set(&task_gate, 7); - rt_run(&rt, 0); - assert(task_finished(&xt.task)); + xco_latch_set(&task_gate, 7); + xco_rt_run(&rt, 0); + assert(xco_task_finished(&xt.task)); uintptr_t v; - assert(event_try(task_done_event(&xt.task), &v)); + assert(xco_event_try(xco_task_done_event(&xt.task), &v)); assert(v == 107); } @@ -209,85 +209,85 @@ static void test_xco_task_join_via_runtime(void) { * captures the return value as its own return. The cleanest expression * of "wait for another task to finish, get its result." */ typedef struct { - runtime_t *rt; - xco_task_t *target; + xco_runtime_t *rt; + xco_cotask_t *target; } joiner_args_t; -static uintptr_t task_joiner(task_t *self, uintptr_t arg) { +static uintptr_t task_joiner(xco_task_t *self, uintptr_t arg) { (void)self; joiner_args_t *ja = (joiner_args_t *)arg; - return xco_await(ja->rt, task_done_event(&ja->target->task)); + return xco_await(ja->rt, xco_task_done_event(&ja->target->task)); } static void test_xco_task_joined_by_other_task(void) { - runtime_t rt; rt_init(&rt); - latch_init(&task_gate); + xco_runtime_t rt; xco_rt_init(&rt); + xco_latch_init(&task_gate); - xco_task_t target; - xco_task_spawn(&target, task_body_gated, + xco_cotask_t target; + xco_cotask_spawn(&target, task_body_gated, stack_t2, STACK_BYTES, (uintptr_t)&rt); - assert(!task_finished(&target.task)); + assert(!xco_task_finished(&target.task)); joiner_args_t ja = {.rt = &rt, .target = &target}; - xco_task_t joiner; - xco_task_spawn(&joiner, task_joiner, + xco_cotask_t joiner; + xco_cotask_spawn(&joiner, task_joiner, stack_t4, STACK_BYTES, (uintptr_t)&ja); - assert(!task_finished(&joiner.task)); + assert(!xco_task_finished(&joiner.task)); - latch_set(&task_gate, 7); - rt_run(&rt, 0); - assert(task_finished(&target.task)); - assert(task_finished(&joiner.task)); + xco_latch_set(&task_gate, 7); + xco_rt_run(&rt, 0); + assert(xco_task_finished(&target.task)); + assert(xco_task_finished(&joiner.task)); /* Joiner's return is the target's return propagated through. */ uintptr_t v; - assert(event_try(task_done_event(&joiner.task), &v)); + assert(xco_event_try(xco_task_done_event(&joiner.task), &v)); assert(v == 107); } /* Cancellable body: awaits a never-firing latch under the task's cancel. * Returns 1 on event, 0 on cancel — surfacing the outcome in done. */ -static uintptr_t task_body_cancellable(task_t *self, uintptr_t arg) { - runtime_t *rt = (runtime_t *)arg; - latch_t never; latch_init(&never); +static uintptr_t task_body_cancellable(xco_task_t *self, uintptr_t arg) { + xco_runtime_t *rt = (xco_runtime_t *)arg; + xco_latch_t never; xco_latch_init(&never); uintptr_t v; - return xco_await_or_cancel(rt, &never.base, task_cancel(self), &v) ? 1 : 0; + return xco_await_or_cancel(rt, &never.base, xco_task_cancel(self), &v) ? 1 : 0; } static void test_xco_task_cancel(void) { - runtime_t rt; rt_init(&rt); + xco_runtime_t rt; xco_rt_init(&rt); - xco_task_t xt; - xstep_result_t r = xco_task_spawn(&xt, task_body_cancellable, + xco_cotask_t xt; + xco_step_result_t r = xco_cotask_spawn(&xt, task_body_cancellable, stack_t3, STACK_BYTES, (uintptr_t)&rt); - assert(r.status == XSTEP_SUSPENDED); - assert(!task_finished(&xt.task)); + assert(r.status == XCO_STEP_SUSPENDED); + assert(!xco_task_finished(&xt.task)); - cancel_set(task_cancel(&xt.task)); - rt_run(&rt, 0); - assert(task_finished(&xt.task)); + xco_cancel_set(xco_task_cancel(&xt.task)); + xco_rt_run(&rt, 0); + assert(xco_task_finished(&xt.task)); uintptr_t v; - assert(event_try(task_done_event(&xt.task), &v)); + assert(xco_event_try(xco_task_done_event(&xt.task), &v)); assert(v == 0); /* cancelled */ } -/* Unification at the task layer: drive an xco_task_t through xstep - * directly, bypassing xco_task_spawn. The trampoline still wires - * task_done from the body's return, because that lives inside xco_step, +/* Unification at the task layer: drive an xco_cotask_t through xco_step + * directly, bypassing xco_cotask_spawn. The xco_trampoline still wires + * xco_task_done from the body's return, because that lives inside xco_co_step, * not in any spawn helper. */ static void test_xco_task_via_xstep(void) { - xco_task_t xt; - xco_task_init(&xt, task_body_simple, stack_t1, STACK_BYTES); - assert(xstep_status(&xt.co.base) == XSTEP_INIT); + xco_cotask_t xt; + xco_cotask_init(&xt, task_body_simple, stack_t1, STACK_BYTES); + assert(xco_step_status(&xt.co.base) == XCO_STEP_INIT); - xstep_result_t r = xstep(&xt.co.base, 41); - assert(r.status == XSTEP_DEAD); + xco_step_result_t r = xco_step(&xt.co.base, 41); + assert(r.status == XCO_STEP_DEAD); assert(r.value == 42); - assert(task_finished(&xt.task)); + assert(xco_task_finished(&xt.task)); uintptr_t v; - assert(event_try(task_done_event(&xt.task), &v)); + assert(xco_event_try(xco_task_done_event(&xt.task), &v)); assert(v == 42); } @@ -296,21 +296,21 @@ int main(void) { xco_t c; xco_init(&c, outer, stack_a, STACK_BYTES); - assert(xstep_status(&c.base) == XSTEP_INIT); + assert(xco_step_status(&c.base) == XCO_STEP_INIT); - xstep_result_t r = xstep(&c.base, 0); + xco_step_result_t r = xco_step(&c.base, 0); assert(xco_self() == NULL); - assert(r.status == XSTEP_SUSPENDED); + assert(r.status == XCO_STEP_SUSPENDED); assert(r.value == 11); - assert(xstep_status(&c.base) == XSTEP_SUSPENDED); + assert(xco_step_status(&c.base) == XCO_STEP_SUSPENDED); - r = xstep(&c.base, 42); - assert(r.status == XSTEP_DEAD); + r = xco_step(&c.base, 42); + assert(r.status == XCO_STEP_DEAD); assert(r.value == 999); - assert(xstep_status(&c.base) == XSTEP_DEAD); + assert(xco_step_status(&c.base) == XCO_STEP_DEAD); /* Unification: the same generic driver pumps a coroutine and a - * hand-coded state machine, both reaching XSTEP_DEAD with the + * hand-coded state machine, both reaching XCO_STEP_DEAD with the * expected value sequence. */ xco_t co; xco_init(&co, counter, stack_c, STACK_BYTES); diff --git a/xco.c b/xco.c @@ -13,14 +13,14 @@ * (arch/<name>/xco_arch.c) supplies the register save/restore * primitive (xco_platform_switch) and the initial-context setup * (xco_platform_init). Everything else — the state machine, the - * resume chain, the trampoline wrapping user fn entry/exit, and + * resume chain, the xco_trampoline wrapping user fn entry/exit, and * the xco_self() TLS pointer — lives here. * * This translation unit is architecture-neutral. The platform context * type is forward-declared (in xco_platform.h) and only ever referred * to by pointer, so its actual size and layout never cross into this * file. We reserve raw space for it inside xco_impl_t using - * _XCO_CTX_SIZE / _XCO_CTX_ALIGN from the arch's xco_arch.h, and cast + * XCO__CTX_SIZE / XCO__CTX_ALIGN from the arch's xco_arch.h, and cast * to xco_platform_ctx_t * when calling into the platform layer. */ @@ -35,10 +35,10 @@ * Runtime * ==================================================================== */ -/* rt_init and rt_enqueue are defined inline in xco.h. */ +/* xco_rt_init and xco_rt_enqueue are defined inline in xco.h. */ -static waker_t *rt_dequeue(runtime_t *rt) { - waker_t *w = rt->head; +static xco_waker_t *xco_rt_dequeue(xco_runtime_t *rt) { + xco_waker_t *w = rt->head; if (!w) return NULL; rt->head = w->next; if (!rt->head) rt->tail = NULL; @@ -52,7 +52,7 @@ static waker_t *rt_dequeue(runtime_t *rt) { return w; } -void rt_run(runtime_t *rt, uint64_t now) { +void xco_rt_run(xco_runtime_t *rt, uint64_t now) { /* The runtime ready queue holds only step-wakers. Other waker * shapes (e.g. select_input) fire synchronously inside event * notify paths and never reach here. @@ -65,42 +65,42 @@ void rt_run(runtime_t *rt, uint64_t now) { * queue or exits, and advance only fires timers it then removes, * so total work is bounded. */ for (;;) { - if (rt->timers) timers_advance(rt->timers, now); + if (rt->timers) xco_timers_advance(rt->timers, now); if (!rt->head) return; - for (waker_t *w; (w = rt_dequeue(rt));) { - step_waker_t *sw = (step_waker_t *)w; - xstep(sw->step, sw->resume_value); + for (xco_waker_t *w; (w = xco_rt_dequeue(rt));) { + xco_step_waker_t *sw = (xco_step_waker_t *)w; + xco_step(sw->step, sw->resume_value); } } } /* ---- Step waker ------------------------------------------------------- */ -/* Exposed (with leading underscore) so the inline step_waker_init in +/* Exposed (with leading underscore) so the inline xco_step_waker_init in * xco.h can install it without dragging the body into the header. */ -void _step_waker_fire(waker_t *w, uintptr_t value) { - step_waker_t *sw = (step_waker_t *)w; +void xco__step_waker_fire(xco_waker_t *w, uintptr_t value) { + xco_step_waker_t *sw = (xco_step_waker_t *)w; sw->resume_value = value; - rt_enqueue(sw->rt, w); + xco_rt_enqueue(sw->rt, w); } /* ==================================================================== * Latch * ==================================================================== */ -static bool latch_try(event_t *e, uintptr_t *out) { - latch_t *l = (latch_t *)e; +static bool xco_latch_try(xco_event_t *e, uintptr_t *out) { + xco_latch_t *l = (xco_latch_t *)e; if (!l->set) return false; if (out) *out = l->value; return true; } -static void latch_park(event_t *e, waker_t *w) { - latch_t *l = (latch_t *)e; +static void xco_latch_park(xco_event_t *e, xco_waker_t *w) { + xco_latch_t *l = (xco_latch_t *)e; /* Single-threaded contract: caller must have just observed try=false. */ assert(!l->set); - /* One-waker invariant: w must arrive clean. Detach paths (latch_set - * iterator, latch_unpark, select_event_deinit) all leave wakers in + /* One-waker invariant: w must arrive clean. Detach paths (xco_latch_set + * iterator, xco_latch_unpark, xco_select_event_deinit) all leave wakers in * this state. A trip here means a double-park bug. */ assert(!w->prev && !w->next); w->next = l->waiters; @@ -108,8 +108,8 @@ static void latch_park(event_t *e, waker_t *w) { l->waiters = w; } -static void latch_unpark(event_t *e, waker_t *w) { - latch_t *l = (latch_t *)e; +static void xco_latch_unpark(xco_event_t *e, xco_waker_t *w) { + xco_latch_t *l = (xco_latch_t *)e; /* Detect "not on this list" via the invariant maintained by park * and the detach paths: a parked waker has prev set OR is the * head; a detached one has prev == NULL and is not the head. */ @@ -120,14 +120,14 @@ static void latch_unpark(event_t *e, waker_t *w) { w->prev = w->next = NULL; } -/* Exposed so the inline latch_init in xco.h can reference it. */ -const event_vtable_t _latch_vt = { - .try_ = latch_try, - .park = latch_park, - .unpark = latch_unpark, +/* Exposed so the inline xco_latch_init in xco.h can reference it. */ +const xco_event_vtable_t xco__latch_vt = { + .try_ = xco_latch_try, + .park = xco_latch_park, + .unpark = xco_latch_unpark, }; -void latch_set(latch_t *l, uintptr_t value) { +void xco_latch_set(xco_latch_t *l, uintptr_t value) { if (l->set) return; l->set = true; l->value = value; @@ -135,11 +135,11 @@ void latch_set(latch_t *l, uintptr_t value) { /* Detach the whole waitlist before firing. A waker's fire callback * might do anything (including unpark a sibling on another event), * but it cannot mutate this list — it's already gone. */ - waker_t *w = l->waiters; + xco_waker_t *w = l->waiters; l->waiters = NULL; while (w) { - waker_t *next = w->next; /* save before waker_fire clears */ - waker_fire(w, value); + xco_waker_t *next = w->next; /* save before xco_waker_fire clears */ + xco_waker_fire(w, value); w = next; } } @@ -148,11 +148,11 @@ void latch_set(latch_t *l, uintptr_t value) { * Semaphore * * FIFO doubly-linked waitlist, same shape as the chan_q_* helpers below - * but specialized to a semaphore_t (so we don't have to thread the + * but specialized to a xco_semaphore_t (so we don't have to thread the * head/tail pair through chan_q_*). * ==================================================================== */ -static void sem_q_push(semaphore_t *s, waker_t *w) { +static void xco_sem_q_push(xco_semaphore_t *s, xco_waker_t *w) { assert(!w->prev && !w->next); w->prev = s->tail; w->next = NULL; @@ -161,8 +161,8 @@ static void sem_q_push(semaphore_t *s, waker_t *w) { s->tail = w; } -static waker_t *sem_q_pop(semaphore_t *s) { - waker_t *w = s->head; +static xco_waker_t *xco_sem_q_pop(xco_semaphore_t *s) { + xco_waker_t *w = s->head; if (!w) return NULL; s->head = w->next; if (s->head) s->head->prev = NULL; @@ -171,7 +171,7 @@ static waker_t *sem_q_pop(semaphore_t *s) { return w; } -static void sem_q_remove(semaphore_t *s, waker_t *w) { +static void xco_sem_q_remove(xco_semaphore_t *s, xco_waker_t *w) { if (!w->prev && s->head != w) return; if (w->prev) w->prev->next = w->next; else s->head = w->next; @@ -180,46 +180,46 @@ static void sem_q_remove(semaphore_t *s, waker_t *w) { w->prev = w->next = NULL; } -static bool semaphore_try(event_t *e, uintptr_t *out) { - semaphore_t *s = (semaphore_t *)e; +static bool xco_semaphore_try(xco_event_t *e, uintptr_t *out) { + xco_semaphore_t *s = (xco_semaphore_t *)e; if (s->permits == 0) return false; s->permits--; if (out) *out = 1; return true; } -static void semaphore_park(event_t *e, waker_t *w) { - semaphore_t *s = (semaphore_t *)e; +static void xco_semaphore_park(xco_event_t *e, xco_waker_t *w) { + xco_semaphore_t *s = (xco_semaphore_t *)e; /* Single-threaded contract: caller just observed try=false (permits=0). */ assert(s->permits == 0); - sem_q_push(s, w); + xco_sem_q_push(s, w); } -static void semaphore_unpark(event_t *e, waker_t *w) { - semaphore_t *s = (semaphore_t *)e; - sem_q_remove(s, w); +static void xco_semaphore_unpark(xco_event_t *e, xco_waker_t *w) { + xco_semaphore_t *s = (xco_semaphore_t *)e; + xco_sem_q_remove(s, w); } -const event_vtable_t _semaphore_acquire_vt = { - .try_ = semaphore_try, - .park = semaphore_park, - .unpark = semaphore_unpark, +const xco_event_vtable_t xco__semaphore_acquire_vt = { + .try_ = xco_semaphore_try, + .park = xco_semaphore_park, + .unpark = xco_semaphore_unpark, }; -void semaphore_release(semaphore_t *s, size_t n) { +void xco_semaphore_release(xco_semaphore_t *s, size_t n) { /* Hand a permit directly to each FIFO waiter, then drop any leftover * into the count. Direct handoff prevents a fresh try from jumping * the queue: an arriving acquirer that called try_ would see permits=0 * and park behind the existing waiters until everyone ahead has been * served. */ while (n > 0) { - waker_t *w = sem_q_pop(s); + xco_waker_t *w = xco_sem_q_pop(s); if (!w) break; n--; /* Fire value is conventional 1 — "you got a permit". Step-waker * users ignore the value; select inputs capture it as the input's * value field. */ - waker_fire(w, 1); + xco_waker_fire(w, 1); } s->permits += n; } @@ -234,9 +234,9 @@ void semaphore_release(semaphore_t *s, size_t n) { * disarm-siblings loop is a no-op for already-fired wakers, so it runs * uniformly: for select it cleans up still-parked losers, for allof it * does nothing (every sibling is already detached by its source). */ -static void select_input_fire(waker_t *w, uintptr_t value) { - select_input_t *in = (select_input_t *)w; - select_event_t *s = in->parent; +static void xco_select_input_fire(xco_waker_t *w, uintptr_t value) { + xco_select_input_t *in = (xco_select_input_t *)w; + xco_select_event_t *s = in->parent; /* Defensive: guard against any straggler that escaped disarm. */ if (s->done.set) return; @@ -250,15 +250,15 @@ static void select_input_fire(waker_t *w, uintptr_t value) { /* Disarm anyone still parked so their wakers don't dangle on input * waitlists past s's lifetime. Idempotent on already-detached wakers. */ for (size_t j = 0; j < s->n; j++) { - if (j != i) event_unpark(s->inputs[j].src, &s->inputs[j].w); + if (j != i) xco_event_unpark(s->inputs[j].src, &s->inputs[j].w); } - latch_set(&s->done, i); + xco_latch_set(&s->done, i); } -void select_event_init(select_event_t *s, - select_input_t *inputs, size_t n, - event_t *const *srcs) { - latch_init(&s->done); +void xco_select_event_init(xco_select_event_t *s, + xco_select_input_t *inputs, size_t n, + xco_event_t *const *srcs) { + xco_latch_init(&s->done); s->inputs = inputs; s->n = n; s->remaining = 1; /* any one fire closes the wait */ @@ -267,9 +267,9 @@ void select_event_init(select_event_t *s, * so deinit has nothing to disarm. */ for (size_t i = 0; i < n; i++) { uintptr_t v; - if (event_try(srcs[i], &v)) { + if (xco_event_try(srcs[i], &v)) { inputs[i].value = v; /* captured for inputs[winner].value */ - latch_set(&s->done, i); + xco_latch_set(&s->done, i); return; } } @@ -277,23 +277,23 @@ void select_event_init(select_event_t *s, for (size_t i = 0; i < n; i++) { inputs[i].w.next = NULL; inputs[i].w.prev = NULL; - inputs[i].w.fire = select_input_fire; + inputs[i].w.fire = xco_select_input_fire; inputs[i].src = srcs[i]; inputs[i].parent = s; inputs[i].value = 0; - event_park(srcs[i], &inputs[i].w); + xco_event_park(srcs[i], &inputs[i].w); } } -void allof_event_init(select_event_t *s, - select_input_t *inputs, size_t n, - event_t *const *srcs) { - latch_init(&s->done); +void xco_allof_event_init(xco_select_event_t *s, + xco_select_input_t *inputs, size_t n, + xco_event_t *const *srcs) { + xco_latch_init(&s->done); s->inputs = inputs; s->n = n; s->remaining = n; /* every input must fire to close */ - if (n == 0) { latch_set(&s->done, 0); return; } + if (n == 0) { xco_latch_set(&s->done, 0); return; } /* Initialize each input then try-or-park. An already-ready input is * consumed inline (value captured, remaining--, no parking); the @@ -301,33 +301,33 @@ void allof_event_init(select_event_t *s, for (size_t i = 0; i < n; i++) { inputs[i].w.next = NULL; inputs[i].w.prev = NULL; - inputs[i].w.fire = select_input_fire; + inputs[i].w.fire = xco_select_input_fire; inputs[i].src = srcs[i]; inputs[i].parent = s; inputs[i].value = 0; uintptr_t v; - if (event_try(srcs[i], &v)) { + if (xco_event_try(srcs[i], &v)) { inputs[i].value = v; s->remaining--; } else { - event_park(srcs[i], &inputs[i].w); + xco_event_park(srcs[i], &inputs[i].w); } } /* All inline-ready: fire done with the last input's index, matching * the "closing index" semantics of the parked path. */ - if (s->remaining == 0) latch_set(&s->done, n - 1); + if (s->remaining == 0) xco_latch_set(&s->done, n - 1); } -void select_event_deinit(select_event_t *s) { +void xco_select_event_deinit(xco_select_event_t *s) { /* done.set => the closing fire already disarmed everyone (or the * fast path skipped parking entirely). Otherwise — possible after a * partial allof — some inputs may still be parked; unpark is * idempotent for already-detached wakers. */ if (s->done.set) return; for (size_t i = 0; i < s->n; i++) { - event_unpark(s->inputs[i].src, &s->inputs[i].w); + xco_event_unpark(s->inputs[i].src, &s->inputs[i].w); } } @@ -339,7 +339,7 @@ void select_event_deinit(select_event_t *s) { * is preserved (unlike latch, where waiter order is irrelevant). * ==================================================================== */ -static void chan_q_push(waker_t **head, waker_t **tail, waker_t *w) { +static void xco_chan_q_push(xco_waker_t **head, xco_waker_t **tail, xco_waker_t *w) { assert(!w->prev && !w->next); w->prev = *tail; w->next = NULL; @@ -348,8 +348,8 @@ static void chan_q_push(waker_t **head, waker_t **tail, waker_t *w) { *tail = w; } -static waker_t *chan_q_pop(waker_t **head, waker_t **tail) { - waker_t *w = *head; +static xco_waker_t *xco_chan_q_pop(xco_waker_t **head, xco_waker_t **tail) { + xco_waker_t *w = *head; if (!w) return NULL; *head = w->next; if (*head) (*head)->prev = NULL; @@ -358,8 +358,8 @@ static waker_t *chan_q_pop(waker_t **head, waker_t **tail) { return w; } -static void chan_q_remove(waker_t **head, waker_t **tail, waker_t *w) { - /* Same not-on-list test as latch_unpark: a queued waker has prev +static void xco_chan_q_remove(xco_waker_t **head, xco_waker_t **tail, xco_waker_t *w) { + /* Same not-on-list test as xco_latch_unpark: a queued waker has prev * set OR is the head; a detached one has prev == NULL and isn't * the head. */ if (!w->prev && *head != w) return; @@ -370,27 +370,27 @@ static void chan_q_remove(waker_t **head, waker_t **tail, waker_t *w) { w->prev = w->next = NULL; } -/* Recover the chan_t from its embedded recv event. */ -static inline chan_t *chan_of_recv(event_t *e) { - return (chan_t *)((char *)e - offsetof(chan_t, recv)); +/* Recover the xco_chan_t from its embedded recv event. */ +static inline xco_chan_t *xco_chan_of_recv(xco_event_t *e) { + return (xco_chan_t *)((char *)e - offsetof(xco_chan_t, recv)); } -static bool chan_recv_try(event_t *e, uintptr_t *out) { - chan_t *c = chan_of_recv(e); - waker_t *w = chan_q_pop(&c->send_head, &c->send_tail); +static bool xco_chan_recv_try(xco_event_t *e, uintptr_t *out) { + xco_chan_t *c = xco_chan_of_recv(e); + xco_waker_t *w = xco_chan_q_pop(&c->send_head, &c->send_tail); if (w) { - /* w is &csw->sw.base; sw is the first field of chan_send_waker_t, - * and base is the first field of step_waker_t, so addresses align. */ - chan_send_waker_t *csw = (chan_send_waker_t *)w; + /* w is &csw->sw.base; sw is the first field of xco_chan_send_waker_t, + * and base is the first field of xco_step_waker_t, so addresses align. */ + xco_chan_send_waker_t *csw = (xco_chan_send_waker_t *)w; if (out) *out = csw->value; csw->delivered = true; /* Resume the sender. The fire value is unused for step-wakers; - * for op senders, _chan_send_op_fire reads csw->delivered. */ - waker_fire(w, 0); + * for op senders, xco__chan_send_op_fire reads csw->delivered. */ + xco_waker_fire(w, 0); return true; } /* Close makes the recv event "ready" with no value: the receiver is - * expected to call chan_recv to learn it's RECV_CLOSED. *out is + * expected to call xco_chan_recv to learn it's XCO_RECV_CLOSED. *out is * undefined in that case (set to 0 here for determinism). */ if (c->closed) { if (out) *out = 0; @@ -399,125 +399,125 @@ static bool chan_recv_try(event_t *e, uintptr_t *out) { return false; } -static void chan_recv_park(event_t *e, waker_t *w) { - chan_t *c = chan_of_recv(e); - chan_q_push(&c->recv_head, &c->recv_tail, w); +static void xco_chan_recv_park(xco_event_t *e, xco_waker_t *w) { + xco_chan_t *c = xco_chan_of_recv(e); + xco_chan_q_push(&c->recv_head, &c->recv_tail, w); } -static void chan_recv_unpark(event_t *e, waker_t *w) { - chan_t *c = chan_of_recv(e); - chan_q_remove(&c->recv_head, &c->recv_tail, w); +static void xco_chan_recv_unpark(xco_event_t *e, xco_waker_t *w) { + xco_chan_t *c = xco_chan_of_recv(e); + xco_chan_q_remove(&c->recv_head, &c->recv_tail, w); } -const event_vtable_t _chan_recv_vt = { - .try_ = chan_recv_try, - .park = chan_recv_park, - .unpark = chan_recv_unpark, +const xco_event_vtable_t xco__chan_recv_vt = { + .try_ = xco_chan_recv_try, + .park = xco_chan_recv_park, + .unpark = xco_chan_recv_unpark, }; -bool chan_try_send(chan_t *c, uintptr_t value) { +bool xco_chan_try_send(xco_chan_t *c, uintptr_t value) { if (c->closed) return false; - waker_t *w = chan_q_pop(&c->recv_head, &c->recv_tail); + xco_waker_t *w = xco_chan_q_pop(&c->recv_head, &c->recv_tail); if (!w) return false; /* Hand the value to the recv-side waker. step_waker stashes it as - * resume_value; select_input_fire stashes it in input.value. */ - waker_fire(w, value); + * resume_value; xco_select_input_fire stashes it in input.value. */ + xco_waker_fire(w, value); return true; } -void chan_park_send(chan_t *c, chan_send_waker_t *csw) { - /* park_send after close is UB — caller must check chan_is_closed - * (typically via chan_try_send returning false plus chan_is_closed). */ +void xco_chan_park_send(xco_chan_t *c, xco_chan_send_waker_t *csw) { + /* park_send after close is UB — caller must check xco_chan_is_closed + * (typically via xco_chan_try_send returning false plus xco_chan_is_closed). */ assert(!c->closed); - chan_q_push(&c->send_head, &c->send_tail, &csw->sw.base); + xco_chan_q_push(&c->send_head, &c->send_tail, &csw->sw.base); } -void chan_unpark_send(chan_t *c, chan_send_waker_t *csw) { - chan_q_remove(&c->send_head, &c->send_tail, &csw->sw.base); +void xco_chan_unpark_send(xco_chan_t *c, xco_chan_send_waker_t *csw) { + xco_chan_q_remove(&c->send_head, &c->send_tail, &csw->sw.base); } -recv_status_t chan_recv(chan_t *c, uintptr_t *out) { - waker_t *w = chan_q_pop(&c->send_head, &c->send_tail); +xco_recv_status_t xco_chan_recv(xco_chan_t *c, uintptr_t *out) { + xco_waker_t *w = xco_chan_q_pop(&c->send_head, &c->send_tail); if (w) { - chan_send_waker_t *csw = (chan_send_waker_t *)w; + xco_chan_send_waker_t *csw = (xco_chan_send_waker_t *)w; if (out) *out = csw->value; csw->delivered = true; - waker_fire(w, 0); - return RECV_GOT; + xco_waker_fire(w, 0); + return XCO_RECV_GOT; } - if (c->closed) return RECV_CLOSED; - return RECV_EMPTY; + if (c->closed) return XCO_RECV_CLOSED; + return XCO_RECV_EMPTY; } -void chan_close(chan_t *c) { +void xco_chan_close(xco_chan_t *c) { if (c->closed) return; c->closed = true; - /* Drain parked senders with delivered=false. waker_fire detaches + /* Drain parked senders with delivered=false. xco_waker_fire detaches * before invoking the callback, so re-park inside fire (e.g. to * land on the runtime ready queue) is safe. */ - waker_t *w; - while ((w = chan_q_pop(&c->send_head, &c->send_tail)) != NULL) { - chan_send_waker_t *csw = (chan_send_waker_t *)w; + xco_waker_t *w; + while ((w = xco_chan_q_pop(&c->send_head, &c->send_tail)) != NULL) { + xco_chan_send_waker_t *csw = (xco_chan_send_waker_t *)w; csw->delivered = false; - waker_fire(w, 0); + xco_waker_fire(w, 0); } - /* Wake parked receivers so they observe RECV_CLOSED via chan_recv. - * Fire value is irrelevant — the recv event_try will return true - * because c->closed is set, but receivers should use chan_recv. */ - while ((w = chan_q_pop(&c->recv_head, &c->recv_tail)) != NULL) { - waker_fire(w, 0); + /* Wake parked receivers so they observe XCO_RECV_CLOSED via xco_chan_recv. + * Fire value is irrelevant — the recv xco_event_try will return true + * because c->closed is set, but receivers should use xco_chan_recv. */ + while ((w = xco_chan_q_pop(&c->recv_head, &c->recv_tail)) != NULL) { + xco_waker_fire(w, 0); } } /* ---- Send op (selectable send) ---------------------------------------- */ -void _chan_send_op_fire(waker_t *w, uintptr_t value) { +void xco__chan_send_op_fire(xco_waker_t *w, uintptr_t value) { /* Receiver hands no payload to the sender on delivery — the sender * learns whether its value reached someone via op->csw.delivered, - * set by chan_recv* (true) or chan_close (false). */ + * set by xco_chan_recv* (true) or xco_chan_close (false). */ (void)value; - /* csw is the first field of chan_send_op_t; sw is first of - * chan_send_waker_t; base is first of step_waker_t. All offsets + /* csw is the first field of xco_chan_send_op_t; sw is first of + * xco_chan_send_waker_t; base is first of xco_step_waker_t. All offsets * coincide, so w aliases op. */ - chan_send_op_t *op = (chan_send_op_t *)w; - latch_set(&op->done, op->csw.delivered ? 1 : 0); + xco_chan_send_op_t *op = (xco_chan_send_op_t *)w; + xco_latch_set(&op->done, op->csw.delivered ? 1 : 0); } -void chan_send_op_init(chan_send_op_t *op, chan_t *c, uintptr_t value) { +void xco_chan_send_op_init(xco_chan_send_op_t *op, xco_chan_t *c, uintptr_t value) { /* The embedded chan_send_waker carries the value and provides the * waker layout the chan's send list expects. rt/step are unused — * fire goes straight to the latch, no scheduler hop. */ - chan_send_waker_init(&op->csw, NULL, NULL, value); - op->csw.sw.base.fire = _chan_send_op_fire; + xco_chan_send_waker_init(&op->csw, NULL, NULL, value); + op->csw.sw.base.fire = xco__chan_send_op_fire; op->chan = c; - latch_init(&op->done); + xco_latch_init(&op->done); if (c->closed) { /* Closed channel: no delivery possible, resolve immediately. */ - latch_set(&op->done, 0); + xco_latch_set(&op->done, 0); return; } - if (chan_try_send(c, value)) { + if (xco_chan_try_send(c, value)) { /* Inline delivery: no parking, done set immediately. */ op->csw.delivered = true; - latch_set(&op->done, 1); + xco_latch_set(&op->done, 1); return; } - chan_park_send(c, &op->csw); + xco_chan_park_send(c, &op->csw); } -void chan_send_op_deinit(chan_send_op_t *op) { +void xco_chan_send_op_deinit(xco_chan_send_op_t *op) { /* If delivered, the waker is already off the send list. If not * (e.g., select cancellation), pull it off so it doesn't dangle. */ if (op->done.set) return; - chan_unpark_send(op->chan, &op->csw); + xco_chan_unpark_send(op->chan, &op->csw); } /* ==================================================================== * Queue * - * The FIFO list helpers (chan_q_push/pop/remove) are reused for the + * The FIFO list helpers (xco_chan_q_push/pop/remove) are reused for the * queue's send and recv waitlists — same shape, same invariants. The * ring buffer lives in caller-provided storage; we just track head and * len. cap == 0 leaves the buffer logic dormant: every send either @@ -525,17 +525,17 @@ void chan_send_op_deinit(chan_send_op_t *op) { * or parks — i.e. it degenerates to chan rendezvous. * ==================================================================== */ -static inline queue_t *queue_of_recv(event_t *e) { - return (queue_t *)((char *)e - offsetof(queue_t, recv)); +static inline xco_queue_t *xco_queue_of_recv(xco_event_t *e) { + return (xco_queue_t *)((char *)e - offsetof(xco_queue_t, recv)); } -static inline void queue_push_buf(queue_t *q, uintptr_t v) { +static inline void xco_queue_push_buf(xco_queue_t *q, uintptr_t v) { assert(q->len < q->cap); q->buf[(q->head + q->len) % q->cap] = v; q->len++; } -static inline uintptr_t queue_pop_buf(queue_t *q) { +static inline uintptr_t xco_queue_pop_buf(xco_queue_t *q) { assert(q->len > 0); uintptr_t v = q->buf[q->head]; q->head = (q->head + 1) % q->cap; @@ -548,35 +548,35 @@ static inline uintptr_t queue_pop_buf(queue_t *q) { * from the buffer, or cap > len). Maintains FIFO across the buffer + * sender-waitlist boundary: oldest buffered values come out before any * sender's value (which was queued later). No-op if no sender parked. */ -static void queue_drain_one_sender(queue_t *q) { +static void xco_queue_drain_one_sender(xco_queue_t *q) { if (!q->send_head) return; - waker_t *w = chan_q_pop(&q->send_head, &q->send_tail); - queue_send_waker_t *qsw = (queue_send_waker_t *)w; - queue_push_buf(q, qsw->value); + xco_waker_t *w = xco_chan_q_pop(&q->send_head, &q->send_tail); + xco_queue_send_waker_t *qsw = (xco_queue_send_waker_t *)w; + xco_queue_push_buf(q, qsw->value); qsw->delivered = true; /* Fire after pushing so the sender sees its delivery as complete. */ - waker_fire(w, 0); + xco_waker_fire(w, 0); } -static bool queue_recv_try(event_t *e, uintptr_t *out) { - queue_t *q = queue_of_recv(e); +static bool xco_queue_recv_try(xco_event_t *e, uintptr_t *out) { + xco_queue_t *q = xco_queue_of_recv(e); if (q->len > 0) { - uintptr_t v = queue_pop_buf(q); + uintptr_t v = xco_queue_pop_buf(q); if (out) *out = v; - queue_drain_one_sender(q); + xco_queue_drain_one_sender(q); return true; } /* Empty buffer. If a sender is parked here it can only mean cap==0 * (otherwise the sender would have used the buffer). Hand directly. */ if (q->send_head) { - waker_t *w = chan_q_pop(&q->send_head, &q->send_tail); - queue_send_waker_t *qsw = (queue_send_waker_t *)w; + xco_waker_t *w = xco_chan_q_pop(&q->send_head, &q->send_tail); + xco_queue_send_waker_t *qsw = (xco_queue_send_waker_t *)w; if (out) *out = qsw->value; qsw->delivered = true; - waker_fire(w, 0); + xco_waker_fire(w, 0); return true; } - /* Closed and drained: receivers learn EOF via queue_recv; out is + /* Closed and drained: receivers learn EOF via xco_queue_recv; out is * undefined. */ if (q->closed) { if (out) *out = 0; @@ -585,140 +585,140 @@ static bool queue_recv_try(event_t *e, uintptr_t *out) { return false; } -static void queue_recv_park(event_t *e, waker_t *w) { - queue_t *q = queue_of_recv(e); - chan_q_push(&q->recv_head, &q->recv_tail, w); +static void xco_queue_recv_park(xco_event_t *e, xco_waker_t *w) { + xco_queue_t *q = xco_queue_of_recv(e); + xco_chan_q_push(&q->recv_head, &q->recv_tail, w); } -static void queue_recv_unpark(event_t *e, waker_t *w) { - queue_t *q = queue_of_recv(e); - chan_q_remove(&q->recv_head, &q->recv_tail, w); +static void xco_queue_recv_unpark(xco_event_t *e, xco_waker_t *w) { + xco_queue_t *q = xco_queue_of_recv(e); + xco_chan_q_remove(&q->recv_head, &q->recv_tail, w); } -const event_vtable_t _queue_recv_vt = { - .try_ = queue_recv_try, - .park = queue_recv_park, - .unpark = queue_recv_unpark, +const xco_event_vtable_t xco__queue_recv_vt = { + .try_ = xco_queue_recv_try, + .park = xco_queue_recv_park, + .unpark = xco_queue_recv_unpark, }; -bool queue_try_send(queue_t *q, uintptr_t value) { +bool xco_queue_try_send(xco_queue_t *q, uintptr_t value) { if (q->closed) { /* Send-after-close. BLOCK: no delivery, signal failure. DROP_*: * silently drop (queue policy already says "may be lost"). */ - if (q->policy == QUEUE_BLOCK) return false; + if (q->policy == XCO_QUEUE_BLOCK) return false; return true; } /* Direct handoff first: parked receivers always win over the buffer. * This is the rendezvous case and the cap==0 case. */ - waker_t *w = chan_q_pop(&q->recv_head, &q->recv_tail); + xco_waker_t *w = xco_chan_q_pop(&q->recv_head, &q->recv_tail); if (w) { - waker_fire(w, value); + xco_waker_fire(w, value); return true; } if (q->len < q->cap) { - queue_push_buf(q, value); + xco_queue_push_buf(q, value); return true; } /* Buffer full and no waiting receiver. */ switch (q->policy) { - case QUEUE_BLOCK: + case XCO_QUEUE_BLOCK: return false; - case QUEUE_DROP_NEWEST: + case XCO_QUEUE_DROP_NEWEST: return true; - case QUEUE_DROP_OLDEST: - (void)queue_pop_buf(q); - queue_push_buf(q, value); + case XCO_QUEUE_DROP_OLDEST: + (void)xco_queue_pop_buf(q); + xco_queue_push_buf(q, value); return true; } __builtin_unreachable(); } -void queue_park_send(queue_t *q, queue_send_waker_t *qsw) { +void xco_queue_park_send(xco_queue_t *q, xco_queue_send_waker_t *qsw) { /* DROP_* never parks (try_send always returns true); only valid * for BLOCK. park_send after close is UB. */ - assert(q->policy == QUEUE_BLOCK); + assert(q->policy == XCO_QUEUE_BLOCK); assert(!q->closed); - chan_q_push(&q->send_head, &q->send_tail, &qsw->sw.base); + xco_chan_q_push(&q->send_head, &q->send_tail, &qsw->sw.base); } -void queue_unpark_send(queue_t *q, queue_send_waker_t *qsw) { - chan_q_remove(&q->send_head, &q->send_tail, &qsw->sw.base); +void xco_queue_unpark_send(xco_queue_t *q, xco_queue_send_waker_t *qsw) { + xco_chan_q_remove(&q->send_head, &q->send_tail, &qsw->sw.base); } -recv_status_t queue_recv(queue_t *q, uintptr_t *out) { +xco_recv_status_t xco_queue_recv(xco_queue_t *q, uintptr_t *out) { if (q->len > 0) { - uintptr_t v = queue_pop_buf(q); + uintptr_t v = xco_queue_pop_buf(q); if (out) *out = v; - queue_drain_one_sender(q); - return RECV_GOT; + xco_queue_drain_one_sender(q); + return XCO_RECV_GOT; } if (q->send_head) { - waker_t *w = chan_q_pop(&q->send_head, &q->send_tail); - queue_send_waker_t *qsw = (queue_send_waker_t *)w; + xco_waker_t *w = xco_chan_q_pop(&q->send_head, &q->send_tail); + xco_queue_send_waker_t *qsw = (xco_queue_send_waker_t *)w; if (out) *out = qsw->value; qsw->delivered = true; - waker_fire(w, 0); - return RECV_GOT; + xco_waker_fire(w, 0); + return XCO_RECV_GOT; } - if (q->closed) return RECV_CLOSED; - return RECV_EMPTY; + if (q->closed) return XCO_RECV_CLOSED; + return XCO_RECV_EMPTY; } -void queue_close(queue_t *q) { +void xco_queue_close(xco_queue_t *q) { if (q->closed) return; q->closed = true; /* Drain parked senders with delivered=false. Senders only park * under BLOCK, so this is no-op for DROP_* (their waitlist is * always empty). */ - waker_t *w; - while ((w = chan_q_pop(&q->send_head, &q->send_tail)) != NULL) { - queue_send_waker_t *qsw = (queue_send_waker_t *)w; + xco_waker_t *w; + while ((w = xco_chan_q_pop(&q->send_head, &q->send_tail)) != NULL) { + xco_queue_send_waker_t *qsw = (xco_queue_send_waker_t *)w; qsw->delivered = false; - waker_fire(w, 0); + xco_waker_fire(w, 0); } - /* Wake parked receivers so they can observe closed via queue_recv. - * Receivers may still drain buffered values first — queue_recv's - * RECV_GOT path is hit before the RECV_CLOSED branch. */ - while ((w = chan_q_pop(&q->recv_head, &q->recv_tail)) != NULL) { - waker_fire(w, 0); + /* Wake parked receivers so they can observe closed via xco_queue_recv. + * Receivers may still drain buffered values first — xco_queue_recv's + * XCO_RECV_GOT path is hit before the XCO_RECV_CLOSED branch. */ + while ((w = xco_chan_q_pop(&q->recv_head, &q->recv_tail)) != NULL) { + xco_waker_fire(w, 0); } } /* ---- Queue send op (selectable send) ---------------------------------- */ -void _queue_send_op_fire(waker_t *w, uintptr_t value) { +void xco__queue_send_op_fire(xco_waker_t *w, uintptr_t value) { (void)value; - queue_send_op_t *op = (queue_send_op_t *)w; - latch_set(&op->done, op->qsw.delivered ? 1 : 0); + xco_queue_send_op_t *op = (xco_queue_send_op_t *)w; + xco_latch_set(&op->done, op->qsw.delivered ? 1 : 0); } -void queue_send_op_init(queue_send_op_t *op, queue_t *q, uintptr_t value) { - queue_send_waker_init(&op->qsw, NULL, NULL, value); - op->qsw.sw.base.fire = _queue_send_op_fire; +void xco_queue_send_op_init(xco_queue_send_op_t *op, xco_queue_t *q, uintptr_t value) { + xco_queue_send_waker_init(&op->qsw, NULL, NULL, value); + op->qsw.sw.base.fire = xco__queue_send_op_fire; op->queue = q; - latch_init(&op->done); + xco_latch_init(&op->done); if (q->closed) { /* BLOCK: no delivery; DROP_*: dropped per policy. Either way the * value did not reach a receiver, so delivered=false. */ - latch_set(&op->done, 0); + xco_latch_set(&op->done, 0); return; } - if (queue_try_send(q, value)) { + if (xco_queue_try_send(q, value)) { /* Inline accept: handoff to receiver, buffered, or DROP_* policy * accepted it. */ op->qsw.delivered = true; - latch_set(&op->done, 1); + xco_latch_set(&op->done, 1); return; } /* Only BLOCK policy with full buffer reaches here. */ - queue_park_send(q, &op->qsw); + xco_queue_park_send(q, &op->qsw); } -void queue_send_op_deinit(queue_send_op_t *op) { +void xco_queue_send_op_deinit(xco_queue_send_op_t *op) { if (op->done.set) return; - queue_unpark_send(op->queue, &op->qsw); + xco_queue_unpark_send(op->queue, &op->qsw); } /* ==================================================================== @@ -732,21 +732,21 @@ void queue_send_op_deinit(queue_send_op_t *op) { * publishes — subscribers re-park to receive subsequent values. * ==================================================================== */ -static bool broadcast_try(event_t *e, uintptr_t *out) { +static bool xco_broadcast_try(xco_event_t *e, uintptr_t *out) { (void)e; (void)out; return false; } -static void broadcast_park(event_t *e, waker_t *w) { - broadcast_t *b = (broadcast_t *)e; +static void xco_broadcast_park(xco_event_t *e, xco_waker_t *w) { + xco_broadcast_t *b = (xco_broadcast_t *)e; assert(!w->prev && !w->next); w->next = b->waiters; if (b->waiters) b->waiters->prev = w; b->waiters = w; } -static void broadcast_unpark(event_t *e, waker_t *w) { - broadcast_t *b = (broadcast_t *)e; +static void xco_broadcast_unpark(xco_event_t *e, xco_waker_t *w) { + xco_broadcast_t *b = (xco_broadcast_t *)e; if (!w->prev && b->waiters != w) return; if (w->prev) w->prev->next = w->next; else b->waiters = w->next; @@ -754,26 +754,26 @@ static void broadcast_unpark(event_t *e, waker_t *w) { w->prev = w->next = NULL; } -const event_vtable_t _broadcast_vt = { - .try_ = broadcast_try, - .park = broadcast_park, - .unpark = broadcast_unpark, +const xco_event_vtable_t xco__broadcast_vt = { + .try_ = xco_broadcast_try, + .park = xco_broadcast_park, + .unpark = xco_broadcast_unpark, }; -void broadcast_publish(broadcast_t *b, uintptr_t value) { +void xco_broadcast_publish(xco_broadcast_t *b, uintptr_t value) { b->has_value = true; b->value = value; /* Detach the waitlist before iterating — same hazard-free pattern as - * latch_set. A waker's fire callback may re-park itself on us (the + * xco_latch_set. A waker's fire callback may re-park itself on us (the * common case for a re-arming subscriber); decoupling means that * re-park lands on a fresh waitlist, not on the snapshot we're * walking. */ - waker_t *w = b->waiters; + xco_waker_t *w = b->waiters; b->waiters = NULL; while (w) { - waker_t *next = w->next; /* save before waker_fire clears */ - waker_fire(w, value); + xco_waker_t *next = w->next; /* save before xco_waker_fire clears */ + xco_waker_fire(w, value); w = next; } } @@ -782,47 +782,47 @@ void broadcast_publish(broadcast_t *b, uintptr_t value) { * Notify * * Doubly-linked FIFO waitlist (same shape as the chan/queue waitlists). - * notify_one fires the head; notify_all detaches the whole list before + * xco_notify_one fires the head; xco_notify_all detaches the whole list before * iterating so callbacks can re-park onto a fresh waitlist without - * iterator hazards (same pattern as latch_set). event_try is always + * iterator hazards (same pattern as xco_latch_set). xco_event_try is always * false: notify is purely transient. * ==================================================================== */ -static bool notify_try(event_t *e, uintptr_t *out) { +static bool xco_notify_try(xco_event_t *e, uintptr_t *out) { (void)e; (void)out; return false; } -static void notify_park(event_t *e, waker_t *w) { - notify_t *n = (notify_t *)e; - chan_q_push(&n->head, &n->tail, w); +static void xco_notify_park(xco_event_t *e, xco_waker_t *w) { + xco_notify_t *n = (xco_notify_t *)e; + xco_chan_q_push(&n->head, &n->tail, w); } -static void notify_unpark(event_t *e, waker_t *w) { - notify_t *n = (notify_t *)e; - chan_q_remove(&n->head, &n->tail, w); +static void xco_notify_unpark(xco_event_t *e, xco_waker_t *w) { + xco_notify_t *n = (xco_notify_t *)e; + xco_chan_q_remove(&n->head, &n->tail, w); } -const event_vtable_t _notify_vt = { - .try_ = notify_try, - .park = notify_park, - .unpark = notify_unpark, +const xco_event_vtable_t xco__notify_vt = { + .try_ = xco_notify_try, + .park = xco_notify_park, + .unpark = xco_notify_unpark, }; -void notify_one(notify_t *n) { - waker_t *w = chan_q_pop(&n->head, &n->tail); +void xco_notify_one(xco_notify_t *n) { + xco_waker_t *w = xco_chan_q_pop(&n->head, &n->tail); if (!w) return; - waker_fire(w, 0); + xco_waker_fire(w, 0); } -void notify_all(notify_t *n) { +void xco_notify_all(xco_notify_t *n) { /* Detach before iterating: re-parking inside fire lands on a fresh * (empty) list. Walk the snapshot via saved next pointers. */ - waker_t *w = n->head; + xco_waker_t *w = n->head; n->head = n->tail = NULL; while (w) { - waker_t *next = w->next; - waker_fire(w, 0); + xco_waker_t *next = w->next; + xco_waker_fire(w, 0); w = next; } } @@ -835,21 +835,21 @@ void notify_all(notify_t *n) { * triggers the latch from advance(). No bespoke timer vtable needed. * ==================================================================== */ -void timer_init(timer_t *t, timers_t *ts, uint64_t deadline) { - latch_init(&t->done); +void xco_timer_init(xco_timer_t *t, xco_timers_t *ts, uint64_t deadline) { + xco_latch_init(&t->done); t->deadline = deadline; t->src = ts; t->in_heap = false; t->child = NULL; t->prev = NULL; t->next = NULL; - timers_insert(ts, t); /* sets in_heap = true */ + xco_timers_insert(ts, t); /* sets in_heap = true */ } -void timer_deinit(timer_t *t) { +void xco_timer_deinit(xco_timer_t *t) { /* Idempotent. After fire, in_heap is false (advance popped us); * after explicit cancel, also false. Otherwise we're still queued. */ - if (t->in_heap) timers_cancel(t->src, t); + if (t->in_heap) xco_timers_cancel(t->src, t); } /* ==================================================================== @@ -874,10 +874,10 @@ void timer_deinit(timer_t *t) { /* Merge two detached subtree roots (each with prev=next=NULL). Returns * the merged root (also detached: prev=next=NULL). */ -static timer_t *ph_meld(timer_t *a, timer_t *b) { +static xco_timer_t *xco_ph_meld(xco_timer_t *a, xco_timer_t *b) { if (!a) return b; if (!b) return a; - timer_t *small, *large; + xco_timer_t *small, *large; if (a->deadline <= b->deadline) { small = a; large = b; } else { small = b; large = a; } /* Graft `large` as the new first child of `small`. */ @@ -893,28 +893,28 @@ static timer_t *ph_meld(timer_t *a, timer_t *b) { /* Two-pass pairwise meld of a children sibling list. Detaches each node * before melding so meld inputs satisfy its prev=next=NULL contract. * The output has prev=next=NULL. */ -static timer_t *ph_merge_pairs(timer_t *first) { +static xco_timer_t *xco_ph_merge_pairs(xco_timer_t *first) { /* Pass 1: walk the sibling list left-to-right, melding consecutive * pairs. Chain results via `next` (ab)use as a temporary list link. */ - timer_t *list = NULL; + xco_timer_t *list = NULL; while (first) { - timer_t *a = first; - timer_t *b = a->next; - timer_t *rest = b ? b->next : NULL; + xco_timer_t *a = first; + xco_timer_t *b = a->next; + xco_timer_t *rest = b ? b->next : NULL; a->prev = a->next = NULL; if (b) { b->prev = b->next = NULL; } - timer_t *m = ph_meld(a, b); + xco_timer_t *m = xco_ph_meld(a, b); m->next = list; /* prepend to pass-1 list */ list = m; first = rest; } /* Pass 2: meld the pass-1 list into a single root. */ - timer_t *acc = NULL; + xco_timer_t *acc = NULL; while (list) { - timer_t *nxt = list->next; + xco_timer_t *nxt = list->next; list->prev = NULL; list->next = NULL; - acc = ph_meld(acc, list); + acc = xco_ph_meld(acc, list); list = nxt; } return acc; @@ -924,10 +924,10 @@ static timer_t *ph_merge_pairs(timer_t *first) { * (possibly new) main-heap root. n is left fully detached: child still * points to its subtree, but prev/next are NULL — caller decides what * to do with that subtree. */ -static timer_t *ph_detach(pairing_heap_t *h, timer_t *n) { +static xco_timer_t *xco_ph_detach(xco_pairing_heap_t *h, xco_timer_t *n) { if (h->root == n) { /* n is the main root; pop it and rebuild from its children. */ - timer_t *new_root = ph_merge_pairs(n->child); + xco_timer_t *new_root = xco_ph_merge_pairs(n->child); n->child = NULL; n->prev = NULL; n->next = NULL; @@ -951,54 +951,54 @@ static timer_t *ph_detach(pairing_heap_t *h, timer_t *n) { return h->root; } -static void ph_insert(timers_t *ts, timer_t *t) { - pairing_heap_t *h = (pairing_heap_t *)ts; - /* Singleton tree (prev/next/child already NULL via timer_init). */ - h->root = ph_meld(h->root, t); +static void xco_ph_insert(xco_timers_t *ts, xco_timer_t *t) { + xco_pairing_heap_t *h = (xco_pairing_heap_t *)ts; + /* Singleton tree (prev/next/child already NULL via xco_timer_init). */ + h->root = xco_ph_meld(h->root, t); t->in_heap = true; } -static void ph_cancel(timers_t *ts, timer_t *t) { - pairing_heap_t *h = (pairing_heap_t *)ts; +static void xco_ph_cancel(xco_timers_t *ts, xco_timer_t *t) { + xco_pairing_heap_t *h = (xco_pairing_heap_t *)ts; if (!t->in_heap) return; - h->root = ph_detach(h, t); + h->root = xco_ph_detach(h, t); /* Now meld t's subtree (its children) back into the main heap. */ - timer_t *sub = ph_merge_pairs(t->child); + xco_timer_t *sub = xco_ph_merge_pairs(t->child); t->child = NULL; - h->root = ph_meld(h->root, sub); + h->root = xco_ph_meld(h->root, sub); t->in_heap = false; } -static void ph_advance(timers_t *ts, uint64_t now) { - pairing_heap_t *h = (pairing_heap_t *)ts; +static void xco_ph_advance(xco_timers_t *ts, uint64_t now) { + xco_pairing_heap_t *h = (xco_pairing_heap_t *)ts; /* Pop while the min-key timer is due. Each fire may run callbacks * that insert *new* timers (with later deadlines, normally) — those * land back in the heap, and we keep checking the root. */ while (h->root && h->root->deadline <= now) { - timer_t *t = h->root; - h->root = ph_merge_pairs(t->child); + xco_timer_t *t = h->root; + h->root = xco_ph_merge_pairs(t->child); t->child = NULL; t->prev = NULL; t->next = NULL; t->in_heap = false; /* Trigger the latch: drains the waitlist and delivers the * deadline as the fire payload. */ - latch_set(&t->done, (uintptr_t)t->deadline); + xco_latch_set(&t->done, (uintptr_t)t->deadline); } } -static bool ph_peek(const timers_t *ts, uint64_t *out) { - const pairing_heap_t *h = (const pairing_heap_t *)ts; +static bool xco_ph_peek(const xco_timers_t *ts, uint64_t *out) { + const xco_pairing_heap_t *h = (const xco_pairing_heap_t *)ts; if (!h->root) return false; if (out) *out = h->root->deadline; return true; } -const timers_vtable_t _pairing_heap_vt = { - .insert = ph_insert, - .cancel = ph_cancel, - .advance = ph_advance, - .peek = ph_peek, +const xco_timers_vtable_t xco__pairing_heap_vt = { + .insert = xco_ph_insert, + .cancel = xco_ph_cancel, + .advance = xco_ph_advance, + .peek = xco_ph_peek, }; /* ==================================================================== @@ -1007,30 +1007,30 @@ const timers_vtable_t _pairing_heap_vt = { /* Bridge waker: parked on the timer's latch, fires the cancel when the * timer fires. Two-step indirection so cancel can already have its own - * waiters (e.g. a wait_or_cancel select) without the timer's waitlist + * waiters (e.g. a xco_wait_or_cancel select) without the timer's waitlist * caring about cancel internals. */ -static void _timeout_bridge_fire(waker_t *w, uintptr_t value) { +static void xco__timeout_bridge_fire(xco_waker_t *w, uintptr_t value) { (void)value; - timeout_t *to = (timeout_t *)((char *)w - offsetof(timeout_t, bridge)); - cancel_set(&to->cancel); + xco_timeout_t *to = (xco_timeout_t *)((char *)w - offsetof(xco_timeout_t, bridge)); + xco_cancel_set(&to->cancel); } -void timeout_init(timeout_t *to, timers_t *ts, uint64_t deadline) { - timer_init(&to->timer, ts, deadline); - cancel_init(&to->cancel); +void xco_timeout_init(xco_timeout_t *to, xco_timers_t *ts, uint64_t deadline) { + xco_timer_init(&to->timer, ts, deadline); + xco_cancel_init(&to->cancel); to->bridge.next = NULL; to->bridge.prev = NULL; - to->bridge.fire = _timeout_bridge_fire; - /* Park the bridge on the timer. If the timer fires, latch_set + to->bridge.fire = xco__timeout_bridge_fire; + /* Park the bridge on the timer. If the timer fires, xco_latch_set * detaches the bridge and calls our fire callback inline. */ - event_park(timer_event(&to->timer), &to->bridge); + xco_event_park(xco_timer_event(&to->timer), &to->bridge); } -void timeout_deinit(timeout_t *to) { +void xco_timeout_deinit(xco_timeout_t *to) { /* unpark is idempotent (no-op if the bridge already fired or was - * never parked). timer_deinit is idempotent on the in_heap flag. */ - event_unpark(timer_event(&to->timer), &to->bridge); - timer_deinit(&to->timer); + * never parked). xco_timer_deinit is idempotent on the in_heap flag. */ + xco_event_unpark(xco_timer_event(&to->timer), &to->bridge); + xco_timer_deinit(&to->timer); } /* ==================================================================== @@ -1041,21 +1041,21 @@ void timeout_deinit(timeout_t *to) { * doesn't matter; doubly-linked gives O(1) unpark for cancellation. * ==================================================================== */ -static bool ticker_try(event_t *e, uintptr_t *out) { +static bool xco_ticker_try(xco_event_t *e, uintptr_t *out) { (void)e; (void)out; return false; /* transient — wait for the next tick */ } -static void ticker_park(event_t *e, waker_t *w) { - ticker_t *t = (ticker_t *)((char *)e - offsetof(ticker_t, base)); +static void xco_ticker_park(xco_event_t *e, xco_waker_t *w) { + xco_ticker_t *t = (xco_ticker_t *)((char *)e - offsetof(xco_ticker_t, base)); assert(!w->prev && !w->next); w->next = t->waiters; if (t->waiters) t->waiters->prev = w; t->waiters = w; } -static void ticker_unpark(event_t *e, waker_t *w) { - ticker_t *t = (ticker_t *)((char *)e - offsetof(ticker_t, base)); +static void xco_ticker_unpark(xco_event_t *e, xco_waker_t *w) { + xco_ticker_t *t = (xco_ticker_t *)((char *)e - offsetof(xco_ticker_t, base)); if (!w->prev && t->waiters != w) return; if (w->prev) w->prev->next = w->next; else t->waiters = w->next; @@ -1063,10 +1063,10 @@ static void ticker_unpark(event_t *e, waker_t *w) { w->prev = w->next = NULL; } -const event_vtable_t _ticker_vt = { - .try_ = ticker_try, - .park = ticker_park, - .unpark = ticker_unpark, +const xco_event_vtable_t xco__ticker_vt = { + .try_ = xco_ticker_try, + .park = xco_ticker_park, + .unpark = xco_ticker_unpark, }; /* Bridge waker: parks on the underlying timer's latch. On fire, compute @@ -1074,8 +1074,8 @@ const event_vtable_t _ticker_vt = { * bridge on the new timer, then fire every parked subscriber with the * just-fired deadline. The waitlist is detached before iteration so * subscribers can re-park inside their fire callbacks. */ -static void _ticker_bridge_fire(waker_t *w, uintptr_t value) { - ticker_t *t = (ticker_t *)((char *)w - offsetof(ticker_t, bridge)); +static void xco__ticker_bridge_fire(xco_waker_t *w, uintptr_t value) { + xco_ticker_t *t = (xco_ticker_t *)((char *)w - offsetof(xco_ticker_t, bridge)); uint64_t fired = (uint64_t)value; uint64_t next = fired + t->period; /* Skip-ahead: in the rare overflow case (period = 0 or wraparound), @@ -1084,48 +1084,48 @@ static void _ticker_bridge_fire(waker_t *w, uintptr_t value) { next += ((fired - next) / t->period + 1) * t->period; } /* Reinstall the timer for the next tick. The latch's storage is - * reused — timer_init runs latch_init on it. */ - timer_init(&t->timer, t->src, next); - /* Bridge waker is fully detached (waker_fire just cleared its + * reused — xco_timer_init runs xco_latch_init on it. */ + xco_timer_init(&t->timer, t->src, next); + /* Bridge waker is fully detached (xco_waker_fire just cleared its * links); park it on the freshly-armed timer. */ - event_park(timer_event(&t->timer), &t->bridge); + xco_event_park(xco_timer_event(&t->timer), &t->bridge); /* Fire the subscribers. Detach the waitlist first so re-park inside * fire lands on the now-empty list. */ - waker_t *waiters = t->waiters; + xco_waker_t *waiters = t->waiters; t->waiters = NULL; while (waiters) { - waker_t *nxt = waiters->next; - waker_fire(waiters, (uintptr_t)fired); + xco_waker_t *nxt = waiters->next; + xco_waker_fire(waiters, (uintptr_t)fired); waiters = nxt; } } -void ticker_init(ticker_t *t, timers_t *ts, +void xco_ticker_init(xco_ticker_t *t, xco_timers_t *ts, uint64_t period, uint64_t first_deadline) { /* period must be positive — the skip-ahead computation in the bridge * divides by period, and a zero-period ticker would loop forever - * inside ph_advance. */ + * inside xco_ph_advance. */ assert(period > 0); - t->base.vt = &_ticker_vt; + t->base.vt = &xco__ticker_vt; t->src = ts; t->period = period; t->waiters = NULL; - timer_init(&t->timer, ts, first_deadline); + xco_timer_init(&t->timer, ts, first_deadline); t->bridge.next = NULL; t->bridge.prev = NULL; - t->bridge.fire = _ticker_bridge_fire; - event_park(timer_event(&t->timer), &t->bridge); + t->bridge.fire = xco__ticker_bridge_fire; + xco_event_park(xco_timer_event(&t->timer), &t->bridge); } -void ticker_deinit(ticker_t *t) { +void xco_ticker_deinit(xco_ticker_t *t) { /* Pull the bridge off the timer (no-op if already fired) and cancel * the timer. Subscribers' wakers are the caller's storage; nothing * to free here. */ - event_unpark(timer_event(&t->timer), &t->bridge); - timer_deinit(&t->timer); + xco_event_unpark(xco_timer_event(&t->timer), &t->bridge); + xco_timer_deinit(&t->timer); } /* ==================================================================== @@ -1138,11 +1138,11 @@ void ticker_deinit(ticker_t *t) { * * Cancellation is fan-out: walk the list, set each task's cancel, then * set the group-level cancel. Bodies cooperate by composing their work - * with task_cancel(self); the group-level cancel is for non-task + * with xco_task_cancel(self); the group-level cancel is for non-task * waiters that want to react to "the group has been told to stop." * ==================================================================== */ -static void _task_group_detach_slot(task_group_t *g, group_attach_t *slot) { +static void xco__task_group_detach_slot(xco_task_group_t *g, xco_group_attach_t *slot) { /* Doubly-linked, head/tail tracked; same shape as other waitlists. */ if (slot->prev) slot->prev->next = slot->next; else g->head = slot->next; @@ -1151,30 +1151,30 @@ static void _task_group_detach_slot(task_group_t *g, group_attach_t *slot) { slot->prev = slot->next = NULL; } -static void _task_group_bridge_fire(waker_t *w, uintptr_t value) { +static void xco__task_group_bridge_fire(xco_waker_t *w, uintptr_t value) { (void)value; - group_attach_t *slot = (group_attach_t *)((char *)w - offsetof(group_attach_t, bridge)); - task_group_t *g = slot->group; - _task_group_detach_slot(g, slot); - countdown_done(&g->pending); + xco_group_attach_t *slot = (xco_group_attach_t *)((char *)w - offsetof(xco_group_attach_t, bridge)); + xco_task_group_t *g = slot->group; + xco__task_group_detach_slot(g, slot); + xco_countdown_done(&g->pending); } -void task_group_init(task_group_t *g) { - /* Don't go through countdown_init(0) — that fires the latch +void xco_task_group_init(xco_task_group_t *g) { + /* Don't go through xco_countdown_init(0) — that fires the latch * immediately, which would make the very first attach's - * countdown_add UB. The group's join must remain not-fired until at + * xco_countdown_add UB. The group's join must remain not-fired until at * least one attached task has finished, so we open with * remaining=0 and an unset latch. The first attach lifts remaining * to 1, and matching countdown_dones bring it back to 0, firing * the latch. */ - latch_init(&g->pending.done); + xco_latch_init(&g->pending.done); g->pending.remaining = 0; - cancel_init(&g->cancel); + xco_cancel_init(&g->cancel); g->head = g->tail = NULL; } -void task_group_attach(task_group_t *g, task_t *t, group_attach_t *slot) { - countdown_add(&g->pending, 1); +void xco_task_group_attach(xco_task_group_t *g, xco_task_t *t, xco_group_attach_t *slot) { + xco_countdown_add(&g->pending, 1); slot->task = t; slot->group = g; @@ -1190,24 +1190,24 @@ void task_group_attach(task_group_t *g, task_t *t, group_attach_t *slot) { slot->bridge.next = NULL; slot->bridge.prev = NULL; - slot->bridge.fire = _task_group_bridge_fire; + slot->bridge.fire = xco__task_group_bridge_fire; /* Park on the task's done event. If the task has already finished * (re-attaching is UB per the contract, but if the task fired * before attach finished initializing — e.g. an inline-spawned - * synchronous task), the latch_park assert would catch it. */ - event_park(task_done_event(t), &slot->bridge); + * synchronous task), the xco_latch_park assert would catch it. */ + xco_event_park(xco_task_done_event(t), &slot->bridge); } -void task_group_cancel(task_group_t *g) { +void xco_task_group_cancel(xco_task_group_t *g) { /* Fan-out cancel: signal each attached task. Walk the snapshot * (cancel doesn't detach the slot — only task done does — so the * list is stable across iteration). */ - for (group_attach_t *s = g->head; s; s = s->next) { - cancel_set(&s->task->cancel); + for (xco_group_attach_t *s = g->head; s; s = s->next) { + xco_cancel_set(&s->task->cancel); } /* Group-level cancel for anyone awaiting "the group as a whole." */ - cancel_set(&g->cancel); + xco_cancel_set(&g->cancel); } /* ==================================================================== @@ -1215,8 +1215,8 @@ void task_group_cancel(task_group_t *g) { * ==================================================================== */ typedef struct xco_impl { - xstep_t base; /* must be first; aliases xco_t.base */ - _Alignas(_XCO_CTX_ALIGN) unsigned char ctx_buf[_XCO_CTX_SIZE]; + xco_step_t base; /* must be first; aliases xco_t.base */ + _Alignas(XCO__CTX_ALIGN) unsigned char ctx_buf[XCO__CTX_SIZE]; xco_platform_ctx_t *resumer_ctx; /* where suspend/return goes */ xco_fn fn; } xco_impl_t; @@ -1224,94 +1224,94 @@ typedef struct xco_impl { _Static_assert(sizeof(xco_impl_t) <= sizeof(xco_t), "xco_t too small"); _Static_assert(_Alignof(xco_impl_t) <= _Alignof(xco_t), "xco_t under-aligned"); -static inline xco_impl_t *impl(xco_t *c) { return (xco_impl_t *)c; } -static inline xco_platform_ctx_t *ctx_of(xco_impl_t *ci) { +static inline xco_impl_t *xco_impl_of(xco_t *c) { return (xco_impl_t *)c; } +static inline xco_platform_ctx_t *xco_ctx_of(xco_impl_t *ci) { return (xco_platform_ctx_t *)ci->ctx_buf; } /* Per-thread state. */ -static _Thread_local xco_impl_t *t_current = NULL; -static _Thread_local _Alignas(_XCO_CTX_ALIGN) - unsigned char t_main_ctx_buf[_XCO_CTX_SIZE]; -static inline xco_platform_ctx_t *main_ctx(void) { - return (xco_platform_ctx_t *)t_main_ctx_buf; +static _Thread_local xco_impl_t *xco_t_current = NULL; +static _Thread_local _Alignas(XCO__CTX_ALIGN) + unsigned char xco_t_main_ctx_buf[XCO__CTX_SIZE]; +static inline xco_platform_ctx_t *xco_main_ctx(void) { + return (xco_platform_ctx_t *)xco_t_main_ctx_buf; } /* Trampoline: runs on the coroutine's own stack, invoked by the * platform layer on the first switch into a fresh context. The - * argument is the value passed to that first xstep. The coroutine - * identifies itself via t_current, set by the resumer just before + * argument is the value passed to that first xco_step. The coroutine + * identifies itself via xco_t_current, set by the resumer just before * switching. */ -static void trampoline(uintptr_t arg) { - xco_impl_t *self = t_current; +static void xco_trampoline(uintptr_t arg) { + xco_impl_t *self = xco_t_current; uintptr_t ret = self->fn(arg); - self->base.status = XSTEP_DEAD; - (void)xco_platform_switch(ctx_of(self), self->resumer_ctx, ret); + self->base.status = XCO_STEP_DEAD; + (void)xco_platform_switch(xco_ctx_of(self), self->resumer_ctx, ret); __builtin_unreachable(); } -/* xstep_fn entry point wired into base.step at init time. All callers - * — generic xstep consumers and xco-aware code alike — route through +/* xco_step_fn entry point wired into base.step at init time. All callers + * — generic xco_step consumers and xco-aware code alike — route through * here. */ -static xstep_result_t xco_step(xstep_t *s, uintptr_t value) { +static xco_step_result_t xco_co_step(xco_step_t *s, uintptr_t value) { xco_impl_t *next = (xco_impl_t *)s; - assert(next->base.status == XSTEP_INIT || next->base.status == XSTEP_SUSPENDED); + assert(next->base.status == XCO_STEP_INIT || next->base.status == XCO_STEP_SUSPENDED); - xco_impl_t *prev = t_current; - next->resumer_ctx = prev ? ctx_of(prev) : main_ctx(); - next->base.status = XSTEP_RUNNING; - t_current = next; + xco_impl_t *prev = xco_t_current; + next->resumer_ctx = prev ? xco_ctx_of(prev) : xco_main_ctx(); + next->base.status = XCO_STEP_RUNNING; + xco_t_current = next; uintptr_t back = xco_platform_switch(next->resumer_ctx, - ctx_of(next), value); + xco_ctx_of(next), value); /* Coroutine has either suspended or returned; status is already - * set correctly by xco_suspend or by the trampoline. */ - t_current = prev; - return (xstep_result_t){ .value = back, .status = next->base.status }; + * set correctly by xco_suspend or by the xco_trampoline. */ + xco_t_current = prev; + return (xco_step_result_t){ .value = back, .status = next->base.status }; } void xco_init(xco_t *c, xco_fn fn, void *stack_base, size_t stack_len) { - xco_impl_t *ci = impl(c); - ci->base.step = xco_step; - ci->base.status = XSTEP_INIT; + xco_impl_t *ci = xco_impl_of(c); + ci->base.step = xco_co_step; + ci->base.status = XCO_STEP_INIT; ci->fn = fn; ci->resumer_ctx = NULL; - xco_platform_init(ctx_of(ci), stack_base, stack_len, trampoline); + xco_platform_init(xco_ctx_of(ci), stack_base, stack_len, xco_trampoline); } uintptr_t xco_suspend(uintptr_t value) { - xco_impl_t *self = t_current; + xco_impl_t *self = xco_t_current; assert(self != NULL); - self->base.status = XSTEP_SUSPENDED; - return xco_platform_switch(ctx_of(self), self->resumer_ctx, value); + self->base.status = XCO_STEP_SUSPENDED; + return xco_platform_switch(xco_ctx_of(self), self->resumer_ctx, value); } xco_t *xco_self(void) { - return (xco_t *)t_current; + return (xco_t *)xco_t_current; } /* ---- xco-backed task -------------------------------------------------- */ -/* The trampoline: runs as the coroutine's xco_fn. Recovers the owning - * xco_task_t via container_of on the embedded co (xco_self() returns +/* The xco_trampoline: runs as the coroutine's xco_fn. Recovers the owning + * xco_cotask_t via container_of on the embedded co (xco_self() returns * the running xco), dispatches the user fn, and surfaces the return - * value through task_done — so a joiner waiting on task_done_event + * value through xco_task_done — so a joiner waiting on xco_task_done_event * wakes with the body's return without the body needing to know about * the task surface at all. */ -static uintptr_t xco_task_trampoline(uintptr_t arg) { +static uintptr_t xco_cotask_trampoline(uintptr_t arg) { xco_t *self = xco_self(); - xco_task_t *xt = (xco_task_t *)((char *)self - offsetof(xco_task_t, co)); + xco_cotask_t *xt = (xco_cotask_t *)((char *)self - offsetof(xco_cotask_t, co)); uintptr_t r = xt->fn(&xt->task, arg); - task_done(&xt->task, r); + xco_task_done(&xt->task, r); return r; } -void xco_task_init(xco_task_t *xt, xco_task_fn fn, +void xco_cotask_init(xco_cotask_t *xt, xco_cotask_fn fn, void *stack_base, size_t stack_len) { - task_init(&xt->task, &xt->co.base); + xco_task_init(&xt->task, &xt->co.base); xt->fn = fn; - xco_init(&xt->co, xco_task_trampoline, stack_base, stack_len); + xco_init(&xt->co, xco_cotask_trampoline, stack_base, stack_len); } diff --git a/xco.h b/xco.h @@ -3,7 +3,7 @@ * * Four layers in this header, bottom-up: * - * xstep_t Generic resumable function. A value that can be + * xco_step_t Generic resumable function. A value that can be * driven forward one step at a time; each step takes a * uintptr_t in, returns one out, and reports whether * the function suspended or finished. Substrate shared @@ -19,10 +19,10 @@ * task_group. All storage caller-provided, no allocation, * no atomics. * - * xco_t Stack-switching coroutine. xco_t embeds xstep_t as + * xco_t Stack-switching coroutine. xco_t embeds xco_step_t as * its first member, so a coroutine is one concrete * kind of resumable function: generic code holding an - * xstep_t * works on coroutines and hand-coded state + * xco_step_t * works on coroutines and hand-coded state * machines uniformly. Values pass between caller and * coroutine through a single uintptr_t channel; pack * richer data behind a pointer. @@ -31,20 +31,20 @@ * Thread-affine: a coroutine must be resumed on the * thread that initialized it. * - * xco_task_t xco specialization of task_t. The trampoline calls - * fn(&xt->task, arg) and then task_done with the - * return value, so wait_or_cancel-style teardown works + * xco_cotask_t xco specialization of xco_task_t. The xco_trampoline calls + * fn(&xt->task, arg) and then xco_task_done with the + * return value, so xco_wait_or_cancel-style teardown works * without the user wiring anything. * * Single-threaded only — matches xco's thread affinity. The contract * "try first, park only if try failed" is race-free because nothing * else runs between the two calls. * - * One-waker invariant. An xstep, while suspended, is parked on at most + * One-waker invariant. An xco_step, while suspended, is parked on at most * one event. Multi-wait is composed in the event graph: build a * select_event (or any future combinator — all-of, timeout, ...) and - * park on that. The xstep never sees more than one event directly. - * This is what lets a single step_waker_t live inline in the xstep + * park on that. The xco_step never sees more than one event directly. + * This is what lets a single xco_step_waker_t live inline in the xco_step * and a single next/prev pair serve both event waitlists and the * runtime ready queue (the two list memberships are disjoint in time). */ @@ -56,66 +56,66 @@ #include <stddef.h> #include <stdint.h> -/* Provides XCO_SIZE, XCO_ALIGN, XCO_STACK_ALIGN, _XCO_CTX_SIZE, - * _XCO_CTX_ALIGN; resolved by the build to the platform-specific +/* Provides XCO_SIZE, XCO_ALIGN, XCO_STACK_ALIGN, XCO__CTX_SIZE, + * XCO__CTX_ALIGN; resolved by the build to the platform-specific * copy via the include path (-Iplatform/$(PLATFORM)). */ #include "xco_platform.h" /* ==================================================================== - * xstep — generic resumable function interface. + * xco_step — generic resumable function interface. * - * The first-member convention: embed xstep_t as the first field of + * The first-member convention: embed xco_step_t as the first field of * your concrete type so a pointer to your type can be passed wherever - * an xstep_t * is expected. + * an xco_step_t * is expected. * * typedef struct { - * xstep_t base; + * xco_step_t base; * int phase; * ... * } parser_t; * - * static xstep_result_t parser_step(xstep_t *s, uintptr_t v) { + * static xco_step_result_t parser_step(xco_step_t *s, uintptr_t v) { * parser_t *p = (parser_t *)s; * switch (p->phase) { - * case 0: p->phase = 1; return (xstep_result_t){v + 1, XSTEP_SUSPENDED}; - * case 1: return (xstep_result_t){v * 2, XSTEP_DEAD}; + * case 0: p->phase = 1; return (xco_step_result_t){v + 1, XCO_STEP_SUSPENDED}; + * case 1: return (xco_step_result_t){v * 2, XCO_STEP_DEAD}; * } * __builtin_unreachable(); * } * - * parser_t p = { .base = {.step = parser_step, .status = XSTEP_INIT} }; - * xstep_result_t r = xstep(&p.base, 0); + * parser_t p = { .base = {.step = parser_step, .status = XCO_STEP_INIT} }; + * xco_step_result_t r = xco_step(&p.base, 0); * ==================================================================== */ typedef enum { - XSTEP_INIT, /* created, never stepped */ - XSTEP_RUNNING, /* inside step(), or in an active resume chain */ - XSTEP_SUSPENDED, /* yielded; resumable */ - XSTEP_DEAD, /* function returned */ -} xstep_status_t; + XCO_STEP_INIT, /* created, never stepped */ + XCO_STEP_RUNNING, /* inside step(), or in an active resume chain */ + XCO_STEP_SUSPENDED, /* yielded; resumable */ + XCO_STEP_DEAD, /* function returned */ +} xco_step_status_t; typedef struct { uintptr_t value; - xstep_status_t status; -} xstep_result_t; + xco_step_status_t status; +} xco_step_result_t; -typedef struct xstep xstep_t; -typedef xstep_result_t (*xstep_fn)(xstep_t *s, uintptr_t value); +typedef struct xco_step xco_step_t; +typedef xco_step_result_t (*xco_step_fn)(xco_step_t *s, uintptr_t value); -struct xstep { - xstep_fn step; - xstep_status_t status; /* cached; xstep() syncs from each result */ +struct xco_step { + xco_step_fn step; + xco_step_status_t status; /* cached; xco_step() syncs from each result */ }; /* Drive one step. The wrapper updates s->status from the returned * result so step implementations only need to populate the result. */ -static inline xstep_result_t xstep(xstep_t *s, uintptr_t value) { - xstep_result_t r = s->step(s, value); +static inline xco_step_result_t xco_step(xco_step_t *s, uintptr_t value) { + xco_step_result_t r = s->step(s, value); s->status = r.status; return r; } -static inline xstep_status_t xstep_status(const xstep_t *s) { +static inline xco_step_status_t xco_step_status(const xco_step_t *s) { return s->status; } @@ -125,40 +125,40 @@ static inline xstep_status_t xstep_status(const xstep_t *s) { * Standard usage from a state machine: * * uintptr_t v; - * if (event_try(e, &v)) { ... use v ... } - * else { event_park(e, &my_waker.base); return SUSPENDED; } + * if (xco_event_try(e, &v)) { ... use v ... } + * else { xco_event_park(e, &my_waker.base); return SUSPENDED; } * * Standard usage from an xco coroutine wrapper: * * uintptr_t v; - * if (!event_try(e, &v)) { - * step_waker_t sw; - * step_waker_init(&sw, rt, &xco_self()->base); - * event_park(e, &sw.base); + * if (!xco_event_try(e, &v)) { + * xco_step_waker_t sw; + * xco_step_waker_init(&sw, rt, &xco_self()->base); + * xco_event_park(e, &sw.base); * xco_suspend(0); - * (void)event_try(e, &v); // now ready + * (void)xco_event_try(e, &v); // now ready * } * ==================================================================== */ /* ---- Waker ------------------------------------------------------------ */ -typedef struct waker waker_t; -struct waker { +typedef struct xco_waker xco_waker_t; +struct xco_waker { /* Doubly-linked while parked on an event waitlist, so unpark is * O(1). Reused as the singly-linked next pointer while on the * runtime ready queue (FIFO, no removal from middle); prev is * undefined in that state and reset on the next park. */ - waker_t *next; - waker_t *prev; + xco_waker_t *next; + xco_waker_t *prev; /* Fire callback. value is the event's payload at fire time — sticky * events also store it on themselves, transient events (channels, * one-shot signals) deliver only here. Wakers that don't care * about the value just ignore the parameter. * - * Invoke via waker_fire (below), not directly: the helper enforces + * Invoke via xco_waker_fire (below), not directly: the helper enforces * the "fire receives a fully detached waker" contract that makes it * safe to re-park inside the callback. */ - void (*fire)(waker_t *w, uintptr_t value); + void (*fire)(xco_waker_t *w, uintptr_t value); }; /* Canonical way to invoke a waker's fire callback. Hands the callback a @@ -166,7 +166,7 @@ struct waker { * does) can re-park on a fresh waitlist without colliding with stale * link state. Detachers that lead into fire (queue pops, latch drains, * etc.) don't need to clear prev/next themselves. */ -static inline void waker_fire(waker_t *w, uintptr_t value) { +static inline void xco_waker_fire(xco_waker_t *w, uintptr_t value) { w->prev = NULL; w->next = NULL; w->fire(w, value); @@ -174,91 +174,91 @@ static inline void waker_fire(waker_t *w, uintptr_t value) { /* ---- Event ------------------------------------------------------------ */ -typedef struct event event_t; +typedef struct xco_event xco_event_t; typedef struct { /* Inline-succeed if possible. *out receives the event's value when * it has one (latch payload, select winner index, channel data). * out may be NULL if the caller doesn't need the value. */ - bool (*try_)(event_t *e, uintptr_t *out); + bool (*try_)(xco_event_t *e, uintptr_t *out); /* Arm w on the event's waitlist. Caller's contract: try_ returned * false. Single-threaded runtime guarantees no transition between. */ - void (*park)(event_t *e, waker_t *w); + void (*park)(xco_event_t *e, xco_waker_t *w); /* Remove w from the waitlist. Idempotent: no-op if not parked. */ - void (*unpark)(event_t *e, waker_t *w); -} event_vtable_t; + void (*unpark)(xco_event_t *e, xco_waker_t *w); +} xco_event_vtable_t; -struct event { const event_vtable_t *vt; }; +struct xco_event { const xco_event_vtable_t *vt; }; -static inline bool event_try(event_t *e, uintptr_t *out) { +static inline bool xco_event_try(xco_event_t *e, uintptr_t *out) { return e->vt->try_(e, out); } -static inline void event_park(event_t *e, waker_t *w) { e->vt->park(e, w); } -static inline void event_unpark(event_t *e, waker_t *w) { e->vt->unpark(e, w); } +static inline void xco_event_park(xco_event_t *e, xco_waker_t *w) { e->vt->park(e, w); } +static inline void xco_event_unpark(xco_event_t *e, xco_waker_t *w) { e->vt->unpark(e, w); } /* ---- Runtime ---------------------------------------------------------- */ /* Forward-declared: the optional timer source attached to the runtime. * Defined in the timer section below. */ -typedef struct timers timers_t; +typedef struct xco_timers xco_timers_t; -typedef struct runtime { - waker_t *head, *tail; - timers_t *timers; /* optional; advanced inside rt_run */ -} runtime_t; +typedef struct xco_runtime { + xco_waker_t *head, *tail; + xco_timers_t *timers; /* optional; advanced inside xco_rt_run */ +} xco_runtime_t; -static inline void rt_init(runtime_t *rt) { +static inline void xco_rt_init(xco_runtime_t *rt) { rt->head = rt->tail = NULL; rt->timers = NULL; } -/* Attach (or detach with NULL) a timer source. While attached, rt_run +/* Attach (or detach with NULL) a timer source. While attached, xco_rt_run * advances it each pass with the now value the caller supplied; firing - * timers may enqueue more wakers, which the same rt_run call then drains. */ -static inline void rt_attach_timers(runtime_t *rt, timers_t *ts) { + * timers may enqueue more wakers, which the same xco_rt_run call then drains. */ +static inline void xco_rt_attach_timers(xco_runtime_t *rt, xco_timers_t *ts) { rt->timers = ts; } -/* Append w to the ready queue. Used by step_waker_fire and by anyone +/* Append w to the ready queue. Used by xco__step_waker_fire and by anyone * else that wants a waker resumed by the scheduler. */ -static inline void rt_enqueue(runtime_t *rt, waker_t *w) { +static inline void xco_rt_enqueue(xco_runtime_t *rt, xco_waker_t *w) { w->next = NULL; if (rt->tail) rt->tail->next = w; else rt->head = w; rt->tail = w; } -/* Drain the ready queue, resuming each step-waker's xstep, until empty. +/* Drain the ready queue, resuming each step-waker's xco_step, until empty. * Steps may re-arm on events (and thus leave the queue) or enqueue - * other steps; rt_run keeps going until quiescent. now is forwarded to + * other steps; xco_rt_run keeps going until quiescent. now is forwarded to * any attached timer source's advance(); pass 0 (or anything) when no * source is attached. The library never reads a clock — now is always * caller-supplied. */ -void rt_run(runtime_t *rt, uint64_t now); +void xco_rt_run(xco_runtime_t *rt, uint64_t now); /* The canonical bridge between events and the scheduler. When fired, - * stashes the value and enqueues itself onto rt; rt_run pops it and - * calls xstep(step, value), so the resumed step receives the event's + * stashes the value and enqueues itself onto rt; xco_rt_run pops it and + * calls xco_step(step, value), so the resumed step receives the event's * payload directly without a re-try. * * Init once, re-park freely. The runtime hands the waker back fully * detached (next/prev cleared) before invoking the resumed step, so a - * subscriber that wants to wait on the next event can call event_park + * subscriber that wants to wait on the next event can call xco_event_park * directly — no re-init needed unless rt or step changes. */ typedef struct { - waker_t base; - runtime_t *rt; - xstep_t *step; - uintptr_t resume_value; /* set by fire, consumed by rt_run */ -} step_waker_t; + xco_waker_t base; + xco_runtime_t *rt; + xco_step_t *step; + uintptr_t resume_value; /* set by fire, consumed by xco_rt_run */ +} xco_step_waker_t; -/* Defined in xco.c; declared here so step_waker_init can install it. */ -void _step_waker_fire(waker_t *w, uintptr_t value); +/* Defined in xco.c; declared here so xco_step_waker_init can install it. */ +void xco__step_waker_fire(xco_waker_t *w, uintptr_t value); -static inline void step_waker_init(step_waker_t *sw, runtime_t *rt, xstep_t *s) { +static inline void xco_step_waker_init(xco_step_waker_t *sw, xco_runtime_t *rt, xco_step_t *s) { sw->base.next = NULL; sw->base.prev = NULL; - sw->base.fire = _step_waker_fire; + sw->base.fire = xco__step_waker_fire; sw->rt = rt; sw->step = s; sw->resume_value = 0; @@ -270,134 +270,134 @@ static inline void step_waker_init(step_waker_t *sw, runtime_t *rt, xstep_t *s) * fires every waiter. Subsequent set() calls are ignored. To re-arm, * reinitialize a fresh latch. */ typedef struct { - event_t base; + xco_event_t base; bool set; uintptr_t value; - waker_t *waiters; -} latch_t; + xco_waker_t *waiters; +} xco_latch_t; -/* Defined in xco.c; referenced by latch_init. */ -extern const event_vtable_t _latch_vt; +/* Defined in xco.c; referenced by xco_latch_init. */ +extern const xco_event_vtable_t xco__latch_vt; -static inline void latch_init(latch_t *l) { - l->base.vt = &_latch_vt; +static inline void xco_latch_init(xco_latch_t *l) { + l->base.vt = &xco__latch_vt; l->set = false; l->value = 0; l->waiters = NULL; } -void latch_set(latch_t *l, uintptr_t value); +void xco_latch_set(xco_latch_t *l, uintptr_t value); /* ---- Countdown -------------------------------------------------------- */ /* One-shot fan-in counter. Fires its embedded latch (payload 0) when - * remaining hits 0. countdown_add(n) is legal while remaining > 0; - * countdown_done decrements; both are UB once the latch has fired. + * remaining hits 0. xco_countdown_add(n) is legal while remaining > 0; + * xco_countdown_done decrements; both are UB once the latch has fired. * - * Compose with the standard event API via countdown_event(). */ -typedef struct countdown { - latch_t done; + * Compose with the standard event API via xco_countdown_event(). */ +typedef struct xco_countdown { + xco_latch_t done; size_t remaining; -} countdown_t; +} xco_countdown_t; -static inline void countdown_init(countdown_t *c, size_t n) { - latch_init(&c->done); +static inline void xco_countdown_init(xco_countdown_t *c, size_t n) { + xco_latch_init(&c->done); c->remaining = n; - if (n == 0) latch_set(&c->done, 0); + if (n == 0) xco_latch_set(&c->done, 0); } -static inline void countdown_add(countdown_t *c, size_t n) { +static inline void xco_countdown_add(xco_countdown_t *c, size_t n) { /* UB after fire — caller's contract. */ c->remaining += n; } -static inline void countdown_done(countdown_t *c) { +static inline void xco_countdown_done(xco_countdown_t *c) { /* UB at 0 — caller's contract. */ - if (--c->remaining == 0) latch_set(&c->done, 0); + if (--c->remaining == 0) xco_latch_set(&c->done, 0); } -static inline event_t *countdown_event(countdown_t *c) { return &c->done.base; } -static inline bool countdown_fired(const countdown_t *c) { return c->done.set; } +static inline xco_event_t *xco_countdown_event(xco_countdown_t *c) { return &c->done.base; } +static inline bool xco_countdown_fired(const xco_countdown_t *c) { return c->done.set; } /* ---- Notify (wake-one / wake-all) ------------------------------------- */ -/* Transient signal with no sticky state. notify_one fires (and detaches) - * the head of a FIFO waitlist; notify_all fires every parked waker. Both +/* Transient signal with no sticky state. xco_notify_one fires (and detaches) + * the head of a FIFO waitlist; xco_notify_all fires every parked waker. Both * are no-ops when the waitlist is empty. Subscribers must re-park to see * subsequent notifications. * - * event_try always returns false: there is no "ready now" state — a + * xco_event_try always returns false: there is no "ready now" state — a * subscriber waits for the *next* notify. */ -typedef struct notify { - event_t base; - waker_t *head, *tail; -} notify_t; +typedef struct xco_notify { + xco_event_t base; + xco_waker_t *head, *tail; +} xco_notify_t; -extern const event_vtable_t _notify_vt; +extern const xco_event_vtable_t xco__notify_vt; -static inline void notify_init(notify_t *n) { - n->base.vt = &_notify_vt; +static inline void xco_notify_init(xco_notify_t *n) { + n->base.vt = &xco__notify_vt; n->head = n->tail = NULL; } -static inline event_t *notify_event(notify_t *n) { return &n->base; } +static inline xco_event_t *xco_notify_event(xco_notify_t *n) { return &n->base; } -void notify_one(notify_t *n); -void notify_all(notify_t *n); +void xco_notify_one(xco_notify_t *n); +void xco_notify_all(xco_notify_t *n); /* ---- Semaphore -------------------------------------------------------- */ -/* Counting semaphore. acquire is exposed as event_t (composable with - * select / wait_or_cancel): event_try succeeds and decrements when - * permits > 0; otherwise the waker parks FIFO. semaphore_release(n) +/* Counting semaphore. acquire is exposed as xco_event_t (composable with + * select / xco_wait_or_cancel): xco_event_try succeeds and decrements when + * permits > 0; otherwise the waker parks FIFO. xco_semaphore_release(n) * hands one permit to each of up to n waiting wakers (each is fired, * which the receiver treats as "you got a permit") before adding any * leftover to the count. * - * One permit per acquire. Bulk acquire isn't expressible in event_t's + * One permit per acquire. Bulk acquire isn't expressible in xco_event_t's * shape; if you need it, call sequentially. For binary use (mutex-style * critical section across awaits) init with permits = 1. * - * Fairness: FIFO at the waitlist. A racing inline event_try by a fresh + * Fairness: FIFO at the waitlist. A racing inline xco_event_try by a fresh * caller can jump ahead of parked waiters when permits are released * back to count rather than directly handed off — release prefers * parked waiters first to avoid that. */ -typedef struct semaphore { - event_t acquire; +typedef struct xco_semaphore { + xco_event_t acquire; size_t permits; - waker_t *head, *tail; -} semaphore_t; + xco_waker_t *head, *tail; +} xco_semaphore_t; -extern const event_vtable_t _semaphore_acquire_vt; +extern const xco_event_vtable_t xco__semaphore_acquire_vt; -static inline void semaphore_init(semaphore_t *s, size_t initial) { - s->acquire.vt = &_semaphore_acquire_vt; +static inline void xco_semaphore_init(xco_semaphore_t *s, size_t initial) { + s->acquire.vt = &xco__semaphore_acquire_vt; s->permits = initial; s->head = s->tail = NULL; } -static inline event_t *semaphore_event(semaphore_t *s) { return &s->acquire; } +static inline xco_event_t *xco_semaphore_event(xco_semaphore_t *s) { return &s->acquire; } -void semaphore_release(semaphore_t *s, size_t n); +void xco_semaphore_release(xco_semaphore_t *s, size_t n); /* ---- Mutex ------------------------------------------------------------ */ -/* Binary semaphore wrapper for vocabulary at call sites. mutex_init is - * semaphore_init(s, 1); the event_t fires once per release; mutex_release +/* Binary semaphore wrapper for vocabulary at call sites. xco_mutex_init is + * xco_semaphore_init(s, 1); the xco_event_t fires once per release; xco_mutex_release * hands the permit to the next waiter (or returns it to the count). */ -typedef semaphore_t mutex_t; +typedef xco_semaphore_t xco_mutex_t; -static inline void mutex_init (mutex_t *m) { semaphore_init(m, 1); } -static inline event_t *mutex_event (mutex_t *m) { return semaphore_event(m); } -static inline void mutex_release(mutex_t *m) { semaphore_release(m, 1); } +static inline void xco_mutex_init (xco_mutex_t *m) { xco_semaphore_init(m, 1); } +static inline xco_event_t *xco_mutex_event (xco_mutex_t *m) { return xco_semaphore_event(m); } +static inline void xco_mutex_release(xco_mutex_t *m) { xco_semaphore_release(m, 1); } /* ---- Select / all-of -------------------------------------------------- */ /* Wait over N input events. Two semantics share the same storage shape, * so a caller can switch between them by changing only the init call: * - * select_event_init fires when ANY input fires (any-of) - * allof_event_init fires when ALL inputs fire (all-of) + * xco_select_event_init fires when ANY input fires (any-of) + * xco_allof_event_init fires when ALL inputs fire (all-of) * * In both cases done's payload is the index of the input whose firing * closed the wait — the winner for select, the last-to-fire for allof — @@ -406,21 +406,21 @@ static inline void mutex_release(mutex_t *m) { semaphore_release(m, 1); } * would either succeed or fail). Composes: a select_event is itself an * event. */ -typedef struct select_event select_event_t; +typedef struct xco_select_event xco_select_event_t; /* Per-input arming record. Caller-allocated as an array of n alongside * the select_event. After fire, .value holds whatever the input * delivered; other fields are internal. */ typedef struct { - waker_t w; - event_t *src; - select_event_t *parent; + xco_waker_t w; + xco_event_t *src; + xco_select_event_t *parent; uintptr_t value; /* captured at fire time */ -} select_input_t; +} xco_select_input_t; -struct select_event { - latch_t done; /* fires with the closing input's index */ - select_input_t *inputs; +struct xco_select_event { + xco_latch_t done; /* fires with the closing input's index */ + xco_select_input_t *inputs; size_t n; size_t remaining; /* counts down; done fires at 0 * (select: starts at 1, allof: at n) */ @@ -430,70 +430,70 @@ struct select_event { * for n nodes; srcs[] is the array of n input event pointers (read * once during init). If any input is already ready, the select fires * immediately and no wakers are parked. Use &s->done.base as the - * resulting event_t. */ -void select_event_init(select_event_t *s, - select_input_t *inputs, size_t n, - event_t *const *srcs); + * resulting xco_event_t. */ +void xco_select_event_init(xco_select_event_t *s, + xco_select_input_t *inputs, size_t n, + xco_event_t *const *srcs); /* Initialize as an allof (all-of). Inputs already ready at init are * consumed inline (no parking, value captured); if every input is * ready, done fires immediately. n == 0 fires done with payload 0. */ -void allof_event_init(select_event_t *s, - select_input_t *inputs, size_t n, - event_t *const *srcs); +void xco_allof_event_init(xco_select_event_t *s, + xco_select_input_t *inputs, size_t n, + xco_event_t *const *srcs); /* Disarm any inputs still parked. Safe to call after fire (no-op) and * after partial completion (allof). Required before s leaves scope if * it has not yet fired. */ -void select_event_deinit(select_event_t *s); +void xco_select_event_deinit(xco_select_event_t *s); /* ---- Channel ---------------------------------------------------------- */ /* Unbuffered rendezvous channel carrying uintptr_t. Senders and receivers * wait on each other; whichever arrives first parks until its peer - * shows up. The pending value lives in the sender's chan_send_waker_t + * shows up. The pending value lives in the sender's xco_chan_send_waker_t * for the duration of any wait — no per-channel buffer. * - * Recv side is exposed as event_t (composable with select). Send side is - * a typed API because send carries a value that doesn't fit event_t's + * Recv side is exposed as xco_event_t (composable with select). Send side is + * a typed API because send carries a value that doesn't fit xco_event_t's * try(out) signature. Selecting on send is therefore not supported in * this layer; if needed, wrap a send in a per-call op event. * * Rendezvous matrix: * send + parked recv fire recv with value, sender continues inline. - * send + no recv sender parks (chan_park_send); peer pulls later. + * send + no recv sender parks (xco_chan_park_send); peer pulls later. * recv + parked sender read sender's value, fire sender (delivery * confirmation), receiver continues inline. - * recv + no sender receiver parks (event_park on recv); peer + * recv + no sender receiver parks (xco_event_park on recv); peer * delivers later. * * FIFO order on both waitlists. * - * Close: optional EOF semantics. After chan_close, try_send fails (no + * Close: optional EOF semantics. After xco_chan_close, try_send fails (no * delivery), parked senders are drained with delivered=false, and parked - * receivers are woken so they can observe RECV_CLOSED via chan_recv. The + * receivers are woken so they can observe XCO_RECV_CLOSED via xco_chan_recv. The * recv event is "ready" iff a value is available OR the channel is - * closed — receivers must call chan_recv to disambiguate value vs EOF. - * chan_park_send after close is UB. */ + * closed — receivers must call xco_chan_recv to disambiguate value vs EOF. + * xco_chan_park_send after close is UB. */ /* Result of a typed receive on a channel or queue. */ typedef enum { - RECV_GOT, /* *out holds the delivered value */ - RECV_EMPTY, /* nothing available right now; caller may park */ - RECV_CLOSED, /* peer closed and no values remain */ -} recv_status_t; - -typedef struct chan { - event_t recv; /* the recv-side event */ - waker_t *send_head, *send_tail; /* parked chan_send_waker_t bases */ - waker_t *recv_head, *recv_tail; /* parked recv-side wakers */ + XCO_RECV_GOT, /* *out holds the delivered value */ + XCO_RECV_EMPTY, /* nothing available right now; caller may park */ + XCO_RECV_CLOSED, /* peer closed and no values remain */ +} xco_recv_status_t; + +typedef struct xco_chan { + xco_event_t recv; /* the recv-side event */ + xco_waker_t *send_head, *send_tail; /* parked xco_chan_send_waker_t bases */ + xco_waker_t *recv_head, *recv_tail; /* parked recv-side wakers */ bool closed; -} chan_t; +} xco_chan_t; -extern const event_vtable_t _chan_recv_vt; +extern const xco_event_vtable_t xco__chan_recv_vt; -static inline void chan_init(chan_t *c) { - c->recv.vt = &_chan_recv_vt; +static inline void xco_chan_init(xco_chan_t *c) { + c->recv.vt = &xco__chan_recv_vt; c->send_head = c->send_tail = NULL; c->recv_head = c->recv_tail = NULL; c->closed = false; @@ -504,19 +504,19 @@ static inline void chan_init(chan_t *c) { * step_waker — the sender resumes via the runtime, no payload passed. * * `delivered` is set by the closing side before fire: true on a normal - * recv handoff, false when chan_close drains the parked-sender list. + * recv handoff, false when xco_chan_close drains the parked-sender list. * Senders read it after resume to know whether their value reached a * receiver. */ typedef struct { - step_waker_t sw; + xco_step_waker_t sw; uintptr_t value; bool delivered; -} chan_send_waker_t; +} xco_chan_send_waker_t; -static inline void chan_send_waker_init(chan_send_waker_t *csw, - runtime_t *rt, xstep_t *s, +static inline void xco_chan_send_waker_init(xco_chan_send_waker_t *csw, + xco_runtime_t *rt, xco_step_t *s, uintptr_t value) { - step_waker_init(&csw->sw, rt, s); + xco_step_waker_init(&csw->sw, rt, s); csw->value = value; csw->delivered = false; } @@ -525,35 +525,35 @@ static inline void chan_send_waker_init(chan_send_waker_t *csw, * AND the channel is open; its waker fires with the value, the receiver * becomes ready, and the sender continues without parking. Returns false * after close (no delivery). */ -bool chan_try_send(chan_t *c, uintptr_t value); +bool xco_chan_try_send(xco_chan_t *c, uintptr_t value); -/* Park a sender. csw->value must already be set (use chan_send_waker_init). - * The sender's xstep is resumed when a receiver consumes the value, or - * when chan_close drains the list (sender resumes with csw->delivered - * false). Calling chan_park_send on a closed channel is UB. */ -void chan_park_send(chan_t *c, chan_send_waker_t *csw); +/* Park a sender. csw->value must already be set (use xco_chan_send_waker_init). + * The sender's xco_step is resumed when a receiver consumes the value, or + * when xco_chan_close drains the list (sender resumes with csw->delivered + * false). Calling xco_chan_park_send on a closed channel is UB. */ +void xco_chan_park_send(xco_chan_t *c, xco_chan_send_waker_t *csw); /* Remove a parked sender (cancellation). No-op if not parked. */ -void chan_unpark_send(chan_t *c, chan_send_waker_t *csw); +void xco_chan_unpark_send(xco_chan_t *c, xco_chan_send_waker_t *csw); -/* Typed receive. Disambiguates value vs EOF where event_try cannot. */ -recv_status_t chan_recv(chan_t *c, uintptr_t *out); +/* Typed receive. Disambiguates value vs EOF where xco_event_try cannot. */ +xco_recv_status_t xco_chan_recv(xco_chan_t *c, uintptr_t *out); /* Close the channel. Idempotent. Drains parked senders (each resumes * with csw->delivered = false) and wakes parked receivers (which then - * observe RECV_CLOSED via chan_recv). Subsequent chan_try_send returns - * false; chan_park_send is UB. */ -void chan_close(chan_t *c); -static inline bool chan_is_closed(const chan_t *c) { return c->closed; } + * observe XCO_RECV_CLOSED via xco_chan_recv). Subsequent xco_chan_try_send returns + * false; xco_chan_park_send is UB. */ +void xco_chan_close(xco_chan_t *c); +static inline bool xco_chan_is_closed(const xco_chan_t *c) { return c->closed; } /* Selectable send op. A per-call object that holds the value, parks * on the channel, and exposes &op->done.base as the event that fires * when delivery resolves. Compose with select like any other event. * - * The op embeds a chan_send_waker_t (so the chan's send list stays + * The op embeds a xco_chan_send_waker_t (so the chan's send list stays * uniform — receivers read .value at the same offset for both direct * and op senders) but rewires its fire callback: instead of resuming - * an xstep, fire sets op->done. Polymorphism via the function pointer. + * an xco_step, fire sets op->done. Polymorphism via the function pointer. * * The done latch's payload is 1 on a successful delivery and 0 on * close-drain (sender's value did not reach a receiver). The init path @@ -562,60 +562,60 @@ static inline bool chan_is_closed(const chan_t *c) { return c->closed; } * Lifecycle: init → wait on &op->done.base → deinit. Always deinit; * it's a no-op after resolution and unparks the chan-side waker if not. */ typedef struct { - chan_send_waker_t csw; /* parked on chan; fire overridden */ - chan_t *chan; - latch_t done; -} chan_send_op_t; + xco_chan_send_waker_t csw; /* parked on chan; fire overridden */ + xco_chan_t *chan; + xco_latch_t done; +} xco_chan_send_op_t; -/* Implementation detail: exposed so chan_send_op_init can install it. */ -void _chan_send_op_fire(waker_t *w, uintptr_t value); +/* Implementation detail: exposed so xco_chan_send_op_init can install it. */ +void xco__chan_send_op_fire(xco_waker_t *w, uintptr_t value); -void chan_send_op_init(chan_send_op_t *op, chan_t *c, uintptr_t value); -void chan_send_op_deinit(chan_send_op_t *op); +void xco_chan_send_op_init(xco_chan_send_op_t *op, xco_chan_t *c, uintptr_t value); +void xco_chan_send_op_deinit(xco_chan_send_op_t *op); /* ---- Queue ------------------------------------------------------------ */ /* Bounded FIFO of uintptr_t. Caller provides the ring buffer storage. - * Recv side is exposed as event_t (composable with select). Send side is + * Recv side is exposed as xco_event_t (composable with select). Send side is * a typed API (carries a value), shaped after chan's send. * * Three full-buffer policies, fixed at init: - * QUEUE_BLOCK senders park until a receiver makes room. - * QUEUE_DROP_NEWEST queue_try_send silently discards the new value. - * QUEUE_DROP_OLDEST queue_try_send evicts the head and pushes new tail. + * XCO_QUEUE_BLOCK senders park until a receiver makes room. + * XCO_QUEUE_DROP_NEWEST xco_queue_try_send silently discards the new value. + * XCO_QUEUE_DROP_OLDEST xco_queue_try_send evicts the head and pushes new tail. * - * Senders never park under DROP_* policies — queue_park_send is only - * meaningful under QUEUE_BLOCK, and only after queue_try_send returned - * false. queue_unpark_send is idempotent (cancellation-safe). + * Senders never park under DROP_* policies — xco_queue_park_send is only + * meaningful under XCO_QUEUE_BLOCK, and only after xco_queue_try_send returned + * false. xco_queue_unpark_send is idempotent (cancellation-safe). * - * Direct-handoff: queue_try_send first checks for a parked receiver and + * Direct-handoff: xco_queue_try_send first checks for a parked receiver and * delivers inline if present (payload bypasses the buffer), regardless * of policy. Symmetric to chan. * - * cap == 0 with QUEUE_BLOCK degenerates to a rendezvous channel; chan_t + * cap == 0 with XCO_QUEUE_BLOCK degenerates to a rendezvous channel; xco_chan_t * remains the more direct expression of that case. */ typedef enum { - QUEUE_BLOCK, - QUEUE_DROP_NEWEST, - QUEUE_DROP_OLDEST, -} queue_policy_t; + XCO_QUEUE_BLOCK, + XCO_QUEUE_DROP_NEWEST, + XCO_QUEUE_DROP_OLDEST, +} xco_queue_policy_t; -typedef struct queue { - event_t recv; +typedef struct xco_queue { + xco_event_t recv; uintptr_t *buf; size_t cap, head, len; - queue_policy_t policy; - waker_t *send_head, *send_tail; - waker_t *recv_head, *recv_tail; + xco_queue_policy_t policy; + xco_waker_t *send_head, *send_tail; + xco_waker_t *recv_head, *recv_tail; bool closed; -} queue_t; +} xco_queue_t; -extern const event_vtable_t _queue_recv_vt; +extern const xco_event_vtable_t xco__queue_recv_vt; -static inline void queue_init(queue_t *q, uintptr_t *buf, size_t cap, - queue_policy_t policy) { - q->recv.vt = &_queue_recv_vt; +static inline void xco_queue_init(xco_queue_t *q, uintptr_t *buf, size_t cap, + xco_queue_policy_t policy) { + q->recv.vt = &xco__queue_recv_vt; q->buf = buf; q->cap = cap; q->head = 0; @@ -626,51 +626,51 @@ static inline void queue_init(queue_t *q, uintptr_t *buf, size_t cap, q->closed = false; } -static inline event_t *queue_recv_event(queue_t *q) { return &q->recv; } +static inline xco_event_t *xco_queue_recv_event(xco_queue_t *q) { return &q->recv; } /* Try to enqueue. Direct-delivers to a parked receiver if one is waiting. * Returns: - * QUEUE_BLOCK true if delivered or buffered; false if full. - * QUEUE_DROP_NEWEST always true (silently drops if full). - * QUEUE_DROP_OLDEST always true (evicts head if full). */ -bool queue_try_send(queue_t *q, uintptr_t value); + * XCO_QUEUE_BLOCK true if delivered or buffered; false if full. + * XCO_QUEUE_DROP_NEWEST always true (silently drops if full). + * XCO_QUEUE_DROP_OLDEST always true (evicts head if full). */ +bool xco_queue_try_send(xco_queue_t *q, uintptr_t value); -/* Sender-side waker for QUEUE_BLOCK. Same shape as chan_send_waker_t: +/* Sender-side waker for XCO_QUEUE_BLOCK. Same shape as xco_chan_send_waker_t: * a step_waker plus the value to deliver. Receivers read .value at the * same offset so the queue's send list stays uniform. `delivered` is * set by the closing side: true on a normal handoff, false on a close * drain. */ typedef struct { - step_waker_t sw; + xco_step_waker_t sw; uintptr_t value; bool delivered; -} queue_send_waker_t; +} xco_queue_send_waker_t; -static inline void queue_send_waker_init(queue_send_waker_t *qsw, - runtime_t *rt, xstep_t *s, +static inline void xco_queue_send_waker_init(xco_queue_send_waker_t *qsw, + xco_runtime_t *rt, xco_step_t *s, uintptr_t value) { - step_waker_init(&qsw->sw, rt, s); + xco_step_waker_init(&qsw->sw, rt, s); qsw->value = value; qsw->delivered = false; } -void queue_park_send (queue_t *q, queue_send_waker_t *qsw); -void queue_unpark_send(queue_t *q, queue_send_waker_t *qsw); +void xco_queue_park_send (xco_queue_t *q, xco_queue_send_waker_t *qsw); +void xco_queue_unpark_send(xco_queue_t *q, xco_queue_send_waker_t *qsw); -/* Typed receive. Mirrors chan_recv: returns RECV_GOT (value popped from - * the buffer or directly from a parked sender), RECV_CLOSED (closed and - * drained), or RECV_EMPTY (caller may park). */ -recv_status_t queue_recv(queue_t *q, uintptr_t *out); +/* Typed receive. Mirrors xco_chan_recv: returns XCO_RECV_GOT (value popped from + * the buffer or directly from a parked sender), XCO_RECV_CLOSED (closed and + * drained), or XCO_RECV_EMPTY (caller may park). */ +xco_recv_status_t xco_queue_recv(xco_queue_t *q, uintptr_t *out); /* Close the queue. Idempotent. Drains parked senders (delivered=false) - * and wakes parked receivers. After close, queue_try_send under - * QUEUE_BLOCK returns false; under QUEUE_DROP_* the value is silently - * dropped (returns true). queue_park_send after close is UB. */ -void queue_close(queue_t *q); -static inline bool queue_is_closed(const queue_t *q) { return q->closed; } - -/* Selectable send op. Mirrors chan_send_op_t: a per-call object that - * holds the value, parks on the queue (only meaningful under QUEUE_BLOCK), + * and wakes parked receivers. After close, xco_queue_try_send under + * XCO_QUEUE_BLOCK returns false; under QUEUE_DROP_* the value is silently + * dropped (returns true). xco_queue_park_send after close is UB. */ +void xco_queue_close(xco_queue_t *q); +static inline bool xco_queue_is_closed(const xco_queue_t *q) { return q->closed; } + +/* Selectable send op. Mirrors xco_chan_send_op_t: a per-call object that + * holds the value, parks on the queue (only meaningful under XCO_QUEUE_BLOCK), * and exposes &op->done.base as the event that fires when the send * resolves. The done latch's payload is 1 on a successful delivery * (handed to a receiver, buffered, or accepted under DROP_*) and 0 on @@ -679,97 +679,97 @@ static inline bool queue_is_closed(const queue_t *q) { return q->closed; } * Under DROP_* policies the send always resolves inline at init: the * try_send path returns true and op->done fires immediately. */ typedef struct { - queue_send_waker_t qsw; /* parked on queue; fire overridden */ - queue_t *queue; - latch_t done; -} queue_send_op_t; + xco_queue_send_waker_t qsw; /* parked on queue; fire overridden */ + xco_queue_t *queue; + xco_latch_t done; +} xco_queue_send_op_t; -void _queue_send_op_fire(waker_t *w, uintptr_t value); +void xco__queue_send_op_fire(xco_waker_t *w, uintptr_t value); -void queue_send_op_init (queue_send_op_t *op, queue_t *q, uintptr_t value); -void queue_send_op_deinit(queue_send_op_t *op); +void xco_queue_send_op_init (xco_queue_send_op_t *op, xco_queue_t *q, uintptr_t value); +void xco_queue_send_op_deinit(xco_queue_send_op_t *op); /* ---- Broadcast (slot) ------------------------------------------------- */ /* Re-armable signal carrying a "latest value" slot. Subscribers park on - * the event; broadcast_publish stores the new value, fires every parked + * the event; xco_broadcast_publish stores the new value, fires every parked * subscriber with that value, and clears the waitlist — subscribers must * re-park to see further publishes. Subscribers that aren't parked at * publish time miss that publish but will see the next one. This is the * coalescing "watch a slot" semantics, not lossless fan-out. * - * event_try always returns false: there is no "ready now" state — a + * xco_event_try always returns false: there is no "ready now" state — a * subscriber waits for the *next* publish. To read the latest published - * value at any time, use broadcast_value (valid once broadcast_has_value + * value at any time, use xco_broadcast_value (valid once xco_broadcast_has_value * returns true). * * For lossless multi-consumer delivery, give each subscriber its own * queue and have the producer write to all of them. */ -typedef struct broadcast { - event_t base; +typedef struct xco_broadcast { + xco_event_t base; bool has_value; uintptr_t value; - waker_t *waiters; -} broadcast_t; + xco_waker_t *waiters; +} xco_broadcast_t; -extern const event_vtable_t _broadcast_vt; +extern const xco_event_vtable_t xco__broadcast_vt; -static inline void broadcast_init(broadcast_t *b) { - b->base.vt = &_broadcast_vt; +static inline void xco_broadcast_init(xco_broadcast_t *b) { + b->base.vt = &xco__broadcast_vt; b->has_value = false; b->value = 0; b->waiters = NULL; } -static inline event_t *broadcast_event (broadcast_t *b) { return &b->base; } -static inline bool broadcast_has_value(const broadcast_t *b) { return b->has_value; } -static inline uintptr_t broadcast_value (const broadcast_t *b) { return b->value; } +static inline xco_event_t *xco_broadcast_event (xco_broadcast_t *b) { return &b->base; } +static inline bool xco_broadcast_has_value(const xco_broadcast_t *b) { return b->has_value; } +static inline uintptr_t xco_broadcast_value (const xco_broadcast_t *b) { return b->value; } -void broadcast_publish(broadcast_t *b, uintptr_t value); +void xco_broadcast_publish(xco_broadcast_t *b, uintptr_t value); /* ---- Cancellation ----------------------------------------------------- */ /* A cancellation token is a sticky latch — these aliases exist for - * vocabulary at call sites. cancel_set fires every parked waiter; the - * idempotency of latch_set means racing cancellers are fine. + * vocabulary at call sites. xco_cancel_set fires every parked waiter; the + * idempotency of xco_latch_set means racing cancellers are fine. * - * Pair a cancel_t with any blocking event via wait_or_cancel to get + * Pair a xco_cancel_t with any blocking event via xco_wait_or_cancel to get * "await X, or be cancelled." * * Discipline: cancellation notifies; it never drops in-flight values. * A cancelled await returns control to its caller, which is responsible * for draining whatever it owns — deinit a pending chan_send_op so its * value goes back to the sender, deinit a select_event so input wakers - * detach, drive a cancellable coroutine to XSTEP_DEAD before freeing + * detach, drive a cancellable coroutine to XCO_STEP_DEAD before freeing * its stack. The xco layer does no unwinding; the coroutine cooperates. */ -typedef latch_t cancel_t; +typedef xco_latch_t xco_cancel_t; -static inline void cancel_init(cancel_t *c) { latch_init(c); } -static inline void cancel_set(cancel_t *c) { latch_set(c, 0); } -static inline bool cancel_is_set(const cancel_t *c) { return c->set; } -static inline event_t *cancel_event(cancel_t *c) { return &c->base; } +static inline void xco_cancel_init(xco_cancel_t *c) { xco_latch_init(c); } +static inline void xco_cancel_set(xco_cancel_t *c) { xco_latch_set(c, 0); } +static inline bool xco_cancel_is_set(const xco_cancel_t *c) { return c->set; } +static inline xco_event_t *xco_cancel_event(xco_cancel_t *c) { return &c->base; } -/* Outcome indices for wait_or_cancel — match the inputs[] order so the +/* Outcome indices for xco_wait_or_cancel — match the inputs[] order so the * value the resumer receives from the latched select reads as one of * these directly. */ enum { - WAIT_OK = 0, /* ev fired; inputs[0].value holds its payload */ - WAIT_CANCELLED = 1, /* cancel fired before ev */ + XCO_WAIT_OK = 0, /* ev fired; inputs[0].value holds its payload */ + XCO_WAIT_CANCELLED = 1, /* cancel fired before ev */ }; /* Build a select over (ev, cancel) using caller-provided storage. If * either is already ready at init the select fast-paths and never parks * anyone (ev is checked first, so an event that has already resolved * wins over a concurrent cancel). Treat &out->done.base as the event - * to wait on. Always pair with select_event_deinit before storage + * to wait on. Always pair with xco_select_event_deinit before storage * leaves scope (no-op once fired). */ -static inline void wait_or_cancel(select_event_t *out, - select_input_t inputs[2], - event_t *ev, cancel_t *c) { - event_t *srcs[2] = {ev, cancel_event(c)}; - select_event_init(out, inputs, 2, srcs); +static inline void xco_wait_or_cancel(xco_select_event_t *out, + xco_select_input_t inputs[2], + xco_event_t *ev, xco_cancel_t *c) { + xco_event_t *srcs[2] = {ev, xco_cancel_event(c)}; + xco_select_event_init(out, inputs, 2, srcs); } /* ---- Timers ----------------------------------------------------------- */ @@ -777,7 +777,7 @@ static inline void wait_or_cancel(select_event_t *out, /* A timer is a sticky event keyed on a u64 deadline. It fires (exactly * once) when the attached timer source is advanced past that deadline. * The library never reads a clock; the caller provides `now` to - * timers_advance (or via rt_run). + * xco_timers_advance (or via xco_rt_run). * * Storage is pluggable through the timers vtable so callers can swap a * pairing heap (in-tree, O(log n) amortized everywhere including cancel) @@ -786,95 +786,95 @@ static inline void wait_or_cancel(select_event_t *out, * impl interprets them. * * Lifecycle: - * timer_init(t, ts, deadline) // inserts into ts - * ... wait on timer_event(t), or compose into select/wait_or_cancel ... - * timer_deinit(t) // removes from ts if not yet fired + * xco_timer_init(t, ts, deadline) // inserts into ts + * ... wait on xco_timer_event(t), or compose into select/xco_wait_or_cancel ... + * xco_timer_deinit(t) // removes from ts if not yet fired * * Fire payload is the deadline. Re-arming = reinit a fresh timer. */ -typedef struct timer timer_t; +typedef struct xco_timer xco_timer_t; typedef struct { /* Insert t into the source. t must be initialized but not yet * inserted; insert sets t's heap link fields. */ - void (*insert) (timers_t *ts, timer_t *t); + void (*insert) (xco_timers_t *ts, xco_timer_t *t); /* Remove t from the source if currently inserted. Caller must * ensure t was inserted into this same source. */ - void (*cancel) (timers_t *ts, timer_t *t); + void (*cancel) (xco_timers_t *ts, xco_timer_t *t); /* Fire every timer whose deadline <= now, in deadline order, popping * each from the source. Each fire drains the timer's waiter list. */ - void (*advance)(timers_t *ts, uint64_t now); + void (*advance)(xco_timers_t *ts, uint64_t now); /* If any timer is queued, write its deadline to *out and return * true. *out untouched on false. */ - bool (*peek) (const timers_t *ts, uint64_t *out); -} timers_vtable_t; + bool (*peek) (const xco_timers_t *ts, uint64_t *out); +} xco_timers_vtable_t; -struct timers { const timers_vtable_t *vt; }; +struct xco_timers { const xco_timers_vtable_t *vt; }; -static inline void timers_insert (timers_t *ts, timer_t *t) { ts->vt->insert (ts, t); } -static inline void timers_cancel (timers_t *ts, timer_t *t) { ts->vt->cancel (ts, t); } -static inline void timers_advance(timers_t *ts, uint64_t now) { ts->vt->advance(ts, now); } -static inline bool timers_peek (const timers_t *ts, uint64_t *o) { return ts->vt->peek(ts, o); } +static inline void xco_timers_insert (xco_timers_t *ts, xco_timer_t *t) { ts->vt->insert (ts, t); } +static inline void xco_timers_cancel (xco_timers_t *ts, xco_timer_t *t) { ts->vt->cancel (ts, t); } +static inline void xco_timers_advance(xco_timers_t *ts, uint64_t now) { ts->vt->advance(ts, now); } +static inline bool xco_timers_peek (const xco_timers_t *ts, uint64_t *o) { return ts->vt->peek(ts, o); } /* Concrete timer. Embeds a latch so try/park/unpark and the fire-all * waitlist handling come for free; the timer source manipulates only * the heap link fields and triggers the latch on fire. The latch's * payload after fire is the timer's deadline. */ -struct timer { - latch_t done; /* fires once, payload = deadline */ +struct xco_timer { + xco_latch_t done; /* fires once, payload = deadline */ uint64_t deadline; - timers_t *src; /* source this timer is registered with */ + xco_timers_t *src; /* source this timer is registered with */ bool in_heap; /* true between insert and fire/cancel */ /* Pairing-heap link fields; opaque to anyone but the source impl. * prev is parent if first child, else previous sibling, else NULL. */ - timer_t *child, *prev, *next; + xco_timer_t *child, *prev, *next; }; -static inline event_t *timer_event(timer_t *t) { return &t->done.base; } -static inline bool timer_fired(const timer_t *t) { return t->done.set; } +static inline xco_event_t *xco_timer_event(xco_timer_t *t) { return &t->done.base; } +static inline bool xco_timer_fired(const xco_timer_t *t) { return t->done.set; } -void timer_init (timer_t *t, timers_t *ts, uint64_t deadline); -void timer_deinit(timer_t *t); +void xco_timer_init (xco_timer_t *t, xco_timers_t *ts, uint64_t deadline); +void xco_timer_deinit(xco_timer_t *t); /* In-tree timer source: intrusive pairing heap. O(1) amortized insert * and meld; O(log n) amortized advance and cancel. No per-source * allocation — the heap is just a root pointer; nodes live in the - * caller's timer_t's. */ + * caller's xco_timer_t's. */ typedef struct { - timers_t base; - timer_t *root; -} pairing_heap_t; + xco_timers_t base; + xco_timer_t *root; +} xco_pairing_heap_t; -extern const timers_vtable_t _pairing_heap_vt; +extern const xco_timers_vtable_t xco__pairing_heap_vt; -static inline void pairing_heap_init(pairing_heap_t *h) { - h->base.vt = &_pairing_heap_vt; +static inline void xco_pairing_heap_init(xco_pairing_heap_t *h) { + h->base.vt = &xco__pairing_heap_vt; h->root = NULL; } /* ---- Timeout ---------------------------------------------------------- */ -/* Bundle: a timer that fires a cancel_t on expiration. The natural +/* Bundle: a timer that fires a xco_cancel_t on expiration. The natural * pairing for "await ev, or be cancelled by deadline": * - * timeout_t to; - * timeout_init(&to, ts, now + budget); - * select_event_t sel; select_input_t inputs[2]; - * wait_or_cancel(&sel, inputs, ev, &to.cancel); + * xco_timeout_t to; + * xco_timeout_init(&to, ts, now + budget); + * xco_select_event_t sel; xco_select_input_t inputs[2]; + * xco_wait_or_cancel(&sel, inputs, ev, &to.cancel); * ... wait on &sel.done.base ... - * select_event_deinit(&sel); - * timeout_deinit(&to); // safe whether the timer fired or not + * xco_select_event_deinit(&sel); + * xco_timeout_deinit(&to); // safe whether the timer fired or not * * The bridge waker is parked on the timer; when it fires it sets the - * cancel. Bridge fire is idempotent vs cancel_set (a sticky latch). */ -typedef struct timeout { - timer_t timer; - cancel_t cancel; - waker_t bridge; -} timeout_t; + * cancel. Bridge fire is idempotent vs xco_cancel_set (a sticky latch). */ +typedef struct xco_timeout { + xco_timer_t timer; + xco_cancel_t cancel; + xco_waker_t bridge; +} xco_timeout_t; -void timeout_init (timeout_t *to, timers_t *ts, uint64_t deadline); -void timeout_deinit(timeout_t *to); +void xco_timeout_init (xco_timeout_t *to, xco_timers_t *ts, uint64_t deadline); +void xco_timeout_deinit(xco_timeout_t *to); /* ---- Ticker ----------------------------------------------------------- */ @@ -885,117 +885,117 @@ void timeout_deinit(timeout_t *to); * just-fired deadline as the payload. Subscribers that aren't parked at * a fire miss it (transient — same coalescing semantics as broadcast). * - * ticker_init(&t, ts, period, first_deadline); - * ... wait on ticker_event(&t), re-park to see further ticks ... - * ticker_deinit(&t); // cancels the in-flight timer + * xco_ticker_init(&t, ts, period, first_deadline); + * ... wait on xco_ticker_event(&t), re-park to see further ticks ... + * xco_ticker_deinit(&t); // cancels the in-flight timer * - * event_try always returns false; subscribers wait for the *next* tick. */ -typedef struct ticker { - timer_t timer; - timers_t *src; + * xco_event_try always returns false; subscribers wait for the *next* tick. */ +typedef struct xco_ticker { + xco_timer_t timer; + xco_timers_t *src; uint64_t period; - event_t base; - waker_t *waiters; - waker_t bridge; /* internal: parks on timer_event */ -} ticker_t; + xco_event_t base; + xco_waker_t *waiters; + xco_waker_t bridge; /* internal: parks on xco_timer_event */ +} xco_ticker_t; -extern const event_vtable_t _ticker_vt; +extern const xco_event_vtable_t xco__ticker_vt; -void ticker_init (ticker_t *t, timers_t *ts, +void xco_ticker_init (xco_ticker_t *t, xco_timers_t *ts, uint64_t period, uint64_t first_deadline); -void ticker_deinit(ticker_t *t); -static inline event_t *ticker_event(ticker_t *t) { return &t->base; } +void xco_ticker_deinit(xco_ticker_t *t); +static inline xco_event_t *xco_ticker_event(xco_ticker_t *t) { return &t->base; } /* ---- Task ------------------------------------------------------------- */ -/* Lifecycle handle for a running xstep. Bundles a done latch (fires when - * the xstep returns, payload = its return value) with a cancel latch - * (the canonical signal to ask the xstep to wind down). The xstep itself +/* Lifecycle handle for a running xco_step. Bundles a done latch (fires when + * the xco_step returns, payload = its return value) with a cancel latch + * (the canonical signal to ask the xco_step to wind down). The xco_step itself * is caller-allocated; the task holds a pointer to it. * * Who fires done: - * - Hand-coded state machine: call task_done(t, ret) in the same arm - * that returns XSTEP_DEAD. - * - xco-backed task (see xco_task_t below): the trampoline calls - * task_done automatically with the coroutine's return value. + * - Hand-coded state machine: call xco_task_done(t, ret) in the same arm + * that returns XCO_STEP_DEAD. + * - xco-backed task (see xco_cotask_t below): the xco_trampoline calls + * xco_task_done automatically with the coroutine's return value. * - * Cooperation: cancellation only notifies — the xstep is responsible for - * draining what it owns and reaching XSTEP_DEAD. The task's cancel is a - * normal cancel_t, so the xstep typically composes wait_or_cancel against + * Cooperation: cancellation only notifies — the xco_step is responsible for + * draining what it owns and reaching XCO_STEP_DEAD. The task's cancel is a + * normal xco_cancel_t, so the xco_step typically composes xco_wait_or_cancel against * it on every blocking await. * - * Joining: callers wait on task_done_event with the standard event API - * (try / park, or compose into select / wait_or_cancel). On fire the - * latch's payload is the xstep's return value. */ + * Joining: callers wait on xco_task_done_event with the standard event API + * (try / park, or compose into select / xco_wait_or_cancel). On fire the + * latch's payload is the xco_step's return value. */ -typedef struct task { - xstep_t *step; - latch_t done; - cancel_t cancel; -} task_t; +typedef struct xco_task { + xco_step_t *step; + xco_latch_t done; + xco_cancel_t cancel; +} xco_task_t; -static inline void task_init(task_t *t, xstep_t *step) { +static inline void xco_task_init(xco_task_t *t, xco_step_t *step) { t->step = step; - latch_init(&t->done); - cancel_init(&t->cancel); + xco_latch_init(&t->done); + xco_cancel_init(&t->cancel); } -/* Mark the task complete with its return value. Idempotent (latch_set is). */ -static inline void task_done(task_t *t, uintptr_t value) { - latch_set(&t->done, value); +/* Mark the task complete with its return value. Idempotent (xco_latch_set is). */ +static inline void xco_task_done(xco_task_t *t, uintptr_t value) { + xco_latch_set(&t->done, value); } -static inline event_t *task_done_event (task_t *t) { return &t->done.base; } -static inline cancel_t *task_cancel (task_t *t) { return &t->cancel; } -static inline bool task_finished (const task_t *t) { return t->done.set; } -static inline bool task_is_cancelled(const task_t *t) { return cancel_is_set(&t->cancel); } -static inline xstep_t *task_step (task_t *t) { return t->step; } +static inline xco_event_t *xco_task_done_event (xco_task_t *t) { return &t->done.base; } +static inline xco_cancel_t *xco_task_cancel (xco_task_t *t) { return &t->cancel; } +static inline bool xco_task_finished (const xco_task_t *t) { return t->done.set; } +static inline bool xco_task_is_cancelled(const xco_task_t *t) { return xco_cancel_is_set(&t->cancel); } +static inline xco_step_t *xco_task_step (xco_task_t *t) { return t->step; } /* ---- Task group ------------------------------------------------------- */ /* Fan-in join + fan-out cancel for a dynamic set of tasks. Caller - * provides storage for each per-attachment record (group_attach_t), so + * provides storage for each per-attachment record (xco_group_attach_t), so * the group itself does no allocation. * - * task_group_attach(g, t, slot): - * countdown_add(g->pending, 1); slot's bridge waker parks on - * task_done_event(t); slot is appended to g's list. When the task's + * xco_task_group_attach(g, t, slot): + * xco_countdown_add(g->pending, 1); slot's bridge waker parks on + * xco_task_done_event(t); slot is appended to g's list. When the task's * done fires, the bridge fires: it splices the slot out of g's list - * and calls countdown_done(&g->pending). Re-attaching a finished + * and calls xco_countdown_done(&g->pending). Re-attaching a finished * task is UB. * - * task_group_cancel(g): walks the attachment list and cancel_set's - * each &slot->task->cancel, then cancel_set's g->cancel. Bodies that - * compose wait_or_cancel against task_cancel(t) wind down cooperatively; + * xco_task_group_cancel(g): walks the attachment list and xco_cancel_set's + * each &slot->task->cancel, then xco_cancel_set's g->cancel. Bodies that + * compose xco_wait_or_cancel against xco_task_cancel(t) wind down cooperatively; * meanwhile, anything waiting on g->cancel observes the group-level * signal directly. * - * task_group_join_event(g): fires when every attached task has reached - * task_done. Compose with select / wait_or_cancel like any event. */ - -typedef struct group_attach group_attach_t; - -typedef struct task_group { - countdown_t pending; - cancel_t cancel; - group_attach_t *head, *tail; -} task_group_t; - -struct group_attach { - waker_t bridge; /* parked on task_done_event(task) */ - task_t *task; - task_group_t *group; - group_attach_t *next, *prev; + * xco_task_group_join_event(g): fires when every attached task has reached + * xco_task_done. Compose with select / xco_wait_or_cancel like any event. */ + +typedef struct xco_group_attach xco_group_attach_t; + +typedef struct xco_task_group { + xco_countdown_t pending; + xco_cancel_t cancel; + xco_group_attach_t *head, *tail; +} xco_task_group_t; + +struct xco_group_attach { + xco_waker_t bridge; /* parked on xco_task_done_event(task) */ + xco_task_t *task; + xco_task_group_t *group; + xco_group_attach_t *next, *prev; }; -void task_group_init (task_group_t *g); -void task_group_attach (task_group_t *g, task_t *t, - group_attach_t *slot); -void task_group_cancel (task_group_t *g); -static inline event_t *task_group_join_event (task_group_t *g) { - return countdown_event(&g->pending); +void xco_task_group_init (xco_task_group_t *g); +void xco_task_group_attach (xco_task_group_t *g, xco_task_t *t, + xco_group_attach_t *slot); +void xco_task_group_cancel (xco_task_group_t *g); +static inline xco_event_t *xco_task_group_join_event (xco_task_group_t *g) { + return xco_countdown_event(&g->pending); } -static inline cancel_t *task_group_cancel_handle(task_group_t *g) { +static inline xco_cancel_t *xco_task_group_cancel_handle(xco_task_group_t *g) { return &g->cancel; } @@ -1008,24 +1008,24 @@ static inline cancel_t *task_group_cancel_handle(task_group_t *g) { * ==================================================================== */ /* Coroutine entry point. The argument is the value supplied to the - * first xstep on this xco. The return value is delivered to the resumer - * as the final xstep_result, with status XSTEP_DEAD. */ + * first xco_step on this xco. The return value is delivered to the resumer + * as the final xco_step_result, with status XCO_STEP_DEAD. */ typedef uintptr_t (*xco_fn)(uintptr_t arg); /* Coroutine control block. Allocate anywhere — on a stack, in a - * struct, on the heap. xstep_t base is first so xco_t * can be passed - * directly to xstep() or any generic xstep_t * consumer. The trailing - * _private storage holds the saved register context and bookkeeping; + * struct, on the heap. xco_step_t base is first so xco_t * can be passed + * directly to xco_step() or any generic xco_step_t * consumer. The trailing + * priv storage holds the saved register context and bookkeeping; * its contents are private to the implementation. */ typedef struct xco { - xstep_t base; - _Alignas(XCO_ALIGN) unsigned char _private[XCO_SIZE]; + xco_step_t base; + _Alignas(XCO_ALIGN) unsigned char priv[XCO_SIZE]; } xco_t; /* Initialize *c to run fn on [stack_base, stack_base + stack_len). * stack_base must be XCO_STACK_ALIGN-aligned; the runtime picks the * starting SP based on the architecture's stack growth direction. - * Status after init is XSTEP_INIT. */ + * Status after init is XCO_STEP_INIT. */ void xco_init(xco_t *c, xco_fn fn, void *stack_base, size_t stack_len); @@ -1038,44 +1038,44 @@ uintptr_t xco_suspend(uintptr_t value); * one. The runtime maintains this for xco_suspend. */ xco_t *xco_self(void); -/* Resuming a coroutine is just driving its xstep: callers use - * xstep(&c->base, v) directly. Reading status without resuming is - * xstep_status(&c->base). The xco layer adds no separate vocabulary +/* Resuming a coroutine is just driving its xco_step: callers use + * xco_step(&c->base, v) directly. Reading status without resuming is + * xco_step_status(&c->base). The xco layer adds no separate vocabulary * for these — that's the unification with hand-coded state machines. - * Resuming a coroutine that is not XSTEP_INIT or XSTEP_SUSPENDED is + * Resuming a coroutine that is not XCO_STEP_INIT or XCO_STEP_SUSPENDED is * undefined. */ /* Convenience: init then first-step in one call. */ -static inline xstep_result_t xco_spawn(xco_t *c, xco_fn fn, +static inline xco_step_result_t xco_spawn(xco_t *c, xco_fn fn, void *stack_base, size_t stack_len, uintptr_t arg) { xco_init(c, fn, stack_base, stack_len); - return xstep(&c->base, arg); + return xco_step(&c->base, arg); } /* Cooperative yield. Enqueues self on rt's ready queue and suspends; - * the next rt_run pass resumes us. Useful for fairness when a coroutine + * the next xco_rt_run pass resumes us. Useful for fairness when a coroutine * wants to give other ready work a turn between long-running steps. * Must be called from inside a coroutine driven by rt. */ -static inline void xco_yield(runtime_t *rt) { - step_waker_t sw; - step_waker_init(&sw, rt, &xco_self()->base); - rt_enqueue(rt, &sw.base); +static inline void xco_yield(xco_runtime_t *rt) { + xco_step_waker_t sw; + xco_step_waker_init(&sw, rt, &xco_self()->base); + xco_rt_enqueue(rt, &sw.base); xco_suspend(0); } /* Await an event from inside a coroutine. The standard try-park-suspend * dance, in one call. Returns the event's value (delivered by fire on - * the slow path, by event_try on the fast path). Must be called from + * the slow path, by xco_event_try on the fast path). Must be called from * inside a coroutine driven by rt. */ -static inline uintptr_t xco_await(runtime_t *rt, event_t *e) { +static inline uintptr_t xco_await(xco_runtime_t *rt, xco_event_t *e) { uintptr_t v; - if (event_try(e, &v)) return v; - step_waker_t sw; - step_waker_init(&sw, rt, &xco_self()->base); - event_park(e, &sw.base); + if (xco_event_try(e, &v)) return v; + xco_step_waker_t sw; + xco_step_waker_init(&sw, rt, &xco_self()->base); + xco_event_park(e, &sw.base); xco_suspend(0); - (void)event_try(e, &v); + (void)xco_event_try(e, &v); return v; } @@ -1084,57 +1084,57 @@ static inline uintptr_t xco_await(runtime_t *rt, event_t *e) { * internal select_event is always cleaned up before return. * * The canonical shape for cooperative work inside a task body — pair - * with task_cancel(self) on every blocking await. */ -static inline bool xco_await_or_cancel(runtime_t *rt, event_t *ev, - cancel_t *c, uintptr_t *out) { - select_event_t sel; - select_input_t inputs[2]; - wait_or_cancel(&sel, inputs, ev, c); + * with xco_task_cancel(self) on every blocking await. */ +static inline bool xco_await_or_cancel(xco_runtime_t *rt, xco_event_t *ev, + xco_cancel_t *c, uintptr_t *out) { + xco_select_event_t sel; + xco_select_input_t inputs[2]; + xco_wait_or_cancel(&sel, inputs, ev, c); uintptr_t winner = xco_await(rt, &sel.done.base); - bool ok = (winner == WAIT_OK); - if (ok && out) *out = inputs[WAIT_OK].value; - select_event_deinit(&sel); + bool ok = (winner == XCO_WAIT_OK); + if (ok && out) *out = inputs[XCO_WAIT_OK].value; + xco_select_event_deinit(&sel); return ok; } /* ---- xco-backed task ------------------------------------------------- */ -/* xco specialization of task_t. Bundles the user-visible task handle - * with the xco that runs it; the trampoline calls fn(&xt->task, arg) - * and then task_done with its return value, so wait_or_cancel-style +/* xco specialization of xco_task_t. Bundles the user-visible task handle + * with the xco that runs it; the xco_trampoline calls fn(&xt->task, arg) + * and then xco_task_done with its return value, so xco_wait_or_cancel-style * teardown works without the user wiring anything. * - * The trampoline recovers xt from xco_self() at first entry (container_of + * The xco_trampoline recovers xt from xco_self() at first entry (container_of * on the embedded co), so the first-resume uintptr_t is preserved as * fn's arg under normal xco semantics. Subsequent resumes pass values * to the coroutine in the usual way. * - * Storage shape: caller allocates xco_task_t and a stack. The task_t - * inside is the handle to wait/cancel on; cancel via cancel_set on + * Storage shape: caller allocates xco_cotask_t and a stack. The xco_task_t + * inside is the handle to wait/cancel on; cancel via xco_cancel_set on * &xt->task.cancel and the body is expected to observe it (typically - * by composing wait_or_cancel against task_cancel(&xt->task)). */ + * by composing xco_wait_or_cancel against xco_task_cancel(&xt->task)). */ -typedef uintptr_t (*xco_task_fn)(task_t *t, uintptr_t arg); +typedef uintptr_t (*xco_cotask_fn)(xco_task_t *t, uintptr_t arg); -typedef struct xco_task { - task_t task; +typedef struct xco_cotask { + xco_task_t task; xco_t co; - xco_task_fn fn; -} xco_task_t; + xco_cotask_fn fn; +} xco_cotask_t; /* Initialize xt to run fn on the given stack. After this the embedded - * xco is XSTEP_INIT; drive it with xstep(&xt->co.base, v) or use - * xco_task_spawn for the init-and-first-step convenience. */ -void xco_task_init(xco_task_t *xt, xco_task_fn fn, + * xco is XCO_STEP_INIT; drive it with xco_step(&xt->co.base, v) or use + * xco_cotask_spawn for the init-and-first-step convenience. */ +void xco_cotask_init(xco_cotask_t *xt, xco_cotask_fn fn, void *stack_base, size_t stack_len); /* Convenience: init then first-step in one call. arg is delivered as * fn's argument. */ -static inline xstep_result_t xco_task_spawn(xco_task_t *xt, xco_task_fn fn, +static inline xco_step_result_t xco_cotask_spawn(xco_cotask_t *xt, xco_cotask_fn fn, void *stack_base, size_t stack_len, uintptr_t arg) { - xco_task_init(xt, fn, stack_base, stack_len); - return xstep(&xt->co.base, arg); + xco_cotask_init(xt, fn, stack_base, stack_len); + return xco_step(&xt->co.base, arg); } #endif /* XCO_H */