Skip to main content

shuttle_engine/runtime/task/
waker.rs

1use crate::runtime::execution::ExecutionState;
2use crate::runtime::task::TaskId;
3use std::task::{RawWaker, RawWakerVTable, Waker};
4
5// Safety: the `RawWaker` interface is unsafe because it requires manually enforcing resource
6// management contracts on each method in the vtable:
7// * `clone` should create an additional RawWaker, including creating all the resources required
8// * `wake` should consume the waker it was invoked on and release its resources
9// * `wake_by_ref` is like `wake` but does not consume or release the resources
10// * `drop` releases all the resources associated with a waker
11// Our wakers don't have any resources associated with them -- the `data` pointer's bits are just
12// the task ID -- so all these safety requirements are trivial.
13
14/// Create a `Waker` that will make the given `task_id` runnable when invoked.
15pub fn make_waker(task_id: TaskId) -> Waker {
16    // We stash the task ID into the bits of the `data` pointer that all the vtable method below
17    // receive as an argument.
18    let data = task_id.0 as *const ();
19    // Safety: see above
20    unsafe { Waker::from_raw(RawWaker::new(data, &RAW_WAKER_VTABLE)) }
21}
22
23unsafe fn raw_waker_clone(data: *const ()) -> RawWaker {
24    // No resources associated with our wakers, so just duplicate the pointer
25    RawWaker::new(data, &RAW_WAKER_VTABLE)
26}
27
28unsafe fn raw_waker_wake(data: *const ()) {
29    let task_id = TaskId::from(data as usize);
30    ExecutionState::with(|state| {
31        // While execution teardown tears a task down, it is the current task, so the execution
32        // isn't finished, and wakers work (see `ExecutionState::tear_down`).
33        if state.is_finished() {
34            return;
35        }
36
37        // A waker can outlive its execution (in a `static`, say), and then names a task of another one.
38        let Some(waiter) = state.try_get_mut(task_id) else {
39            return;
40        };
41
42        if waiter.finished() {
43            return;
44        }
45
46        waiter.wake();
47    });
48}
49
50unsafe fn raw_waker_wake_by_ref(data: *const ()) {
51    // Our wakers have no resources associated with then, so `wake` and `wake_by_ref` are the same
52    raw_waker_wake(data);
53}
54
55unsafe fn raw_waker_drop(_data: *const ()) {
56    // No resources associated with our wakers, so nothing to do on drop
57}
58
59const RAW_WAKER_VTABLE: RawWakerVTable =
60    RawWakerVTable::new(raw_waker_clone, raw_waker_wake, raw_waker_wake_by_ref, raw_waker_drop);