Waker

Earlier, we saw that when RawTask::run is called, run creates a waker which is used to poll the user-provided Future. In this section, we look at how the Waker instance is created.

To create a waker in Rust, we need to pass a RawWakerVTable to the Waker constructor.

Here is the vtable for the task:

impl<F, R, S> RawTask<F, R, S>
where
    F: Future<Output = R>,
    S: Fn(Task),
{
    const RAW_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
        Self::clone_waker,
        Self::wake,
        Self::wake_by_ref,
        Self::drop_waker,
    );

The most important method here is the wake method, which is invoked when Waker::wake is called.

The Waker::wake() simply reschedules the task by pushing it onto the TaskQueue. Here is the implementation of wake and wake_by_ref:

#![allow(unused)]
fn main() {
unsafe fn wake(ptr: *const ()) {
    Self::wake_by_ref(ptr);
    Self::drop_waker(ptr);
}

/// Wakes a waker. Ptr is the raw task.
unsafe fn wake_by_ref(ptr: *const ()) {
    let raw = Self::from_ptr(ptr);
	  let state = (*raw.header).state;
	
	  // If the task is completed or closed, it can't be woken up.
	  if state & (COMPLETED | CLOSED) == 0 {
	      // If the task is already scheduled do nothing.
	      if state & SCHEDULED == 0 {
	          // Mark the task as scheduled.
	          (*(raw.header as *mut Header)).state = state | SCHEDULED;
	          if state & RUNNING == 0 {
	              // Schedule the task.
	              Self::schedule(ptr);
	          }
	      }
	  }
}
}

The schedule method is passed to the task when the task is created and it looks something like:

let schedule = move |task| {
    let task_queue = tq.upgrade();
    task_queue.local_queue.push(task);
};
create_task(executor_id, future, schedule)

Finally, here is the code that actually creates the waker which is used to poll the user-defined future.

#![allow(unused)]
fn main() {
let waker = ManuallyDrop::new(Waker::from_raw(RawWaker::new(ptr, &Self::RAW_WAKER_VTABLE)));
let cx = &mut Context::from_waker(&waker);

let poll = <F as Future>::poll(Pin::new_unchecked(&mut *raw.future), cx);
}

Code References

To check out my toy implementation or Glommio’s implementation, check out:

My Toy Implementation

Glommio