commit 192570edb8035ef5a3233906c46490b64b1a013d
parent e2e8f4aa33773708c90d37689849c56cf3be6c24
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 5 May 2026 11:31:45 -0700
consistent xco_ prefix
Diffstat:
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 */