Trait LocalWake

trait LocalWake

An analogous trait to Wake but used to construct a LocalWaker.

This API works in exactly the same way as Wake, except that it uses an Rc instead of an Arc, and the result is a LocalWaker instead of a Waker.

The benefits of using LocalWaker over Waker are that it allows the local waker to hold data that does not implement Send and Sync. Additionally, it saves calls to Arc::clone, which requires atomic synchronization.

Examples

This is a simplified example of a spawn and a block_on function. The spawn function is used to push new tasks onto the run queue, while the block on function will remove them and poll them. When a task is woken, it will put itself back on the run queue to be polled by the executor.

Note: This example trades correctness for simplicity. A real world example would interleave poll calls with calls to an io reactor to wait for events instead of spinning on a loop.

#![feature(local_waker)]
use std::task::{LocalWake, ContextBuilder, LocalWaker, Waker};
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::VecDeque;


thread_local! {
    // A queue containing all tasks ready to do progress
    static RUN_QUEUE: RefCell<VecDeque<Rc<Task>>> = RefCell::default();
}

type BoxedFuture = Pin<Box<dyn Future<Output = ()>>>;

struct Task(RefCell<BoxedFuture>);

impl LocalWake for Task {
    fn wake(self: Rc<Self>) {
        RUN_QUEUE.with_borrow_mut(|queue| {
            queue.push_back(self)
        })
    }
}

fn spawn<F>(future: F)
where
    F: Future<Output=()> + 'static + Send + Sync
{
    let task = RefCell::new(Box::pin(future));
    RUN_QUEUE.with_borrow_mut(|queue| {
        queue.push_back(Rc::new(Task(task)));
    });
}

fn block_on<F>(future: F)
where
    F: Future<Output=()> + 'static + Sync + Send
{
    spawn(future);
    loop {
        let Some(task) = RUN_QUEUE.with_borrow_mut(|queue| queue.pop_front()) else {
            // we exit, since there are no more tasks remaining on the queue
            return;
        };

        // cast the Rc<Task> into a `LocalWaker`
        let local_waker: LocalWaker = task.clone().into();
        // Build the context using `ContextBuilder`
        let mut cx = ContextBuilder::from_waker(Waker::noop())
            .local_waker(&local_waker)
            .build();

        // Poll the task
        let _ = task.0
            .borrow_mut()
            .as_mut()
            .poll(&mut cx);
    }
}

block_on(async {
    println!("hello world");
});

Required Methods

fn wake(self: Rc<Self>)

Wake this task.

Provided Methods

fn wake_by_ref(self: &Rc<Self>)

Wake this task without consuming the local waker.

If an executor supports a cheaper way to wake without consuming the waker, it should override this method. By default, it clones the Rc and calls wake on the clone.