Course contentsShow
← Rust
Lesson 1300 of 1,91011. Asynchronous ProgrammingFree lesson

Advanced Waker Management

Implement custom Waker logic to wake specific tasks based on external hardware interrupt signals.

In this lesson, you will learn how to bridge the gap between low-level hardware interrupts and the Rust async runtime by manually managing Waker instances.

When working with bare-metal or systems-level code, your async tasks often wait for external events—like a GPIO pin toggle or a UART byte arrival—rather than just waiting on timers. Since the hardware interrupt handler operates in a restricted context, it cannot directly "poll" a task. Instead, you must store the task’s Waker in a globally accessible location that the interrupt service routine (ISR) can trigger.

The Mechanism

The Waker is a handle that tells the executor, "The task associated with this waker is ready to make progress." To implement this, you need a shared, thread-safe container (like an AtomicCell or a Mutex) that holds an Option<Waker>.

When your Future is polled and finds the hardware isn't ready, it performs these steps:

  1. It registers the current cx.waker() into your shared storage.
  2. It returns Poll::Pending.

When the hardware triggers an interrupt, the ISR accesses that shared storage, calls .wake() on the stored Waker, and clears the storage.

// Simplified structure for an interrupt-driven driver
static GLOBAL_WAKER: Mutex<Option<Waker>> = Mutex::new(None);

fn hardware_isr() {
    // Inside the interrupt handler, wake the task
    if let Some(waker) = GLOBAL_WAKER.lock().take() {
        waker.wake();
    }
}

// Inside the poll() method of your custom Future:
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
    if hardware_ready() {
        Poll::Ready(data)
    } else {
        // Register the waker so the ISR can find it
        *GLOBAL_WAKER.lock() = Some(cx.waker().clone());
        Poll::Pending
    }
}

Important Considerations

Because the Waker is essentially a heap-allocated pointer to the executor's task logic, ensure your shared storage is correctly synchronized. If an interrupt occurs after the poll method determines the hardware is ready but before it finishes returning Poll::Ready, you might lose a wake signal. In production systems, this is typically solved by using atomic state flags to ensure the waker is only cleared or stored when the state is strictly Pending.

Key Takeaway: By manually storing and invoking a Waker within an interrupt handler, you provide the bridge necessary to turn asynchronous hardware events into task-level progress notifications.