Skip to main content

dope_runtime/executor/
task_queue.rs

1use std::{
2    cell::{Cell, RefCell, UnsafeCell},
3    future::Future,
4    pin::Pin,
5    ptr::NonNull,
6    rc::Rc,
7    task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
8};
9
10use super::join_handle::JoinHandle;
11
12pub const DEFAULT_READY_BUDGET: usize = 256;
13
14struct ErasedFuture {
15    data: *mut (),
16    poll_fn: unsafe fn(*mut (), &mut Context<'_>) -> Poll<()>,
17    drop_fn: unsafe fn(*mut ()),
18}
19
20impl ErasedFuture {
21    fn new<F>(fut: F) -> Self
22    where
23        F: Future<Output = ()> + 'static,
24    {
25        let raw = Box::into_raw(Box::new(Box::pin(fut))) as *mut ();
26        Self {
27            data: raw,
28            poll_fn: poll_erased_future::<F>,
29            drop_fn: drop_erased_future::<F>,
30        }
31    }
32
33    fn poll(&mut self, cx: &mut Context<'_>) -> Poll<()> {
34        unsafe { (self.poll_fn)(self.data, cx) }
35    }
36}
37
38impl Drop for ErasedFuture {
39    fn drop(&mut self) {
40        if self.data.is_null() {
41            return;
42        }
43
44        unsafe { (self.drop_fn)(self.data) };
45        self.data = std::ptr::null_mut();
46    }
47}
48
49unsafe fn poll_erased_future<F>(raw: *mut (), cx: &mut Context<'_>) -> Poll<()>
50where
51    F: Future<Output = ()> + 'static,
52{
53    let fut = unsafe { &mut *(raw as *mut Pin<Box<F>>) };
54    fut.as_mut().poll(cx)
55}
56
57unsafe fn drop_erased_future<F>(raw: *mut ())
58where
59    F: Future<Output = ()> + 'static,
60{
61    drop(unsafe { Box::from_raw(raw as *mut Pin<Box<F>>) });
62}
63
64pub struct TaskQueue {
65    queue_head: UnsafeCell<Option<NonNull<TaskInner>>>,
66    queue_tail: UnsafeCell<Option<NonNull<TaskInner>>>,
67}
68
69impl Default for TaskQueue {
70    fn default() -> Self {
71        Self::new()
72    }
73}
74
75impl TaskQueue {
76    pub fn new() -> Self {
77        Self {
78            queue_head: UnsafeCell::new(None),
79            queue_tail: UnsafeCell::new(None),
80        }
81    }
82
83    pub fn is_empty(&self) -> bool {
84        unsafe { (&*self.queue_head.get()).is_none() }
85    }
86
87    pub fn spawn<T, F>(&self, fut: F) -> JoinHandle<T>
88    where
89        F: Future<Output = T> + 'static,
90        T: 'static,
91    {
92        let (jh, complete) = JoinHandle::new_local();
93        self.spawn_erased(ErasedFuture::new(async move {
94            let v = fut.await;
95            complete(v);
96        }));
97        jh
98    }
99
100    fn spawn_erased(&self, fut: ErasedFuture) {
101        let task = Rc::new(TaskInner {
102            fut: RefCell::new(Some(fut)),
103
104            exec: NonNull::from(self),
105            queued: Cell::new(false),
106            cached_waker: RefCell::new(None),
107            next: Cell::new(None),
108        });
109        self.enqueue_owned(task);
110    }
111
112    fn enqueue_owned(&self, task: Rc<TaskInner>) {
113        if Self::already_queued(&task) {
114            return;
115        }
116        self.push_queue_ptr(Self::into_ptr(task));
117    }
118
119    fn enqueue_ref(&self, task: &Rc<TaskInner>) {
120        if Self::already_queued(task) {
121            return;
122        }
123        self.push_queue_ptr(Self::into_ptr(task.clone()));
124    }
125
126    fn push_queue_ptr(&self, ptr: NonNull<TaskInner>) {
127        unsafe {
128            ptr.as_ref().next.set(None);
129            let tail = &mut *self.queue_tail.get();
130            if let Some(tail_ptr) = *tail {
131                tail_ptr.as_ref().next.set(Some(ptr));
132            } else {
133                *self.queue_head.get() = Some(ptr);
134            }
135            *tail = Some(ptr);
136        }
137    }
138
139    fn pop_queue_ptr(&self) -> Option<NonNull<TaskInner>> {
140        let head = unsafe { &mut *self.queue_head.get() };
141        let ptr = (*head)?;
142        unsafe {
143            let task_ref = ptr.as_ref();
144            *head = task_ref.next.get();
145            if head.is_none() {
146                *self.queue_tail.get() = None;
147            }
148            task_ref.next.set(None);
149        }
150        Some(ptr)
151    }
152
153    fn already_queued(task: &Rc<TaskInner>) -> bool {
154        task.queued.replace(true)
155    }
156
157    fn into_ptr(task: Rc<TaskInner>) -> NonNull<TaskInner> {
158        unsafe { NonNull::new_unchecked(Rc::into_raw(task) as *mut TaskInner) }
159    }
160
161    pub fn run_ready_with_budget(&self, budget: usize) -> usize {
162        let mut ran = 0usize;
163        while ran < budget {
164            let Some(ptr) = self.pop_queue_ptr() else {
165                break;
166            };
167
168            let task = unsafe { Rc::from_raw(ptr.as_ptr()) };
169            task.queued.set(false);
170            task.poll();
171            ran += 1;
172        }
173        ran
174    }
175
176    pub fn run_ready_all(&self) -> usize {
177        let mut ran = 0usize;
178        while let Some(ptr) = self.pop_queue_ptr() {
179            let task = unsafe { Rc::from_raw(ptr.as_ptr()) };
180            task.queued.set(false);
181            task.poll();
182            ran += 1;
183        }
184        ran
185    }
186}
187
188struct TaskInner {
189    fut: RefCell<Option<ErasedFuture>>,
190    exec: NonNull<TaskQueue>,
191    queued: Cell<bool>,
192    cached_waker: RefCell<Option<Waker>>,
193    next: Cell<Option<NonNull<Self>>>,
194}
195
196impl TaskInner {
197    fn poll(self: &Rc<Self>) {
198        let mut cached_waker = self.cached_waker.borrow_mut();
199        let waker = cached_waker.get_or_insert_with(|| unsafe {
200            Waker::from_raw(raw_waker(Rc::into_raw(self.clone()) as *const ()))
201        });
202        let mut cx = Context::from_waker(waker);
203        let mut borrow = self.fut.borrow_mut();
204        let Some(fut) = borrow.as_mut() else { return };
205        if fut.poll(&mut cx).is_ready() {
206            *borrow = None;
207            let _ = cached_waker.take();
208        }
209    }
210
211    fn wake(self: &Rc<Self>) {
212        let exec = unsafe { self.exec.as_ref() };
213        exec.enqueue_ref(self);
214    }
215}
216
217static VTABLE: RawWakerVTable =
218    RawWakerVTable::new(clone_waker, wake_by_val, wake_by_ref, drop_waker);
219
220fn raw_waker(ptr: *const ()) -> RawWaker {
221    RawWaker::new(ptr, &VTABLE)
222}
223
224unsafe fn clone_waker(ptr: *const ()) -> RawWaker {
225    let task = unsafe { Rc::<TaskInner>::from_raw(ptr as *const TaskInner) };
226    let cloned = task.clone();
227    std::mem::forget(task);
228    raw_waker(Rc::into_raw(cloned) as *const ())
229}
230
231unsafe fn wake_by_val(ptr: *const ()) {
232    let task = unsafe { Rc::<TaskInner>::from_raw(ptr as *const TaskInner) };
233    task.wake();
234}
235
236unsafe fn wake_by_ref(ptr: *const ()) {
237    let task = unsafe { Rc::<TaskInner>::from_raw(ptr as *const TaskInner) };
238    task.wake();
239    std::mem::forget(task);
240}
241
242unsafe fn drop_waker(ptr: *const ()) {
243    drop(unsafe { Rc::<TaskInner>::from_raw(ptr as *const TaskInner) });
244}