Event Primitives — AxlEvent, AxlCancellable, AxlWait
The foundational synchronization primitives. All three compose: an
AxlCancellable is an AxlEvent with stop-token semantics; the
wait helpers drive a throwaway AxlLoop until an event fires, a
condition holds, a timeout elapses, or Ctrl-C is received.
AxlEvent— one-shot latch wrapping a UEFI event. Replaces the olderAxlCompletion(same mechanical behavior, UEFI-native name).AxlCancellable— typed stop token shared across async ops; cancel it once and every op observing it aborts withAXL_CANCELLED.AxlWait— interruptible wait helpers (axl_wait_for,axl_wait_for_flag,axl_wait_ms, …) built onAxlLoop.
A note on naming: “event” appears three times in AXL docs — the
event loop (the dispatcher), an event source (a thing
registered with the loop), and AxlEvent (one kind of source).
UEFI carries the same overload; an AxlEvent is a one-shot latch
backed by a UEFI event, and the event loop dispatches them.
Headers:
<axl/axl-event.h>,
<axl/axl-cancellable.h>,
<axl/axl-wait.h>.
API Reference
AxlEvent
Defines
-
axl_event_new()
Captures the caller’s file/line for leak reporting via the tier-1 resource registry. See docs/AXL-Lifecycle.md §4.2.1.
Typedefs
-
typedef struct AxlEvent AxlEvent
axl-event.h:
Foundational one-shot latch. Wraps a UEFI event with signalled / reset state. The building block for producer-waiter rendezvous across the library.
AxlCancellableis a typed contract on top: same mechanical behavior, stop-token semantics.Typical use: an async callback signals the event; the main thread waits for it. Internally wraps a UEFI event — waits are driven by AxlLoop, so they idle the CPU (not busy-wait) and are interrupted by Ctrl-C.
Ordering: AXL targets UEFI BSP, which is single-threaded and cooperative. Event ops are safe from any context that single thread naturally reaches (protocol notifications, nested callbacks, interrupt-like handlers). No cross-core ordering guarantee between signal and is_set on platforms with real parallelism — use an explicit AP-to-BSP channel (
AxlAsync) if you need one.AxlEvent *e = axl_event_new(); start_async_op(on_done, e); if (axl_event_wait_timeout(e, NULL, 5000000) != 0) { // -1 timeout, AXL_CANCELLED on Ctrl-C / cancel } axl_event_free(e); static void on_done(void *user) { axl_event_signal(user); }
Relationship to the event loop: the “event loop” (
AxlLoop) dispatches events — timer expirations, key presses, raw UEFI event signals. AnAxlEventis one such event (a one-shot latch). The name is overloaded but consistent: AXL is a thin layer over UEFI events, and the loop is the dispatcher.
-
typedef struct AxlCancellable AxlCancellable
-
typedef void *AxlEventHandle
AxlEventHandle:
Raw UEFI event handle (
EFI_EVENT). Used where firmware owns the event — protocol completion tokens, protocol-notify events. For AXL-managed events useAxlEventand passaxl_event_handle(e)where a handle is required (e.g.,axl_loop_add_event).
Functions
-
AxlEvent *axl_event_new_impl(const char *file, int line)
Create a new, unsignalled event.
- Returns:
new AxlEvent, or NULL on failure. Free with axl_event_free().
-
void axl_event_signal(AxlEvent *e)
Signal the event. Idempotent, NULL-safe.
Safe from any context — protocol notifications, nested callbacks, interrupt-like handlers.
- Parameters:
e – event (NULL-safe)
-
void axl_event_reset(AxlEvent *e)
Reset the event to an unsignalled state. NULL-safe.
Drops any pending signal so the same event can be reused across multiple wait cycles.
- Parameters:
e – event (NULL-safe)
-
bool axl_event_is_set(const AxlEvent *e)
Fast check: is this event currently signalled?
Reads an internal flag without driving the loop. Transitions:
axl_event_signal(e)→ is_set becomes trueaxl_event_reset(e)→ is_set becomes falsesuccessful
axl_event_wait[_timeout]→ is_set becomes false (the wait consumed the signal via CheckEvent in the loop dispatch; the flag mirrors the backend state)
For the full wait-for-signal behavior with timeout and cancel support, use axl_event_wait_timeout().
- Parameters:
e – event (NULL-safe)
- Returns:
true between signal and the next reset / successful wait, else false. Returns false for NULL.
-
AxlEventHandle axl_event_handle(const AxlEvent *e)
Get the raw UEFI event handle wrapped by this AxlEvent.
Used when registering the event with the loop via axl_loop_add_event, which takes a handle so the same entry point serves AXL-managed events and firmware-owned ones alike.
- Parameters:
e – event (NULL-safe, returns NULL)
- Returns:
the wrapped handle, or NULL for NULL / uninitialized.
-
AxlStatus axl_event_wait(AxlEvent *e, AxlCancellable *cancel)
Wait indefinitely for the event to be signalled.
The CPU idles between events. Returns early on Ctrl-C or a signalled cancellable. Equivalent to axl_event_wait_timeout(e, cancel, 0).
- Parameters:
e – event
cancel – optional cancel token (NULL = only Ctrl-C)
- Returns:
AXL_OK on signal, AXL_ERR on invalid arg, AXL_CANCELLED on Ctrl-C or cancel.
-
AxlStatus axl_event_wait_timeout(AxlEvent *e, AxlCancellable *cancel, uint64_t timeout_us)
Wait for the event with a timeout.
The CPU idles between events. A timeout_us of 0 means wait forever. Returns early on Ctrl-C or a signalled cancellable.
- Parameters:
e – event
cancel – optional cancel token
timeout_us – timeout in microseconds (0 = forever)
- Returns:
AXL_OK on signal, AXL_TIMEOUT on deadline, AXL_ERR on invalid arg, AXL_CANCELLED on Ctrl-C or cancel.
AxlCancellable
Defines
-
axl_cancellable_new()
Captures the caller’s file/line for leak reporting via the tier-1 resource registry. See docs/AXL-Lifecycle.md §4.2.1.
Typedefs
-
typedef struct AxlCancellable AxlCancellable
axl-cancellable.h:
Generic cancellation primitive for async operations. Parallels GLib’s GCancellable, mapped onto AXL’s single-threaded event loop.
A cancellable is an optional “stop token” that any async operation can accept. The caller holds it; the op observes it. Signaling
axl_cancellable_cancelaborts every op currently referencing it.Typical use:
AxlCancellable *cancel = axl_cancellable_new(); axl_tcp_connect_async(host, port, loop, cancel, on_connected, ctx); axl_loop_add_timeout(loop, 5000, cancel_on_timeout, cancel); axl_loop_run(loop); axl_cancellable_free(cancel); static bool cancel_on_timeout(void *data) { axl_cancellable_cancel(data); return AXL_SOURCE_REMOVE; } // The op's callback fires exactly once, with status either AXL_OK // on success, or AXL_CANCELLED if the cancellable fired first. static void on_connected(AxlTcp *sock, AxlStatus status, void *ctx) { if (status == AXL_CANCELLED) { axl_tcp_close(sock); return; } // sock is ready to use }
Group cancellation — one cancellable covers many ops:
axl_tcp_connect_async(h, p, loop, app->shutdown, cb1, c1); axl_http_get_async (u, loop, app->shutdown, cb2, c2); // Cancels both ops on app shutdown — each callback fires with // AXL_CANCELLED. axl_cancellable_cancel(app->shutdown);
Ownership rule: the cancellable must outlive every async op that observes it. Same discipline as AxlLoop outliving its sources.
Functions
-
AxlCancellable *axl_cancellable_new_impl(const char *file, int line)
Create a new, unsignalled cancellable.
- Returns:
new AxlCancellable, or NULL on failure. Free with axl_cancellable_free().
-
void axl_cancellable_free(AxlCancellable *c)
Free a cancellable. NULL-safe.
Must only be called after every async op that observes this cancellable has completed (via its callback) or is otherwise no longer holding a reference. Freeing while an op still references it results in a dangling event handle.
- Parameters:
c – cancellable (NULL-safe)
-
void axl_cancellable_cancel(AxlCancellable *c)
Cancel every async op currently observing this cancellable.
Idempotent — calling more than once is safe and has no additional effect. Safe to call from any context (protocol notifications, nested callbacks). NULL-safe.
- Parameters:
c – cancellable (NULL-safe)
-
bool axl_cancellable_is_cancelled(const AxlCancellable *c)
Check whether the cancellable has been signalled.
- Parameters:
c – cancellable (NULL-safe)
- Returns:
true if axl_cancellable_cancel() was called, else false. Returns false for NULL.
-
void axl_cancellable_reset(AxlCancellable *c)
Reset the cancellable to an unsignalled state. NULL-safe.
Drops any pending cancel signal so the same cancellable can be reused for a fresh batch of async ops. Only call once all ops that might have observed the prior signal have completed.
- Parameters:
c – cancellable (NULL-safe)
AxlWait
Typedefs
-
typedef struct AxlCancellable AxlCancellable
axl-wait.h:
Interruptible wait helpers built on AxlLoop.
These replace the common “busy-poll with axl_backend_stall” idiom with event-driven waits that idle the CPU between checks and return early on Ctrl-C. Every function returns AxlStatus:
AXL_OK — condition met / elapsed AXL_TIMEOUT — deadline elapsed before condition AXL_ERR — invalid arg / internal failure AXL_CANCELLED — interrupted (Ctrl-C / shell break / cancel token)
// Wait for a hardware status flag, CPU idle between checks: if (axl_wait_for_word(&mmio->status, 0, NULL, 500000) != AXL_OK) { return AXL_ERR; } // Interruptible sleep: (void)axl_wait_ms(NULL, 100);
-
typedef bool (*AxlCondFn)(void *ctx)
AxlCondFn:
Condition predicate. Returns true when the wait should end.
-
typedef void (*AxlTickFn)(void *ctx)
AxlTickFn:
Periodic side-effect called between waits. Typical use is to drive a UEFI protocol state machine forward (e.g. call protocol->Poll) so the condition can become true.
Functions
-
void axl_sleep(uint64_t seconds)
Sleep for the specified number of seconds. CPU idles; Ctrl-C returns early.
-
void axl_msleep(uint64_t milliseconds)
Sleep for the specified number of milliseconds. CPU idles; Ctrl-C returns early.
-
void axl_usleep(uint64_t microseconds)
Sleep for the specified number of microseconds (rounded up to ms granularity). CPU idles; Ctrl-C returns early.
-
AxlStatus axl_wait_for_flag(volatile const bool *flag, AxlCancellable *cancel, uint64_t timeout_us)
Wait until *flag becomes true, with optional cancel + timeout.
CPU idles between 1ms checks. Returns 0 immediately if *flag is already true, or AXL_CANCELLED if
cancelwas already signalled.- Parameters:
flag – flag to observe
cancel – optional cancel token (NULL = only Ctrl-C)
timeout_us – timeout in microseconds (0 = forever)
- Returns:
AXL_OK on true, AXL_TIMEOUT on deadline, AXL_ERR on invalid arg, AXL_CANCELLED on Ctrl-C or an observed cancellable.
-
AxlStatus axl_wait_for_word(volatile const uint64_t *word, uint64_t not_ready_value, AxlCancellable *cancel, uint64_t timeout_us)
Wait until *word stops matching not_ready_value.
Covers UEFI completion-token Status polls, DMA flags, and any “keep checking this memory word until it changes” pattern. CPU idles between 1ms checks.
- Parameters:
word – memory word to observe
not_ready_value – value that means “keep waiting”
cancel – optional cancel token
timeout_us – timeout in microseconds (0 = forever)
- Returns:
AXL_OK on change, AXL_TIMEOUT on deadline, AXL_ERR on invalid arg, AXL_CANCELLED on Ctrl-C or an observed cancellable.
-
AxlStatus axl_wait_ms(AxlCancellable *cancel, uint64_t ms)
Interruptible sleep with cancellable support.
The long form of axl_msleep — use this when you need to inspect the return code (Ctrl-C vs elapsed) or pass a shared AxlCancellable. The CPU idles for the duration.
Parameter order note: the rest of the wait family places cancel between the subject and the timeout. Sleep has no subject, so cancel comes first. The relative position (cancel immediately before the duration/timeout) is consistent with the other helpers.
- Parameters:
cancel – optional cancel token (NULL = only Ctrl-C)
ms – milliseconds to sleep (0 returns immediately)
- Returns:
AXL_OK on elapsed, AXL_CANCELLED on Ctrl-C or cancel.
-
AxlStatus axl_wait_for(AxlCondFn cond_fn, void *cond_ctx, AxlCancellable *cancel, uint64_t timeout_us)
Wait until cond_fn returns true, with timeout + optional cancel.
cond_fn is evaluated immediately and then every 1ms. CPU idles between evaluations.
- Parameters:
cond_fn – predicate
cond_ctx – opaque context passed to cond_fn
cancel – optional cancel token
timeout_us – timeout in microseconds (0 = forever)
- Returns:
AXL_OK on cond_fn true, AXL_TIMEOUT on deadline, AXL_ERR on invalid arg, AXL_CANCELLED on Ctrl-C or cancel.
-
AxlStatus axl_wait_for_with_tick(AxlCondFn cond_fn, void *cond_ctx, AxlTickFn tick_fn, void *tick_ctx, uint64_t tick_us, AxlCancellable *cancel, uint64_t timeout_us)
Wait until cond_fn returns true, running tick_fn each period.
cond_fn is evaluated immediately and then each time tick_fn runs. Use this form when the condition only becomes true after an external state machine is advanced (e.g. calling protocol->Poll on a UEFI driver).
- Parameters:
cond_fn – predicate (required)
cond_ctx – opaque context for cond_fn
tick_fn – periodic side-effect (may be NULL)
tick_ctx – opaque context for tick_fn
tick_us – tick period in microseconds (minimum 1ms)
cancel – optional cancel token
timeout_us – timeout in microseconds (0 = forever)
- Returns:
AXL_OK on cond_fn true, AXL_TIMEOUT on deadline, AXL_ERR on invalid arg, AXL_CANCELLED on Ctrl-C or cancel.