1use std::cell::{Cell, RefCell};
9use std::collections::{HashMap, VecDeque};
10use std::fmt;
11use std::future::Future;
12use std::panic::{AssertUnwindSafe, catch_unwind};
13use std::pin::Pin;
14use std::rc::Rc;
15use std::sync::{Arc, Mutex};
16use std::task::{Context, Poll, Wake, Waker};
17
18pub type TaskId = u64;
19
20#[derive(Copy, Clone, Debug, PartialEq, Eq)]
22pub enum TaskState {
23 Unstarted,
24 Scheduled,
25 Running,
26 Pending,
27 Finished,
28 Cancelled,
29 Failed,
30}
31
32#[derive(Debug, Clone)]
33pub enum TaskError {
34 Cancelled,
35 Panicked(String),
36 InvalidState,
38}
39
40impl fmt::Display for TaskError {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 match self {
43 TaskError::Cancelled => write!(f, "task was cancelled"),
44 TaskError::Panicked(m) => write!(f, "task panicked: {m}"),
45 TaskError::InvalidState => write!(f, "task result not available"),
46 }
47 }
48}
49impl std::error::Error for TaskError {}
50
51struct WokenQueue(Mutex<VecDeque<TaskId>>);
58
59struct TaskWaker {
60 id: TaskId,
61 woken: Arc<WokenQueue>,
62}
63
64impl Wake for TaskWaker {
65 fn wake(self: Arc<Self>) {
66 self.woken.0.lock().unwrap().push_back(self.id);
67 }
68}
69
70struct TaskShared<T> {
76 state: Cell<TaskState>,
77 result: RefCell<Option<Result<T, TaskError>>>,
78 joiners: RefCell<Vec<Waker>>,
79}
80
81impl<T> TaskShared<T> {
82 fn complete(&self, r: Result<T, TaskError>) {
83 *self.result.borrow_mut() = Some(r);
84 for w in self.joiners.borrow_mut().drain(..) {
85 w.wake();
86 }
87 }
88}
89
90struct TaskEntry {
91 fut: Pin<Box<dyn Future<Output = ()>>>,
92 name: String,
93 state: TaskState, on_abort: Rc<dyn Fn(TaskError)>,
96 state_cell: Rc<dyn Fn(TaskState)>,
98}
99
100type FailureSink = Box<dyn Fn(&str)>;
101
102struct ExecInner {
103 tasks: RefCell<HashMap<TaskId, TaskEntry>>,
104 next_id: Cell<TaskId>,
105 run_queue: RefCell<VecDeque<TaskId>>,
106 woken: Arc<WokenQueue>,
107 running: Cell<bool>,
108 currently_polling: Cell<Option<TaskId>>,
109 cancel_pending: RefCell<Vec<TaskId>>,
111 failure_sink: RefCell<Option<FailureSink>>,
114}
115
116#[derive(Clone)]
118pub struct Executor {
119 inner: Rc<ExecInner>,
120}
121
122thread_local! {
123 static CURRENT: RefCell<Option<Executor>> = const { RefCell::new(None) };
124}
125
126pub fn init() -> Executor {
128 let ex = Executor {
129 inner: Rc::new(ExecInner {
130 tasks: RefCell::new(HashMap::new()),
131 next_id: Cell::new(1),
132 run_queue: RefCell::new(VecDeque::new()),
133 woken: Arc::new(WokenQueue(Mutex::new(VecDeque::new()))),
134 running: Cell::new(false),
135 currently_polling: Cell::new(None),
136 cancel_pending: RefCell::new(Vec::new()),
137 failure_sink: RefCell::new(None),
138 }),
139 };
140 CURRENT.with(|c| *c.borrow_mut() = Some(ex.clone()));
141 ex
142}
143
144pub fn current() -> Executor {
146 CURRENT.with(|c| c.borrow().clone().expect("rustdv executor not initialized"))
147}
148
149pub fn spawn<F>(fut: F) -> TaskHandle<F::Output>
151where
152 F: Future + 'static,
153{
154 current().spawn_named(fut, None)
155}
156
157pub fn spawn_named<F>(fut: F, name: &str) -> TaskHandle<F::Output>
158where
159 F: Future + 'static,
160{
161 current().spawn_named(fut, Some(name))
162}
163
164impl Executor {
165 pub fn spawn_named<F>(&self, fut: F, name: Option<&str>) -> TaskHandle<F::Output>
166 where
167 F: Future + 'static,
168 {
169 let id = self.inner.next_id.get();
170 self.inner.next_id.set(id + 1);
171 let name = name
172 .map(|s| s.to_string())
173 .unwrap_or_else(|| format!("task_{id}"));
174
175 let shared = Rc::new(TaskShared::<F::Output> {
176 state: Cell::new(TaskState::Unstarted),
177 result: RefCell::new(None),
178 joiners: RefCell::new(Vec::new()),
179 });
180
181 let sh = shared.clone();
183 let wrapped = async move {
184 let out = fut.await;
185 sh.state.set(TaskState::Finished);
186 sh.complete(Ok(out));
187 };
188
189 let sh_abort = shared.clone();
190 let on_abort: Rc<dyn Fn(TaskError)> = Rc::new(move |e: TaskError| {
191 sh_abort.state.set(match e {
192 TaskError::Cancelled => TaskState::Cancelled,
193 _ => TaskState::Failed,
194 });
195 sh_abort.complete(Err(e));
196 });
197 let sh_state = shared.clone();
198 let state_cell: Rc<dyn Fn(TaskState)> = Rc::new(move |s| sh_state.state.set(s));
199
200 shared.state.set(TaskState::Scheduled);
201 self.inner.tasks.borrow_mut().insert(
202 id,
203 TaskEntry {
204 fut: Box::pin(wrapped),
205 name,
206 state: TaskState::Scheduled,
207 on_abort,
208 state_cell,
209 },
210 );
211 self.inner.run_queue.borrow_mut().push_back(id);
212
213 TaskHandle {
214 id,
215 shared,
216 exec: self.clone(),
217 }
218 }
219
220 pub fn run_until_idle(&self) {
223 if self.inner.running.get() {
224 return; }
226 self.inner.running.set(true);
227 loop {
228 self.drain_woken();
229 let next = self.inner.run_queue.borrow_mut().pop_front();
230 let Some(id) = next else { break };
231 self.poll_task(id);
232 }
233 self.inner.running.set(false);
234 }
235
236 fn drain_woken(&self) {
237 let ids: Vec<TaskId> = self.inner.woken.0.lock().unwrap().drain(..).collect();
238 for id in ids {
239 let mut tasks = self.inner.tasks.borrow_mut();
240 if let Some(t) = tasks.get_mut(&id)
241 && t.state == TaskState::Pending
242 {
243 t.state = TaskState::Scheduled;
244 (t.state_cell)(TaskState::Scheduled);
245 self.inner.run_queue.borrow_mut().push_back(id);
246 }
247 }
248 }
249
250 fn poll_task(&self, id: TaskId) {
251 let (mut fut, on_abort, state_cell) = {
255 let mut tasks = self.inner.tasks.borrow_mut();
256 let Some(t) = tasks.get_mut(&id) else { return };
257 if t.state != TaskState::Scheduled {
258 return; }
260 t.state = TaskState::Running;
261 (t.state_cell)(TaskState::Running);
262 let fut = std::mem::replace(&mut t.fut, Box::pin(async {}));
264 (fut, t.on_abort.clone(), t.state_cell.clone())
265 };
266
267 let waker = Waker::from(Arc::new(TaskWaker {
268 id,
269 woken: self.inner.woken.clone(),
270 }));
271 let mut cx = Context::from_waker(&waker);
272
273 self.inner.currently_polling.set(Some(id));
274 let polled = catch_unwind(AssertUnwindSafe(|| fut.as_mut().poll(&mut cx)));
275 self.inner.currently_polling.set(None);
276
277 match polled {
278 Ok(Poll::Ready(())) => {
279 self.inner.tasks.borrow_mut().remove(&id);
281 }
282 Ok(Poll::Pending) => {
283 let mut tasks = self.inner.tasks.borrow_mut();
284 if let Some(t) = tasks.get_mut(&id) {
285 t.fut = fut; t.state = TaskState::Pending;
287 (t.state_cell)(TaskState::Pending);
288 }
289 drop(tasks);
290 let pending: Vec<TaskId> =
292 self.inner.cancel_pending.borrow_mut().drain(..).collect();
293 for cid in pending {
294 self.cancel(cid);
295 }
296 }
297 Err(p) => {
298 let msg = panic_message(p);
299 let name = self
300 .inner
301 .tasks
302 .borrow()
303 .get(&id)
304 .map(|t| t.name.clone())
305 .unwrap_or_default();
306 self.inner.tasks.borrow_mut().remove(&id);
307 drop(fut); state_cell(TaskState::Failed);
309 on_abort(TaskError::Panicked(msg.clone()));
310 if let Some(sink) = self.inner.failure_sink.borrow().as_ref() {
311 sink(&format!("task '{name}' panicked: {msg}"));
312 } else {
313 eprintln!("rustdv: unhandled task panic in '{name}': {msg}");
314 }
315 }
316 }
317 }
318
319 pub fn cancel(&self, id: TaskId) {
322 if self.inner.currently_polling.get() == Some(id) {
323 self.inner.cancel_pending.borrow_mut().push(id);
324 return;
325 }
326 let entry = self.inner.tasks.borrow_mut().remove(&id);
327 if let Some(t) = entry {
328 (t.on_abort)(TaskError::Cancelled);
329 drop(t.fut);
330 }
331 }
332
333 pub fn cancel_after(&self, watermark: TaskId) {
337 let ids: Vec<TaskId> = self
338 .inner
339 .tasks
340 .borrow()
341 .keys()
342 .copied()
343 .filter(|&id| id >= watermark)
344 .collect();
345 for id in ids {
346 self.cancel(id);
347 }
348 }
349
350 pub fn watermark(&self) -> TaskId {
352 self.inner.next_id.get()
353 }
354
355 pub fn set_failure_sink(&self, f: Box<dyn Fn(&str)>) {
356 *self.inner.failure_sink.borrow_mut() = Some(f);
357 }
358
359 pub fn live_tasks(&self) -> usize {
360 self.inner.tasks.borrow().len()
361 }
362}
363
364fn panic_message(p: Box<dyn std::any::Any + Send>) -> String {
365 if let Some(s) = p.downcast_ref::<&str>() {
366 s.to_string()
367 } else if let Some(s) = p.downcast_ref::<String>() {
368 s.clone()
369 } else {
370 "panic (non-string payload)".to_string()
371 }
372}
373
374pub struct TaskHandle<T> {
381 id: TaskId,
382 shared: Rc<TaskShared<T>>,
383 exec: Executor,
384}
385
386impl<T> Clone for TaskHandle<T> {
387 fn clone(&self) -> Self {
388 TaskHandle {
389 id: self.id,
390 shared: self.shared.clone(),
391 exec: self.exec.clone(),
392 }
393 }
394}
395
396impl<T> TaskHandle<T> {
397 pub fn id(&self) -> TaskId {
398 self.id
399 }
400
401 pub fn state(&self) -> TaskState {
402 self.shared.state.get()
403 }
404
405 pub fn done(&self) -> bool {
406 matches!(
407 self.state(),
408 TaskState::Finished | TaskState::Cancelled | TaskState::Failed
409 )
410 }
411
412 pub fn cancel(&self) {
414 self.exec.cancel(self.id);
415 }
416
417 pub fn result(&self) -> Result<T, TaskError> {
419 match self.shared.result.borrow_mut().take() {
420 Some(r) => r,
421 None => Err(TaskError::InvalidState),
422 }
423 }
424}
425
426impl<T> Future for TaskHandle<T> {
427 type Output = Result<T, TaskError>;
428
429 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
430 if self.done() {
431 return Poll::Ready(self.result());
432 }
433 self.shared.joiners.borrow_mut().push(cx.waker().clone());
434 Poll::Pending
435 }
436}