1use crate::{Instant, Priority, RunnableMeta, Scheduler, SessionId, Timer};
2use async_task::Runnable;
3use std::{
4 any::Any,
5 future::Future,
6 marker::PhantomData,
7 mem::ManuallyDrop,
8 panic::Location,
9 pin::Pin,
10 rc::Rc,
11 sync::Arc,
12 task::{Context, Poll},
13 thread::{self, ThreadId},
14 time::Duration,
15};
16
17#[derive(Clone)]
22pub struct LocalExecutor {
23 session_id: SessionId,
24 scheduler: Arc<dyn Scheduler>,
25 dispatch: Arc<dyn Fn(Runnable<RunnableMeta>) + Send + Sync>,
29 not_send: PhantomData<Rc<()>>,
30}
31
32impl LocalExecutor {
33 pub fn new(
42 session_id: SessionId,
43 scheduler: Arc<dyn Scheduler>,
44 dispatch: impl Fn(Runnable<RunnableMeta>) + Send + Sync + 'static,
45 ) -> Self {
46 Self {
47 session_id,
48 scheduler,
49 dispatch: Arc::new(dispatch),
50 not_send: PhantomData,
51 }
52 }
53
54 pub fn session_id(&self) -> SessionId {
55 self.session_id
56 }
57
58 pub fn scheduler(&self) -> &Arc<dyn Scheduler> {
59 &self.scheduler
60 }
61
62 #[track_caller]
63 pub fn spawn<F>(&self, future: F) -> Task<F::Output>
64 where
65 F: Future + 'static,
66 F::Output: 'static,
67 {
68 let dispatch = self.dispatch.clone();
69 let location = Location::caller();
70 let (runnable, task) = spawn_local_with_source_location(
71 future,
72 move |runnable| dispatch(runnable),
73 RunnableMeta {
74 location,
75 spawned: crate::SpawnTime(Instant::now()),
76 },
77 );
78 runnable.schedule();
79 Task(TaskState::Spawned(task))
80 }
81
82 #[track_caller]
87 pub fn spawn_with_dispatch<F>(
88 &self,
89 future: F,
90 dispatch: impl Fn(Runnable<RunnableMeta>) + Send + Sync + 'static,
91 ) -> Task<F::Output>
92 where
93 F: Future + 'static,
94 F::Output: 'static,
95 {
96 let location = Location::caller();
97 let (runnable, task) = spawn_local_with_source_location(
98 future,
99 dispatch,
100 RunnableMeta {
101 location,
102 spawned: crate::SpawnTime(Instant::now()),
103 },
104 );
105 runnable.schedule();
106 Task(TaskState::Spawned(task))
107 }
108
109 pub fn block_on<Fut: Future>(&self, future: Fut) -> Fut::Output {
110 use std::cell::Cell;
111
112 let output = Cell::new(None);
113 let future = async {
114 output.set(Some(future.await));
115 };
116 let mut future = std::pin::pin!(future);
117
118 self.scheduler
119 .block(Some(self.session_id), future.as_mut(), None);
120
121 output.take().expect("block_on future did not complete")
122 }
123
124 pub fn block_with_timeout<Fut: Future>(
127 &self,
128 timeout: Duration,
129 future: Fut,
130 ) -> Result<Fut::Output, impl Future<Output = Fut::Output> + use<Fut>> {
131 use std::cell::Cell;
132
133 let output = Cell::new(None);
134 let mut future = Box::pin(future);
135
136 {
137 let future_ref = &mut future;
138 let wrapper = async {
139 output.set(Some(future_ref.await));
140 };
141 let mut wrapper = std::pin::pin!(wrapper);
142
143 self.scheduler
144 .block(Some(self.session_id), wrapper.as_mut(), Some(timeout));
145 }
146
147 match output.take() {
148 Some(value) => Ok(value),
149 None => Err(future),
150 }
151 }
152
153 #[track_caller]
154 pub fn timer(&self, duration: Duration) -> Timer {
155 self.scheduler.timer(duration)
156 }
157
158 pub fn now(&self) -> Instant {
159 self.scheduler.clock().now()
160 }
161
162 #[track_caller]
171 pub fn spawn_dedicated<F, Fut>(&self, f: F) -> Task<Fut::Output>
172 where
173 F: FnOnce(LocalExecutor) -> Fut + Send + 'static,
174 Fut: Future + 'static,
175 Fut::Output: Send + Sync + 'static,
176 {
177 self.scheduler
178 .clone()
179 .spawn_dedicated(box_dedicated(f))
180 .downcast::<Fut::Output>()
181 }
182}
183
184fn box_dedicated<F, Fut>(
189 f: F,
190) -> Box<
191 dyn FnOnce(LocalExecutor) -> Pin<Box<dyn Future<Output = Box<dyn Any + Send + Sync>> + 'static>>
192 + Send
193 + 'static,
194>
195where
196 F: FnOnce(LocalExecutor) -> Fut + Send + 'static,
197 Fut: Future + 'static,
198 Fut::Output: Send + Sync + 'static,
199{
200 Box::new(move |executor| {
201 Box::pin(async move { Box::new(f(executor).await) as Box<dyn Any + Send + Sync> })
202 })
203}
204
205#[derive(Clone)]
206pub struct BackgroundExecutor {
207 scheduler: Arc<dyn Scheduler>,
208}
209
210impl BackgroundExecutor {
211 pub fn new(scheduler: Arc<dyn Scheduler>) -> Self {
212 Self { scheduler }
213 }
214
215 #[track_caller]
216 pub fn spawn<F>(&self, future: F) -> Task<F::Output>
217 where
218 F: Future + Send + 'static,
219 F::Output: Send + 'static,
220 {
221 self.spawn_with_priority(Priority::default(), future)
222 }
223
224 #[track_caller]
225 pub fn spawn_with_priority<F>(&self, priority: Priority, future: F) -> Task<F::Output>
226 where
227 F: Future + Send + 'static,
228 F::Output: Send + 'static,
229 {
230 let scheduler = Arc::downgrade(&self.scheduler);
231 let location = Location::caller();
232 let (runnable, task) = async_task::Builder::new()
233 .metadata(RunnableMeta {
234 location,
235 spawned: crate::SpawnTime(Instant::now()),
236 })
237 .spawn(
238 move |_| future,
239 move |runnable| {
240 if let Some(scheduler) = scheduler.upgrade() {
241 scheduler.schedule_background_with_priority(runnable, priority);
242 }
243 },
244 );
245 runnable.schedule();
246 Task(TaskState::Spawned(task))
247 }
248
249 #[track_caller]
251 pub fn spawn_realtime<F>(&self, future: F) -> Task<F::Output>
252 where
253 F: Future + Send + 'static,
254 F::Output: Send + 'static,
255 {
256 let location = Location::caller();
257 let (tx, rx) = flume::bounded::<async_task::Runnable<RunnableMeta>>(1);
258
259 self.scheduler.spawn_realtime(Box::new(move || {
260 while let Ok(runnable) = rx.recv() {
261 runnable.run();
262 }
263 }));
264
265 let (runnable, task) = async_task::Builder::new()
266 .metadata(RunnableMeta {
267 location,
268 spawned: crate::SpawnTime(Instant::now()),
269 })
270 .spawn(
271 move |_| future,
272 move |runnable| {
273 let _ = tx.send(runnable);
274 },
275 );
276 runnable.schedule();
277 Task(TaskState::Spawned(task))
278 }
279
280 #[track_caller]
281 pub fn timer(&self, duration: Duration) -> Timer {
282 self.scheduler.timer(duration)
283 }
284
285 pub fn now(&self) -> Instant {
286 self.scheduler.clock().now()
287 }
288
289 pub fn scheduler(&self) -> &Arc<dyn Scheduler> {
290 &self.scheduler
291 }
292
293 #[track_caller]
302 pub fn spawn_dedicated<F, Fut>(&self, f: F) -> Task<Fut::Output>
303 where
304 F: FnOnce(LocalExecutor) -> Fut + Send + 'static,
305 Fut: Future + 'static,
306 Fut::Output: Send + Sync + 'static,
307 {
308 self.scheduler
309 .clone()
310 .spawn_dedicated(box_dedicated(f))
311 .downcast::<Fut::Output>()
312 }
313}
314
315#[must_use]
322pub struct Task<T>(TaskState<T>);
323
324enum TaskState<T> {
325 Ready(Option<T>),
327
328 Spawned(async_task::Task<T, RunnableMeta>),
330
331 Downcast {
335 inner: Box<Task<Box<dyn Any + Send + Sync>>>,
336 marker: PhantomData<fn() -> T>,
337 },
338}
339
340impl<T> Task<T> {
341 pub fn ready(val: T) -> Self {
343 Task(TaskState::Ready(Some(val)))
344 }
345
346 pub fn from_async_task(task: async_task::Task<T, RunnableMeta>) -> Self {
348 Task(TaskState::Spawned(task))
349 }
350
351 pub fn is_ready(&self) -> bool {
352 match &self.0 {
353 TaskState::Ready(_) => true,
354 TaskState::Spawned(task) => task.is_finished(),
355 TaskState::Downcast { inner, .. } => inner.is_ready(),
356 }
357 }
358
359 pub fn detach(self) {
361 match self {
362 Task(TaskState::Ready(_)) => {}
363 Task(TaskState::Spawned(task)) => task.detach(),
364 Task(TaskState::Downcast { inner, .. }) => inner.detach(),
365 }
366 }
367
368 pub fn fallible(self) -> FallibleTask<T> {
370 FallibleTask(match self.0 {
371 TaskState::Ready(val) => FallibleTaskState::Ready(val),
372 TaskState::Spawned(task) => FallibleTaskState::Spawned(task.fallible()),
373 TaskState::Downcast { inner, .. } => FallibleTaskState::Downcast {
374 inner: Box::new(inner.fallible()),
375 marker: PhantomData,
376 },
377 })
378 }
379}
380
381impl Task<Box<dyn Any + Send + Sync>> {
382 pub fn downcast<T: Send + Sync + 'static>(self) -> Task<T> {
391 Task(TaskState::Downcast {
392 inner: Box::new(self),
393 marker: PhantomData,
394 })
395 }
396}
397
398impl<T> std::fmt::Debug for Task<T> {
399 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
400 match &self.0 {
401 TaskState::Ready(_) => f.debug_tuple("Task::Ready").finish(),
402 TaskState::Spawned(task) => f.debug_tuple("Task::Spawned").field(task).finish(),
403 TaskState::Downcast { inner, .. } => {
404 f.debug_tuple("Task::Downcast").field(inner).finish()
405 }
406 }
407 }
408}
409
410#[must_use]
412pub struct FallibleTask<T>(FallibleTaskState<T>);
413
414enum FallibleTaskState<T> {
415 Ready(Option<T>),
417
418 Spawned(async_task::FallibleTask<T, RunnableMeta>),
420
421 Downcast {
423 inner: Box<FallibleTask<Box<dyn Any + Send + Sync>>>,
424 marker: PhantomData<fn() -> T>,
425 },
426}
427
428impl<T> FallibleTask<T> {
429 pub fn ready(val: T) -> Self {
431 FallibleTask(FallibleTaskState::Ready(Some(val)))
432 }
433
434 pub fn detach(self) {
436 match self.0 {
437 FallibleTaskState::Ready(_) => {}
438 FallibleTaskState::Spawned(task) => task.detach(),
439 FallibleTaskState::Downcast { inner, .. } => inner.detach(),
440 }
441 }
442}
443
444impl<T: 'static> Future for FallibleTask<T> {
445 type Output = Option<T>;
446
447 fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
448 match unsafe { self.get_unchecked_mut() } {
449 FallibleTask(FallibleTaskState::Ready(val)) => Poll::Ready(val.take()),
450 FallibleTask(FallibleTaskState::Spawned(task)) => Pin::new(task).poll(cx),
451 FallibleTask(FallibleTaskState::Downcast { inner, .. }) => {
452 match Pin::new(inner.as_mut()).poll(cx) {
453 Poll::Ready(Some(boxed_any)) => Poll::Ready(Some(
454 *boxed_any
455 .downcast::<T>()
456 .expect("FallibleTask::poll: downcast type mismatch"),
457 )),
458 Poll::Ready(None) => Poll::Ready(None),
459 Poll::Pending => Poll::Pending,
460 }
461 }
462 }
463 }
464}
465
466impl<T> std::fmt::Debug for FallibleTask<T> {
467 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
468 match &self.0 {
469 FallibleTaskState::Ready(_) => f.debug_tuple("FallibleTask::Ready").finish(),
470 FallibleTaskState::Spawned(task) => {
471 f.debug_tuple("FallibleTask::Spawned").field(task).finish()
472 }
473 FallibleTaskState::Downcast { inner, .. } => f
474 .debug_tuple("FallibleTask::Downcast")
475 .field(inner)
476 .finish(),
477 }
478 }
479}
480
481impl<T: 'static> Future for Task<T> {
482 type Output = T;
483
484 fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
485 match unsafe { self.get_unchecked_mut() } {
486 Task(TaskState::Ready(val)) => Poll::Ready(val.take().unwrap()),
487 Task(TaskState::Spawned(task)) => Pin::new(task).poll(cx),
488 Task(TaskState::Downcast { inner, .. }) => match Pin::new(inner.as_mut()).poll(cx) {
489 Poll::Ready(boxed_any) => Poll::Ready(
490 *boxed_any
491 .downcast::<T>()
492 .expect("Task::poll: downcast type mismatch"),
493 ),
494 Poll::Pending => Poll::Pending,
495 },
496 }
497 }
498}
499
500#[track_caller]
502fn spawn_local_with_source_location<Fut, S>(
503 future: Fut,
504 schedule: S,
505 metadata: RunnableMeta,
506) -> (
507 async_task::Runnable<RunnableMeta>,
508 async_task::Task<Fut::Output, RunnableMeta>,
509)
510where
511 Fut: Future + 'static,
512 Fut::Output: 'static,
513 S: async_task::Schedule<RunnableMeta> + Send + Sync + 'static,
514{
515 #[inline]
516 fn thread_id() -> ThreadId {
517 std::thread_local! {
518 static ID: ThreadId = thread::current().id();
519 }
520 ID.try_with(|id| *id)
521 .unwrap_or_else(|_| thread::current().id())
522 }
523
524 struct Checked<F> {
525 id: ThreadId,
526 inner: ManuallyDrop<F>,
527 location: &'static Location<'static>,
528 }
529
530 impl<F> Drop for Checked<F> {
531 fn drop(&mut self) {
532 assert_eq!(
533 self.id,
534 thread_id(),
535 "local task dropped by a thread that didn't spawn it. Task spawned at {}",
536 self.location
537 );
538 unsafe { ManuallyDrop::drop(&mut self.inner) };
542 }
543 }
544
545 impl<F: Future> Future for Checked<F> {
546 type Output = F::Output;
547
548 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
549 let this = unsafe { self.get_unchecked_mut() };
553 assert!(
554 this.id == thread_id(),
555 "local task polled by a thread that didn't spawn it. Task spawned at {}",
556 this.location
557 );
558 unsafe { Pin::new_unchecked(&mut *this.inner).poll(cx) }
563 }
564 }
565
566 let location = metadata.location;
567
568 let future = move |_| Checked {
569 id: thread_id(),
570 inner: ManuallyDrop::new(future),
571 location,
572 };
573
574 let builder = async_task::Builder::new().metadata(metadata);
575 unsafe { builder.spawn_unchecked(future, schedule) }
579}