simcore/async_mode/
waker.rs

1// Custom waker creation logic to avoid Send+Sync requirement.
2//
3// Based on waker-related code from the futures crate:
4// https://github.com/rust-lang/futures-rs/tree/master/futures-task/src
5
6use std::rc::Rc;
7use std::task::{RawWaker, RawWakerVTable, Waker};
8
9use core::mem::ManuallyDrop;
10use futures::task::WakerRef;
11
12// A way of waking up a specific task.
13// By implementing this trait, types that are expected to be wrapped in Rc can be converted into Waker objects.
14// The waker is used to signal executor that a task is ready to be polled again.
15pub(super) trait RcWake {
16    // Indicates that the associated task is ready to make progress and should be polled.
17    fn wake(self: Rc<Self>) {
18        Self::wake_by_ref(&self)
19    }
20
21    // Indicates that the associated task is ready to make progress and should be polled.
22    // This function is similar to wake(), but must not consume the provided data pointer.
23    fn wake_by_ref(rc_self: &Rc<Self>);
24}
25
26// Creates a reference to a Waker from a reference to Rc<impl RcWake>.
27// The resulting Waker will call RcWake::wake if awoken.
28pub(super) fn waker_ref<W>(wake: &Rc<W>) -> WakerRef<'_>
29where
30    W: RcWake + 'static,
31{
32    // simply copy the pointer instead of using Rc::into_raw,
33    // as we don't actually keep a refcount by using ManuallyDrop
34    let ptr = Rc::as_ptr(wake).cast::<()>();
35
36    let waker = ManuallyDrop::new(unsafe { Waker::from_raw(RawWaker::new(ptr, waker_vtable::<W>())) });
37    WakerRef::new_unowned(waker)
38}
39
40fn waker_vtable<W: RcWake + 'static>() -> &'static RawWakerVTable {
41    &RawWakerVTable::new(
42        clone_rc_raw::<W>,
43        wake_rc_raw::<W>,
44        wake_by_ref_rc_raw::<W>,
45        drop_rc_raw::<W>,
46    )
47}
48
49#[allow(clippy::redundant_clone)] // The clone here isn't actually redundant.
50unsafe fn increase_refcount<T: RcWake + 'static>(data: *const ()) {
51    // Retain Rc, but don't touch refcount by wrapping in ManuallyDrop
52    let rc = ManuallyDrop::new(unsafe { Rc::<T>::from_raw(data.cast::<T>()) });
53    // Now increase refcount, but don't drop new refcount either
54    let _rc_clone: ManuallyDrop<_> = rc.clone();
55}
56
57unsafe fn clone_rc_raw<T: RcWake + 'static>(data: *const ()) -> RawWaker {
58    unsafe { increase_refcount::<T>(data) };
59    RawWaker::new(data, waker_vtable::<T>())
60}
61
62unsafe fn wake_rc_raw<T: RcWake + 'static>(data: *const ()) {
63    let rc: Rc<T> = unsafe { Rc::from_raw(data.cast::<T>()) };
64    RcWake::wake(rc);
65}
66
67unsafe fn wake_by_ref_rc_raw<T: RcWake + 'static>(data: *const ()) {
68    // Retain Rc, but don't touch refcount by wrapping in ManuallyDrop
69    let rc = ManuallyDrop::new(unsafe { Rc::<T>::from_raw(data.cast::<T>()) });
70    RcWake::wake_by_ref(&rc);
71}
72
73unsafe fn drop_rc_raw<T: RcWake + 'static>(data: *const ()) {
74    drop(unsafe { Rc::<T>::from_raw(data.cast::<T>()) })
75}