dope_runtime/executor/
task_queue.rs1use 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}