Notifications
A notification is an asynchronous signaling primitive. It stores a 64-bit word of pending signal bits. Signaling a notification performs an atomic OR on the bits and optionally wakes a waiting thread. Notifications never block the signaler.
Notification Structure
#[repr(C)]
pub struct Notification {
pub header: KernelObject,
lock: AtomicU8, // per-notification spinlock
pub bits: AtomicU64, // pending signal bits
waiting: *mut Tcb, // thread blocked in Wait (at most one)
pub bound_tcb: *mut Tcb, // TCB with this notification bound
}
Each notification has its own spinlock with the same IRQ-disable pattern as endpoints.
Operations
Signal (syscall 5)
Signal a notification by OR-ing bits into the pending word. Never blocks.
-
Acquire
ntfn_lock. -
bits.fetch_or(signal_bits, SeqCst). -
If a thread is directly waiting (
waiting != null): clear the waiter, wake it. -
Otherwise, if a bound TCB exists and is blocked on an endpoint recv: wake it via the bound notification mechanism (see Bound Notifications).
-
Release
ntfn_lock.
The signal caller’s rights must include SEND.
Wait (syscall 6)
Wait for notification bits. Blocks if no bits are pending.
-
Atomically read and clear
bits. -
If non-zero: return the bits to the caller immediately.
-
If zero: set
waiting = current_tcb, block the thread, release lock, reschedule.
When the thread is eventually woken by a Signal, it reads and clears the accumulated bits.
The waiter’s rights must include RECV.
Bound Notifications
A notification can be bound to a TCB via TCB_BIND_NOTIFICATION (invoke label 0x49).
This creates a bidirectional link: notification.bound_tcb <→ tcb.bound_notification.
When a signal arrives on a bound notification and the bound TCB is blocked on an endpoint Recv, the kernel wakes the thread and sets woken_by_notification = true.
The thread’s recv operation returns with the notification bits instead of an IPC message.
This allows a server to wait on both its endpoint (for client requests) and its notification (for IRQ or async events) in a single blocking call.
Combined Wait Behavior
During Recv on an endpoint:
-
The kernel first checks the bound notification for pending bits (notification pre-check).
-
If bits are pending, they are consumed and returned immediately — the thread never enters the endpoint’s recv queue.
-
If no bits are pending, the thread blocks on the endpoint. A subsequent
Signalon the bound notification wakes the thread from the endpoint queue.
Notification Dispatcher
If the bound TCB has a notification_dispatcher address set, signals can also interrupt Call-blocked threads:
-
A thread blocked in
ReplyWait(waiting for a server reply afterCall) is woken and redirected to the notification dispatcher. -
A thread blocked in
CallSendBlocked(waiting in an endpoint send queue duringCall) is removed from the queue and woken.
This enables a thread to handle urgent notifications (IRQs, timer events) even while waiting for a server reply.
NotifReturn (syscall 27)
NotifReturn allows a thread to return from a notification dispatcher back to the interrupted IPC operation.
It is the counterpart to the notification dispatcher mechanism: the dispatcher handles the event, then calls NotifReturn to resume the original blocking IPC.
IRQ Delivery
Hardware interrupts are delivered to userspace through notifications via IrqHandler objects.
IrqHandler Structure
#[repr(C)]
pub struct IrqHandler {
pub header: KernelObject,
pub irq_num: u32,
pub notification: AtomicPtr<Notification>,
pub acknowledged: AtomicBool,
pub active: bool,
pub level_triggered: bool,
pub next: *mut IrqHandler, // shared IRQ linked list
}
IRQ Delivery Path
-
When a hardware interrupt fires, the architecture-specific handler calls
dispatch_irq(irq_num). -
dispatch_irqacquiresIRQ_LOCKand walks the linked list of handlers registered for that IRQ. -
For each handler that is acknowledged and has a bound notification, the handler’s
acknowledgedflag is cleared and the notification is signaled with1 << (irq_num % 64). -
The userspace driver, waiting on the notification via
Wait, is woken and handles the interrupt. -
The driver acknowledges the interrupt by invoking
IRQ_ACK(label0x61) on the IRQ handler capability.
Shared IRQs
Multiple IrqHandler objects can be registered for the same IRQ line.
They form a singly-linked list per IRQ number.
dispatch_irq signals every acknowledged handler on the chain.
Each handler is independently acknowledged.
IRQ Handler Operations
| Label | Name | Description |
|---|---|---|
|
|
Create a new IRQ handler for a given IRQ number (requires IRQ control capability). |
|
|
Acknowledge the interrupt, re-enabling delivery. |
|
|
Bind a notification to this IRQ handler. |
|
|
Unbind the notification. |
|
|
Create a device untyped capability for an MMIO region. |
Futex (syscall 18)
The futex syscall provides a userspace synchronization primitive. Three operations:
FUTEX_WAIT-
If the value at the given address equals the expected value, block the calling thread. Used to implement userspace mutexes and condition variables.
FUTEX_WAKE-
Wake up to N threads blocked on the given address.
FUTEX_WAIT_TIMEOUT-
Block the calling thread until the futex value changes or the specified timeout expires. Returns
TRONA_TIMEOUTif the timeout elapses.
Futex waiters are stored in a global hash table protected by FUTEX_LOCK.
Error Conditions and Edge Cases
Notification Destruction While Thread Is Waiting
When the last capability to a notification is deleted, Notification::cleanup() wakes the waiting thread (if any).
The woken thread sees zero bits (the notification word was cleared).
If a TCB has this notification bound (bound_tcb), the binding is cleared during cleanup.
Subsequent Recv on endpoints no longer perform the notification pre-check.
Signal During Lock-Order Inversion Window
Notification::signal() must release ntfn_lock before acquiring endpoint.lock when waking a bound TCB blocked on an endpoint.
During this gap, the bound TCB’s state may change (another CPU wakes it, or it times out).
The code re-validates the TCB’s state after reacquiring the endpoint lock (TOCTOU guard).
If the TCB is no longer blocked, the signal bits remain in the notification word for the next Recv pre-check to consume.
Bound Notification + Notification Dispatcher Interaction
When a signal arrives on a bound notification and the bound TCB is in CallSendBlocked or ReplyWait:
-
If the TCB has a
notification_dispatcherset: the TCB is woken and redirected to the dispatcher. PIP donations are cleaned up. -
If the TCB does not have a dispatcher: the signal bits accumulate in the notification word. The TCB is not interrupted.
This distinction prevents servers that use bound notifications for IRQ delivery (without a dispatcher) from having their outgoing Call operations interrupted.
Futex Spurious Wakeup
FUTEX_WAIT can return without the value at the address having changed (spurious wakeup).
Userspace must always re-check the condition in a loop:
while *addr == expected {
futex_wait(addr, expected)
}
Futex Address Aliasing
The futex hash table keys on the virtual address within a single VSpace. Two processes sharing physical memory (via shared MO) at different virtual addresses see different futex keys. Cross-process futex requires mapping the shared region at the same virtual address in both VSpaces.
Related Pages
-
Endpoints — synchronous IPC
-
IPC Fastpath — assembly-optimized paths for Call and ReplyRecv
-
Design Patterns — combined server and IRQ driver patterns
-
Threads — TCB bound_notification field, notification dispatcher