AxlLoop – Event Loop
Event loop with timer, keyboard, idle, protocol notification, and raw event sources. GLib-inspired main loop with FUSE-style primitives.
Also includes the deferred work queue (AxlDefer) and the publish/subscribe
event bus (AxlSignal), both integrated with the loop.
Headers:
<axl/axl-loop.h>– Event loop core<axl/axl-defer.h>– Deferred work queue (ring buffer)<axl/axl-signal.h>– Publish/subscribe event bus
Overview
UEFI applications are single-threaded and event-driven. The event loop is the central dispatcher: it waits for events (timers, keyboard input, network I/O, custom events) and calls registered callbacks.
Basic Pattern
#include <axl.h>
static bool on_timer(void *data) {
axl_printf("tick\n");
return AXL_SOURCE_CONTINUE; // keep firing
}
static bool on_timeout(void *data) {
axl_loop_quit(data);
return AXL_SOURCE_REMOVE; // one-shot, auto-removed
}
int main(int argc, char **argv) {
AXL_AUTOPTR(AxlLoop) loop = axl_loop_new();
axl_loop_add_timer(loop, 1000, on_timer, NULL); // every 1s
axl_loop_add_timeout(loop, 5000, on_timeout, loop); // quit after 5s
axl_loop_run(loop); // blocks until axl_loop_quit
return 0;
}
Source Types
Source |
Purpose |
Callback returns |
|---|---|---|
Timer |
Repeating at fixed interval |
CONTINUE or REMOVE |
Timeout |
One-shot after delay |
Always REMOVE (auto) |
Idle |
Runs when no other events pending |
CONTINUE or REMOVE |
Keypress |
Console keyboard input |
CONTINUE or REMOVE |
Raw Event |
Any |
CONTINUE or REMOVE |
Protocol Notify |
Fires when a protocol is installed |
CONTINUE or REMOVE |
Run vs. Next+Dispatch
axl_loop_run blocks until axl_loop_quit is called. For manual
control (e.g., FUSE-style drivers), use the step API:
while (running) {
axl_loop_next_event(loop, 100); // wait up to 100ms
axl_loop_dispatch_event(loop); // fire callbacks
// ... do other work between iterations ...
}
AxlDefer
Deferred work queue – schedules a function to run on the next loop iteration. Useful in constrained contexts (protocol notifications, nested callbacks) where complex work isn’t safe.
// Called from a protocol notification (can't do complex work here)
void on_protocol_installed(void *ctx) {
axl_defer(initialize_new_protocol, ctx);
}
// Runs safely on the next main loop tick
void initialize_new_protocol(void *ctx) {
// Boot Services calls are safe here
locate_and_configure(ctx);
}
The queue is a fixed-capacity ring buffer with no dynamic allocation
in the hot path. axl_defer_cancel can remove pending work before
it fires.
AxlSignal
Publish/subscribe event bus for decoupling modules. Modules emit named signals; other modules subscribe with callbacks. Delivery is deferred (via AxlDefer) so handlers run in a safe context.
// Producer (network module)
axl_signal_emit("ip-changed", &new_ip);
// Consumer 1 (splash screen)
axl_signal_connect("ip-changed", update_splash_ip, NULL);
// Consumer 2 (REST API)
axl_signal_connect("ip-changed", update_api_endpoint, NULL);
// Adding a third consumer requires zero changes to the producer.
Signals are auto-created on first connect or emit.
axl_signal_disconnect removes a specific subscription.
API Reference
AxlLoop
Defines
-
AXL_SOURCE_CONTINUE
Return from callback to keep the source active.
-
AXL_SOURCE_REMOVE
Return from callback to remove the source from the loop.
Typedefs
-
typedef void *AxlEvent
Opaque event handle for raw event sources.
-
typedef bool (*AxlLoopCallback)(void *data)
AxlLoopCallback:
Generic event callback. Return AXL_SOURCE_CONTINUE to keep the source active, or AXL_SOURCE_REMOVE to remove it. To quit the loop, call axl_loop_quit() from inside the callback.
-
typedef bool (*AxlKeyCallback)(AxlInputKey key, void *data)
AxlKeyCallback:
Key press callback. Return AXL_SOURCE_CONTINUE to keep the source active, or AXL_SOURCE_REMOVE to remove it. To quit the loop, call axl_loop_quit() from inside the callback.
Enums
-
enum AxlSourceType
AxlSourceType:
Identifies the kind of event source in the loop.
Values:
-
enumerator AXL_SOURCE_TIMER
repeating timer
-
enumerator AXL_SOURCE_TIMEOUT
one-shot timer (auto-removed after firing)
-
enumerator AXL_SOURCE_KEYPRESS
console keyboard input
-
enumerator AXL_SOURCE_IDLE
fires every iteration before blocking wait
-
enumerator AXL_SOURCE_PROTOCOL
UEFI protocol install notification.
-
enumerator AXL_SOURCE_EVENT
raw EFI event handle (caller-owned)
-
enumerator AXL_SOURCE_TIMER
Functions
-
void axl_loop_free(AxlLoop *loop)
Free an event loop and close all internal events.
- Parameters:
loop – loop to free (NULL-safe)
-
void axl_loop_quit(AxlLoop *loop)
Signal the loop to quit. Safe to call from callbacks.
- Parameters:
loop – loop to quit
-
bool axl_loop_is_running(AxlLoop *loop)
Check if the loop is running.
- Parameters:
loop – loop to check
- Returns:
true if running and not quit-requested.
-
void axl_loop_add_cleanup(AxlLoop *loop, AxlLoopCallback cb, void *data)
Add a cleanup callback fired on exit (FIFO order).
- Parameters:
loop – loop
cb – callback fired on exit (FIFO order)
data – opaque data
-
int axl_loop_next_event(AxlLoop *loop, bool blocking)
Wait for (or check) the next event.
- Parameters:
loop – event loop
blocking – true to block until event, false to return immediately
- Returns:
0 if event pending (call axl_loop_dispatch_event), 1 if non-blocking and nothing ready, -1 if Ctrl-C detected (loop should exit).
-
void axl_loop_dispatch_event(AxlLoop *loop)
Dispatch the pending event from the last axl_loop_next_event call.
- Parameters:
loop – event loop
-
int axl_loop_dispatch(AxlLoop *loop, bool blocking)
Single iteration: axl_loop_next_event + axl_loop_dispatch_event.
- Parameters:
loop – event loop
blocking – true to block, false for non-blocking
- Returns:
0 on event dispatched, 1 if not ready, -1 on Ctrl-C.
-
int axl_loop_run(AxlLoop *loop)
Run the event loop until quit. Fires cleanup callbacks on exit.
- Parameters:
loop – event loop
- Returns:
0 on normal exit, -1 on Ctrl-C.
-
size_t axl_loop_add_timer(AxlLoop *loop, size_t interval_ms, AxlLoopCallback cb, void *data)
Add a repeating timer.
- Parameters:
loop – event loop
interval_ms – timer interval in milliseconds
cb – callback fired each interval
data – opaque data
- Returns:
source ID for axl_loop_remove_source, or 0 on failure.
-
size_t axl_loop_add_timeout(AxlLoop *loop, size_t delay_ms, AxlLoopCallback cb, void *data)
Add a one-shot timeout (auto-removed after firing).
- Parameters:
loop – event loop
delay_ms – timeout delay in milliseconds
cb – callback fired on timeout (one-shot, auto-removed)
data – opaque data
- Returns:
source ID for axl_loop_remove_source, or 0 on failure.
-
size_t axl_loop_add_key_press(AxlLoop *loop, AxlKeyCallback cb, void *data)
Add a key press handler.
- Parameters:
loop – event loop
cb – key press callback
data – opaque data
- Returns:
source ID for axl_loop_remove_source, or 0 on failure.
-
size_t axl_loop_add_idle(AxlLoop *loop, AxlLoopCallback cb, void *data)
Add an idle callback (fired every iteration before wait).
- Parameters:
loop – event loop
cb – idle callback (fired every iteration before wait)
data – opaque data
- Returns:
source ID for axl_loop_remove_source, or 0 on failure.
-
size_t axl_loop_add_protocol_notify(AxlLoop *loop, void *guid, AxlLoopCallback cb, void *data)
Add a protocol install notification.
- Parameters:
loop – event loop
guid – protocol GUID to watch (void* to avoid EFI_GUID in header)
cb – callback on protocol install
data – opaque data
- Returns:
source ID for axl_loop_remove_source, or 0 on failure.
-
size_t axl_loop_add_event(AxlLoop *loop, AxlEvent event, AxlLoopCallback cb, void *data)
Add a raw event handle to the loop.
Fires cb when the event is signaled. The caller owns the event — the loop does NOT close it on removal. Use this to integrate TCP completion tokens, custom protocol events, or any EFI_EVENT into the main loop without polling.
- Parameters:
loop – event loop
event – event handle from axl_event_create
cb – callback when event is signaled
data – opaque data
- Returns:
source ID for axl_loop_remove_source, or 0 on failure.
-
void axl_loop_remove_source(AxlLoop *loop, size_t source_id)
Remove an event source by ID.
- Parameters:
loop – event loop
source_id – ID returned by axl_loop_add_*
-
int axl_event_create(AxlEvent *event)
Create a raw event handle.
Use with axl_loop_add_event to integrate custom events into the loop. Caller owns the event — free with axl_event_close.
- Parameters:
event – [out] receives event handle
- Returns:
0 on success, -1 on error.
-
struct AxlInputKey
- #include <axl-loop.h>
Keyboard input. Mirrors UEFI EFI_INPUT_KEY layout.
AxlDefer
Typedefs
-
typedef void (*AxlDeferCallback)(void *data)
AxlDeferCallback:
Deferred work function. Runs on the BSP main loop thread.
Functions
-
void axl_defer_init(void)
Initialize (or reset) the deferred work queue.
Clears all pending work and resets internal state. Call once before using axl_defer(). The loop drains the queue automatically at the start of each iteration. Safe to call again to reset between test runs or on reinit.
-
size_t axl_defer(AxlDeferCallback fn, void *data)
Schedule deferred work for the next loop tick.
Safe to call from protocol notifications, nested callbacks, or any context where complex work should not run immediately.
- Parameters:
fn – work function
data – opaque data passed to fn
- Returns:
handle for axl_defer_cancel(), or 0 if the queue is full.
-
bool axl_defer_cancel(size_t handle)
Cancel pending deferred work before it fires.
No-op if the handle is invalid or already fired.
- Parameters:
handle – handle from axl_defer()
- Returns:
true if the work was cancelled, false if already fired or invalid.
-
void axl_defer_drain(void)
Drain all pending deferred work.
Called automatically by axl_loop_next_event(). Manual loop users (FUSE-style next_event/dispatch_event) get this for free. Only call explicitly if bypassing the loop entirely.
AxlSignal
Typedefs
-
typedef void (*AxlSignalCallback)(void *event_data, void *user_data)
AxlSignalCallback:
Signal handler. Runs on the BSP main loop thread (via AxlDefer).
Functions
-
bool axl_signal_new(const char *name)
Explicitly register a named signal.
Optional — signals are auto-created on first connect or emit. Use this to reserve a signal slot early or to validate the name.
- Parameters:
name – signal name (pointer stored, not copied)
- Returns:
true if registered (or already exists), false if table full.
-
void axl_signal_reset(void)
Reset the signal system — free all subscribers and signals.
Call on shutdown or between test runs to release resources.
-
size_t axl_signal_connect(const char *name, AxlSignalCallback cb, void *data)
Subscribe to a named signal.
The callback fires (via AxlDefer) each time the signal is emitted. Auto-creates the signal if it doesn’t exist yet.
- Parameters:
name – signal name
cb – callback (fires on emit, via AxlDefer)
data – opaque data passed to cb
- Returns:
handle for axl_signal_disconnect, or 0 on failure.
-
bool axl_signal_disconnect(size_t handle)
Unsubscribe from a signal.
- Parameters:
handle – handle from axl_signal_connect
- Returns:
true if disconnected, false if handle invalid or already removed.
-
bool axl_signal_emit(const char *name, void *event_data)
Emit a named signal.
Schedules all subscribers’ callbacks via axl_defer(). Safe to call from constrained contexts (protocol notifications, nested callbacks).
The caller must ensure event_data remains valid until the next loop tick (when deferred callbacks fire).
- Parameters:
name – signal name
event_data – data passed to all subscribers (may be NULL)
- Returns:
true if signal exists and had subscribers, false otherwise.