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, Waker},
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 #[cfg(not(target_family = "wasm"))]
110 pub fn block_on<Fut: Future>(&self, future: Fut) -> Fut::Output {
111 use std::cell::Cell;
112
113 let output = Cell::new(None);
114 let future = async {
115 output.set(Some(future.await));
116 };
117 let mut future = std::pin::pin!(future);
118
119 self.scheduler
120 .block(Some(self.session_id), future.as_mut(), None);
121
122 output.take().expect("block_on future did not complete")
123 }
124
125 #[cfg(not(target_family = "wasm"))]
128 pub fn block_with_timeout<Fut: Future>(
129 &self,
130 timeout: Duration,
131 future: Fut,
132 ) -> Result<Fut::Output, impl Future<Output = Fut::Output> + use<Fut>> {
133 use std::cell::Cell;
134
135 let output = Cell::new(None);
136 let mut future = Box::pin(future);
137
138 {
139 let future_ref = &mut future;
140 let wrapper = async {
141 output.set(Some(future_ref.await));
142 };
143 let mut wrapper = std::pin::pin!(wrapper);
144
145 self.scheduler
146 .block(Some(self.session_id), wrapper.as_mut(), Some(timeout));
147 }
148
149 match output.take() {
150 Some(value) => Ok(value),
151 None => Err(future),
152 }
153 }
154
155 #[track_caller]
156 pub fn timer(&self, duration: Duration) -> Timer {
157 self.scheduler.timer(duration)
158 }
159
160 pub fn now(&self) -> Instant {
161 self.scheduler.clock().now()
162 }
163
164 #[track_caller]
173 pub fn spawn_dedicated<F, Fut>(&self, f: F) -> Task<Fut::Output>
174 where
175 F: FnOnce(LocalExecutor) -> Fut + Send + 'static,
176 Fut: Future + 'static,
177 Fut::Output: Send + Sync + 'static,
178 {
179 self.scheduler
180 .clone()
181 .spawn_dedicated(box_dedicated(f))
182 .downcast::<Fut::Output>()
183 }
184}
185
186fn box_dedicated<F, Fut>(
191 f: F,
192) -> Box<
193 dyn FnOnce(LocalExecutor) -> Pin<Box<dyn Future<Output = Box<dyn Any + Send + Sync>> + 'static>>
194 + Send
195 + 'static,
196>
197where
198 F: FnOnce(LocalExecutor) -> Fut + Send + 'static,
199 Fut: Future + 'static,
200 Fut::Output: Send + Sync + 'static,
201{
202 Box::new(move |executor| {
203 Box::pin(async move { Box::new(f(executor).await) as Box<dyn Any + Send + Sync> })
204 })
205}
206
207#[derive(Clone)]
208pub struct BackgroundExecutor {
209 scheduler: Arc<dyn Scheduler>,
210}
211
212impl BackgroundExecutor {
213 pub fn new(scheduler: Arc<dyn Scheduler>) -> Self {
214 Self { scheduler }
215 }
216
217 #[track_caller]
218 pub fn spawn<F>(&self, future: F) -> Task<F::Output>
219 where
220 F: Future + Send + 'static,
221 F::Output: Send + 'static,
222 {
223 self.spawn_with_priority(Priority::default(), future)
224 }
225
226 #[track_caller]
227 pub fn spawn_with_priority<F>(&self, priority: Priority, future: F) -> Task<F::Output>
228 where
229 F: Future + Send + 'static,
230 F::Output: Send + 'static,
231 {
232 let scheduler = Arc::downgrade(&self.scheduler);
233 let location = Location::caller();
234 let (runnable, task) = async_task::Builder::new()
235 .metadata(RunnableMeta {
236 location,
237 spawned: crate::SpawnTime(Instant::now()),
238 })
239 .spawn(
240 move |_| future,
241 move |runnable| {
242 if let Some(scheduler) = scheduler.upgrade() {
243 scheduler.schedule_background_with_priority(runnable, priority);
244 }
245 },
246 );
247 runnable.schedule();
248 Task(TaskState::Spawned(task))
249 }
250
251 #[track_caller]
253 pub fn spawn_realtime<F>(&self, future: F) -> Task<F::Output>
254 where
255 F: Future + Send + 'static,
256 F::Output: Send + 'static,
257 {
258 let location = Location::caller();
259 let (tx, rx) = flume::bounded::<async_task::Runnable<RunnableMeta>>(1);
260
261 self.scheduler.spawn_realtime(Box::new(move || {
262 while let Ok(runnable) = rx.recv() {
263 runnable.run();
264 }
265 }));
266
267 let (runnable, task) = async_task::Builder::new()
268 .metadata(RunnableMeta {
269 location,
270 spawned: crate::SpawnTime(Instant::now()),
271 })
272 .spawn(
273 move |_| future,
274 move |runnable| {
275 let _ = tx.send(runnable);
276 },
277 );
278 runnable.schedule();
279 Task(TaskState::Spawned(task))
280 }
281
282 #[track_caller]
283 pub fn timer(&self, duration: Duration) -> Timer {
284 self.scheduler.timer(duration)
285 }
286
287 pub fn now(&self) -> Instant {
288 self.scheduler.clock().now()
289 }
290
291 pub fn scheduler(&self) -> &Arc<dyn Scheduler> {
292 &self.scheduler
293 }
294
295 #[track_caller]
304 pub fn spawn_dedicated<F, Fut>(&self, f: F) -> Task<Fut::Output>
305 where
306 F: FnOnce(LocalExecutor) -> Fut + Send + 'static,
307 Fut: Future + 'static,
308 Fut::Output: Send + Sync + 'static,
309 {
310 self.scheduler
311 .clone()
312 .spawn_dedicated(box_dedicated(f))
313 .downcast::<Fut::Output>()
314 }
315}
316
317pub struct DedicatedExecutor {
328 sender: flume::Sender<Runnable<RunnableMeta>>,
329 _session: Task<()>,
330}
331
332impl DedicatedExecutor {
333 #[track_caller]
337 pub fn new(executor: &BackgroundExecutor) -> Self {
338 let (sender, receiver) = flume::unbounded::<Runnable<RunnableMeta>>();
339 let session = executor.spawn_dedicated(move |_executor| async move {
340 while let Ok(runnable) = receiver.recv_async().await {
341 runnable.run();
342 }
343 });
344 Self {
345 sender,
346 _session: session,
347 }
348 }
349
350 #[track_caller]
356 pub fn spawn<F>(&self, future: F) -> Task<F::Output>
357 where
358 F: Future + Send + 'static,
359 F::Output: Send + 'static,
360 {
361 let sender = self.sender.clone();
362 let (runnable, task) = async_task::Builder::new()
363 .metadata(RunnableMeta::new_with_callers_location())
364 .spawn(
365 move |_| future,
366 move |runnable| {
367 let _ = sender.send(runnable);
368 },
369 );
370 runnable.schedule();
371 Task(TaskState::Spawned(task))
372 }
373}
374
375#[must_use]
382pub struct Task<T>(TaskState<T>);
383
384enum TaskState<T> {
385 Ready(Option<T>),
387
388 Spawned(async_task::Task<T, RunnableMeta>),
390
391 Downcast {
395 inner: Box<Task<Box<dyn Any + Send + Sync>>>,
396 marker: PhantomData<fn() -> T>,
397 },
398
399 Rendezvous(RendezvousReceiver<T>),
403}
404
405enum RendezvousState<T> {
407 Pending(Option<Waker>),
409 Delivered(Task<T>),
411 Cancelled,
413 Detached,
415 Taken,
417}
418
419pub(crate) struct RendezvousReceiver<T> {
420 shared: Arc<parking_lot::Mutex<RendezvousState<T>>>,
421}
422
423impl<T> RendezvousReceiver<T> {
424 fn poll_take(&self, cx: &mut Context) -> Poll<Task<T>> {
425 let mut state = self.shared.lock();
426 match std::mem::replace(&mut *state, RendezvousState::Taken) {
427 RendezvousState::Delivered(task) => Poll::Ready(task),
428 RendezvousState::Pending(_) => {
429 *state = RendezvousState::Pending(Some(cx.waker().clone()));
430 Poll::Pending
431 }
432 RendezvousState::Cancelled | RendezvousState::Detached | RendezvousState::Taken => {
433 unreachable!("a rendezvous task was polled after its receiver was consumed")
434 }
435 }
436 }
437
438 fn is_ready(&self) -> bool {
439 match &*self.shared.lock() {
440 RendezvousState::Delivered(task) => task.is_ready(),
441 _ => false,
442 }
443 }
444
445 fn detach(self) {
446 let mut state = self.shared.lock();
447 match std::mem::replace(&mut *state, RendezvousState::Detached) {
448 RendezvousState::Delivered(task) => {
449 *state = RendezvousState::Taken;
450 drop(state);
451 task.detach();
452 }
453 RendezvousState::Pending(_) => {}
455 RendezvousState::Cancelled | RendezvousState::Detached | RendezvousState::Taken => {
456 unreachable!("a rendezvous task was detached after its receiver was consumed")
457 }
458 }
459 }
460}
461
462impl<T> Drop for RendezvousReceiver<T> {
463 fn drop(&mut self) {
464 let mut state = self.shared.lock();
465 match &*state {
466 RendezvousState::Pending(_) => *state = RendezvousState::Cancelled,
467 RendezvousState::Delivered(_) => {
468 let delivered = std::mem::replace(&mut *state, RendezvousState::Cancelled);
469 drop(state);
470 drop(delivered);
471 }
472 _ => {}
476 }
477 }
478}
479
480pub(crate) struct RendezvousSender<T> {
481 shared: Arc<parking_lot::Mutex<RendezvousState<T>>>,
482}
483
484impl<T> RendezvousSender<T> {
485 pub(crate) fn deliver(self, task: Task<T>) {
488 let mut state = self.shared.lock();
489 match std::mem::replace(&mut *state, RendezvousState::Delivered(task)) {
490 RendezvousState::Pending(waker) => {
491 drop(state);
492 if let Some(waker) = waker {
493 waker.wake();
494 }
495 }
496 RendezvousState::Cancelled => {
497 let delivered = std::mem::replace(&mut *state, RendezvousState::Cancelled);
498 drop(state);
499 drop(delivered);
500 }
501 RendezvousState::Detached => {
502 let RendezvousState::Delivered(task) =
503 std::mem::replace(&mut *state, RendezvousState::Detached)
504 else {
505 unreachable!("the delivered task was just stored");
506 };
507 drop(state);
508 task.detach();
509 }
510 RendezvousState::Delivered(_) | RendezvousState::Taken => {
511 unreachable!("a rendezvous task was delivered twice")
512 }
513 }
514 }
515}
516
517impl<T> Task<T> {
518 pub fn ready(val: T) -> Self {
520 Task(TaskState::Ready(Some(val)))
521 }
522
523 pub(crate) fn rendezvous() -> (Self, RendezvousSender<T>) {
528 let shared = Arc::new(parking_lot::Mutex::new(RendezvousState::Pending(None)));
529 (
530 Task(TaskState::Rendezvous(RendezvousReceiver {
531 shared: shared.clone(),
532 })),
533 RendezvousSender { shared },
534 )
535 }
536
537 pub fn from_async_task(task: async_task::Task<T, RunnableMeta>) -> Self {
539 Task(TaskState::Spawned(task))
540 }
541
542 pub fn is_ready(&self) -> bool {
543 match &self.0 {
544 TaskState::Ready(_) => true,
545 TaskState::Spawned(task) => task.is_finished(),
546 TaskState::Downcast { inner, .. } => inner.is_ready(),
547 TaskState::Rendezvous(receiver) => receiver.is_ready(),
548 }
549 }
550
551 pub fn detach(self) {
553 match self {
554 Task(TaskState::Ready(_)) => {}
555 Task(TaskState::Spawned(task)) => task.detach(),
556 Task(TaskState::Downcast { inner, .. }) => inner.detach(),
557 Task(TaskState::Rendezvous(receiver)) => receiver.detach(),
558 }
559 }
560
561 pub fn fallible(self) -> FallibleTask<T> {
563 FallibleTask(match self.0 {
564 TaskState::Ready(val) => FallibleTaskState::Ready(val),
565 TaskState::Spawned(task) => FallibleTaskState::Spawned(task.fallible()),
566 TaskState::Downcast { inner, .. } => FallibleTaskState::Downcast {
567 inner: Box::new(inner.fallible()),
568 marker: PhantomData,
569 },
570 TaskState::Rendezvous(receiver) => FallibleTaskState::Rendezvous(receiver),
571 })
572 }
573}
574
575impl Task<Box<dyn Any + Send + Sync>> {
576 pub fn downcast<T: Send + Sync + 'static>(self) -> Task<T> {
585 Task(TaskState::Downcast {
586 inner: Box::new(self),
587 marker: PhantomData,
588 })
589 }
590}
591
592impl<T> std::fmt::Debug for Task<T> {
593 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
594 match &self.0 {
595 TaskState::Ready(_) => f.debug_tuple("Task::Ready").finish(),
596 TaskState::Spawned(task) => f.debug_tuple("Task::Spawned").field(task).finish(),
597 TaskState::Downcast { inner, .. } => {
598 f.debug_tuple("Task::Downcast").field(inner).finish()
599 }
600 TaskState::Rendezvous(_) => f.debug_tuple("Task::Rendezvous").finish(),
601 }
602 }
603}
604
605#[must_use]
607pub struct FallibleTask<T>(FallibleTaskState<T>);
608
609enum FallibleTaskState<T> {
610 Ready(Option<T>),
612
613 Spawned(async_task::FallibleTask<T, RunnableMeta>),
615
616 Downcast {
618 inner: Box<FallibleTask<Box<dyn Any + Send + Sync>>>,
619 marker: PhantomData<fn() -> T>,
620 },
621
622 Rendezvous(RendezvousReceiver<T>),
624}
625
626impl<T> FallibleTask<T> {
627 pub fn ready(val: T) -> Self {
629 FallibleTask(FallibleTaskState::Ready(Some(val)))
630 }
631
632 pub fn detach(self) {
634 match self.0 {
635 FallibleTaskState::Ready(_) => {}
636 FallibleTaskState::Spawned(task) => task.detach(),
637 FallibleTaskState::Downcast { inner, .. } => inner.detach(),
638 FallibleTaskState::Rendezvous(receiver) => receiver.detach(),
639 }
640 }
641}
642
643impl<T: 'static> Future for FallibleTask<T> {
644 type Output = Option<T>;
645
646 fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
647 let this = unsafe { self.get_unchecked_mut() };
648 loop {
649 match &mut this.0 {
650 FallibleTaskState::Ready(val) => return Poll::Ready(val.take()),
651 FallibleTaskState::Spawned(task) => return Pin::new(task).poll(cx),
652 FallibleTaskState::Downcast { inner, .. } => {
653 return match Pin::new(inner.as_mut()).poll(cx) {
654 Poll::Ready(Some(boxed_any)) => Poll::Ready(Some(
655 *boxed_any
656 .downcast::<T>()
657 .expect("FallibleTask::poll: downcast type mismatch"),
658 )),
659 Poll::Ready(None) => Poll::Ready(None),
660 Poll::Pending => Poll::Pending,
661 };
662 }
663 FallibleTaskState::Rendezvous(receiver) => match receiver.poll_take(cx) {
664 Poll::Ready(task) => {
665 this.0 = task.fallible().0;
666 continue;
667 }
668 Poll::Pending => return Poll::Pending,
669 },
670 }
671 }
672 }
673}
674
675impl<T> std::fmt::Debug for FallibleTask<T> {
676 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
677 match &self.0 {
678 FallibleTaskState::Ready(_) => f.debug_tuple("FallibleTask::Ready").finish(),
679 FallibleTaskState::Spawned(task) => {
680 f.debug_tuple("FallibleTask::Spawned").field(task).finish()
681 }
682 FallibleTaskState::Downcast { inner, .. } => f
683 .debug_tuple("FallibleTask::Downcast")
684 .field(inner)
685 .finish(),
686 FallibleTaskState::Rendezvous(_) => f.debug_tuple("FallibleTask::Rendezvous").finish(),
687 }
688 }
689}
690
691impl<T: 'static> Future for Task<T> {
692 type Output = T;
693
694 fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
695 let this = unsafe { self.get_unchecked_mut() };
696 loop {
697 match &mut this.0 {
698 TaskState::Ready(val) => return Poll::Ready(val.take().unwrap()),
699 TaskState::Spawned(task) => return Pin::new(task).poll(cx),
700 TaskState::Downcast { inner, .. } => {
701 return match Pin::new(inner.as_mut()).poll(cx) {
702 Poll::Ready(boxed_any) => Poll::Ready(
703 *boxed_any
704 .downcast::<T>()
705 .expect("Task::poll: downcast type mismatch"),
706 ),
707 Poll::Pending => Poll::Pending,
708 };
709 }
710 TaskState::Rendezvous(receiver) => match receiver.poll_take(cx) {
711 Poll::Ready(task) => {
712 this.0 = task.0;
713 continue;
714 }
715 Poll::Pending => return Poll::Pending,
716 },
717 }
718 }
719 }
720}
721
722#[track_caller]
724fn spawn_local_with_source_location<Fut, S>(
725 future: Fut,
726 schedule: S,
727 metadata: RunnableMeta,
728) -> (
729 async_task::Runnable<RunnableMeta>,
730 async_task::Task<Fut::Output, RunnableMeta>,
731)
732where
733 Fut: Future + 'static,
734 Fut::Output: 'static,
735 S: async_task::Schedule<RunnableMeta> + Send + Sync + 'static,
736{
737 #[inline]
738 fn thread_id() -> ThreadId {
739 std::thread_local! {
740 static ID: ThreadId = thread::current().id();
741 }
742 ID.try_with(|id| *id)
743 .unwrap_or_else(|_| thread::current().id())
744 }
745
746 struct Checked<F> {
747 id: ThreadId,
748 inner: ManuallyDrop<F>,
749 location: &'static Location<'static>,
750 }
751
752 impl<F> Drop for Checked<F> {
753 fn drop(&mut self) {
754 assert_eq!(
755 self.id,
756 thread_id(),
757 "local task dropped by a thread that didn't spawn it. Task spawned at {}",
758 self.location
759 );
760 unsafe { ManuallyDrop::drop(&mut self.inner) };
764 }
765 }
766
767 impl<F: Future> Future for Checked<F> {
768 type Output = F::Output;
769
770 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
771 let this = unsafe { self.get_unchecked_mut() };
775 assert!(
776 this.id == thread_id(),
777 "local task polled by a thread that didn't spawn it. Task spawned at {}",
778 this.location
779 );
780 unsafe { Pin::new_unchecked(&mut *this.inner).poll(cx) }
785 }
786 }
787
788 let location = metadata.location;
789
790 let future = move |_| Checked {
791 id: thread_id(),
792 inner: ManuallyDrop::new(future),
793 location,
794 };
795
796 let builder = async_task::Builder::new().metadata(metadata);
797 unsafe { builder.spawn_unchecked(future, schedule) }
801}