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 to_send(&self) -> SendLocalExecutor {
56 SendLocalExecutor {
57 _scheduler: self.scheduler.clone(),
58 dispatch: self.dispatch.clone(),
59 }
60 }
61
62 pub fn session_id(&self) -> SessionId {
63 self.session_id
64 }
65
66 pub fn scheduler(&self) -> &Arc<dyn Scheduler> {
67 &self.scheduler
68 }
69
70 pub fn is_test(&self) -> bool {
72 self.scheduler.as_test().is_some()
73 }
74
75 #[track_caller]
76 pub fn spawn<F>(&self, future: F) -> Task<F::Output>
77 where
78 F: Future + 'static,
79 F::Output: 'static,
80 {
81 let schedule = self.schedule();
82 let location = Location::caller();
83 let (runnable, task) = spawn_local_with_source_location(
84 future,
85 schedule,
86 RunnableMeta {
87 location,
88 spawned: crate::SpawnTime(Instant::now()),
89 },
90 );
91 runnable.schedule();
92 Task(TaskState::Spawned(task))
93 }
94
95 #[track_caller]
100 pub fn spawn_with_dispatch<F>(
101 &self,
102 future: F,
103 dispatch: impl Fn(Runnable<RunnableMeta>) + Send + Sync + 'static,
104 ) -> Task<F::Output>
105 where
106 F: Future + 'static,
107 F::Output: 'static,
108 {
109 let location = Location::caller();
110 let (runnable, task) = spawn_local_with_source_location(
111 future,
112 dispatch,
113 RunnableMeta {
114 location,
115 spawned: crate::SpawnTime(Instant::now()),
116 },
117 );
118 runnable.schedule();
119 Task(TaskState::Spawned(task))
120 }
121
122 #[cfg(not(target_family = "wasm"))]
123 pub fn block_on<Fut: Future>(&self, future: Fut) -> Fut::Output {
124 use std::cell::Cell;
125
126 let output = Cell::new(None);
127 let future = async {
128 output.set(Some(future.await));
129 };
130 let mut future = std::pin::pin!(future);
131
132 self.scheduler
133 .block(Some(self.session_id), future.as_mut(), None);
134
135 output.take().expect("block_on future did not complete")
136 }
137
138 #[cfg(not(target_family = "wasm"))]
141 pub fn block_with_timeout<Fut: Future>(
142 &self,
143 timeout: Duration,
144 future: Fut,
145 ) -> Result<Fut::Output, impl Future<Output = Fut::Output> + use<Fut>> {
146 use std::cell::Cell;
147
148 let output = Cell::new(None);
149 let mut future = Box::pin(future);
150
151 {
152 let future_ref = &mut future;
153 let wrapper = async {
154 output.set(Some(future_ref.await));
155 };
156 let mut wrapper = std::pin::pin!(wrapper);
157
158 self.scheduler
159 .block(Some(self.session_id), wrapper.as_mut(), Some(timeout));
160 }
161
162 match output.take() {
163 Some(value) => Ok(value),
164 None => Err(future),
165 }
166 }
167
168 #[track_caller]
169 pub fn timer(&self, duration: Duration) -> Timer {
170 self.scheduler.timer(duration)
171 }
172
173 pub fn now(&self) -> Instant {
174 self.scheduler.clock().now()
175 }
176
177 #[track_caller]
186 pub fn spawn_dedicated<F, Fut>(&self, f: F) -> Task<Fut::Output>
187 where
188 F: FnOnce(LocalExecutor) -> Fut + Send + 'static,
189 Fut: Future + 'static,
190 Fut::Output: Send + Sync + 'static,
191 {
192 self.scheduler
193 .clone()
194 .spawn_dedicated(box_dedicated(f))
195 .downcast::<Fut::Output>()
196 }
197
198 fn schedule(&self) -> impl Fn(Runnable<RunnableMeta>) + Send + Sync + 'static {
199 let dispatch = self.dispatch.clone();
200 move |runnable| dispatch(runnable)
201 }
202}
203
204#[derive(Clone)]
208pub struct SendLocalExecutor {
209 _scheduler: Arc<dyn Scheduler>,
210 dispatch: Arc<dyn Fn(Runnable<RunnableMeta>) + Send + Sync>,
211}
212
213impl SendLocalExecutor {
214 #[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 let dispatch = self.dispatch.clone();
222 let location = Location::caller();
223 let (runnable, task) = async_task::Builder::new()
224 .metadata(RunnableMeta {
225 location,
226 spawned: crate::SpawnTime(Instant::now()),
227 })
228 .spawn(move |_| future, move |runnable| dispatch(runnable));
229 runnable.schedule();
230 Task(TaskState::Spawned(task))
231 }
232}
233
234fn box_dedicated<F, Fut>(
239 f: F,
240) -> Box<
241 dyn FnOnce(LocalExecutor) -> Pin<Box<dyn Future<Output = Box<dyn Any + Send + Sync>> + 'static>>
242 + Send
243 + 'static,
244>
245where
246 F: FnOnce(LocalExecutor) -> Fut + Send + 'static,
247 Fut: Future + 'static,
248 Fut::Output: Send + Sync + 'static,
249{
250 Box::new(move |executor| {
251 Box::pin(async move { Box::new(f(executor).await) as Box<dyn Any + Send + Sync> })
252 })
253}
254
255#[derive(Clone)]
256pub struct BackgroundExecutor {
257 scheduler: Arc<dyn Scheduler>,
258}
259
260impl BackgroundExecutor {
261 pub fn new(scheduler: Arc<dyn Scheduler>) -> Self {
262 Self { scheduler }
263 }
264
265 #[track_caller]
266 pub fn spawn<F>(&self, future: F) -> Task<F::Output>
267 where
268 F: Future + Send + 'static,
269 F::Output: Send + 'static,
270 {
271 self.spawn_with_priority(Priority::default(), future)
272 }
273
274 #[track_caller]
275 pub fn spawn_with_priority<F>(&self, priority: Priority, future: F) -> Task<F::Output>
276 where
277 F: Future + Send + 'static,
278 F::Output: Send + 'static,
279 {
280 let schedule = self.schedule_with_priority(priority);
281 let location = Location::caller();
282 let (runnable, task) = async_task::Builder::new()
283 .metadata(RunnableMeta {
284 location,
285 spawned: crate::SpawnTime(Instant::now()),
286 })
287 .spawn(move |_| future, schedule);
288 runnable.schedule();
289 Task(TaskState::Spawned(task))
290 }
291
292 #[track_caller]
294 pub fn spawn_realtime<F>(&self, future: F) -> Task<F::Output>
295 where
296 F: Future + Send + 'static,
297 F::Output: Send + 'static,
298 {
299 let location = Location::caller();
300 let (tx, rx) = flume::bounded::<async_task::Runnable<RunnableMeta>>(1);
301
302 self.scheduler.spawn_realtime(Box::new(move || {
303 while let Ok(runnable) = rx.recv() {
304 runnable.run();
305 }
306 }));
307
308 let (runnable, task) = async_task::Builder::new()
309 .metadata(RunnableMeta {
310 location,
311 spawned: crate::SpawnTime(Instant::now()),
312 })
313 .spawn(
314 move |_| future,
315 move |runnable| {
316 let _ = tx.send(runnable);
317 },
318 );
319 runnable.schedule();
320 Task(TaskState::Spawned(task))
321 }
322
323 #[track_caller]
324 pub fn timer(&self, duration: Duration) -> Timer {
325 self.scheduler.timer(duration)
326 }
327
328 pub fn now(&self) -> Instant {
329 self.scheduler.clock().now()
330 }
331
332 pub fn scheduler(&self) -> &Arc<dyn Scheduler> {
333 &self.scheduler
334 }
335
336 pub fn is_test(&self) -> bool {
338 self.scheduler.as_test().is_some()
339 }
340
341 #[track_caller]
350 pub fn spawn_dedicated<F, Fut>(&self, f: F) -> Task<Fut::Output>
351 where
352 F: FnOnce(LocalExecutor) -> Fut + Send + 'static,
353 Fut: Future + 'static,
354 Fut::Output: Send + Sync + 'static,
355 {
356 self.scheduler
357 .clone()
358 .spawn_dedicated(box_dedicated(f))
359 .downcast::<Fut::Output>()
360 }
361
362 fn schedule_with_priority(
363 &self,
364 priority: Priority,
365 ) -> impl Fn(Runnable<RunnableMeta>) + Send + Sync + 'static {
366 let scheduler = Arc::downgrade(&self.scheduler);
367 move |runnable| {
368 if let Some(scheduler) = scheduler.upgrade() {
369 scheduler.schedule_background_with_priority(runnable, priority);
370 }
371 }
372 }
373}
374
375pub struct DedicatedExecutor {
386 sender: flume::Sender<Runnable<RunnableMeta>>,
387 _session: Task<()>,
388}
389
390impl DedicatedExecutor {
391 #[track_caller]
395 pub fn new(executor: &BackgroundExecutor) -> Self {
396 let (sender, receiver) = flume::unbounded::<Runnable<RunnableMeta>>();
397 let session = executor.spawn_dedicated(move |_executor| async move {
398 while let Ok(runnable) = receiver.recv_async().await {
399 runnable.run();
400 }
401 });
402 Self {
403 sender,
404 _session: session,
405 }
406 }
407
408 #[track_caller]
414 pub fn spawn<F>(&self, future: F) -> Task<F::Output>
415 where
416 F: Future + Send + 'static,
417 F::Output: Send + 'static,
418 {
419 let sender = self.sender.clone();
420 let (runnable, task) = async_task::Builder::new()
421 .metadata(RunnableMeta::new_with_callers_location())
422 .spawn(
423 move |_| future,
424 move |runnable| {
425 let _ = sender.send(runnable);
426 },
427 );
428 runnable.schedule();
429 Task(TaskState::Spawned(task))
430 }
431}
432
433#[must_use]
440pub struct Task<T>(TaskState<T>);
441
442enum TaskState<T> {
443 Ready(Option<T>),
445
446 Spawned(async_task::Task<T, RunnableMeta>),
448
449 Downcast {
453 inner: Box<Task<Box<dyn Any + Send + Sync>>>,
454 marker: PhantomData<fn() -> T>,
455 },
456
457 Rendezvous(RendezvousReceiver<T>),
461}
462
463enum RendezvousState<T> {
465 Pending(Option<Waker>),
467 Delivered(Task<T>),
469 Cancelled,
471 Detached,
473 Taken,
475}
476
477pub(crate) struct RendezvousReceiver<T> {
478 shared: Arc<parking_lot::Mutex<RendezvousState<T>>>,
479}
480
481impl<T> RendezvousReceiver<T> {
482 fn poll_take(&self, cx: &mut Context) -> Poll<Task<T>> {
483 let mut state = self.shared.lock();
484 match std::mem::replace(&mut *state, RendezvousState::Taken) {
485 RendezvousState::Delivered(task) => Poll::Ready(task),
486 RendezvousState::Pending(_) => {
487 *state = RendezvousState::Pending(Some(cx.waker().clone()));
488 Poll::Pending
489 }
490 RendezvousState::Cancelled | RendezvousState::Detached | RendezvousState::Taken => {
491 unreachable!("a rendezvous task was polled after its receiver was consumed")
492 }
493 }
494 }
495
496 fn is_ready(&self) -> bool {
497 match &*self.shared.lock() {
498 RendezvousState::Delivered(task) => task.is_ready(),
499 _ => false,
500 }
501 }
502
503 fn detach(self) {
504 let mut state = self.shared.lock();
505 match std::mem::replace(&mut *state, RendezvousState::Detached) {
506 RendezvousState::Delivered(task) => {
507 *state = RendezvousState::Taken;
508 drop(state);
509 task.detach();
510 }
511 RendezvousState::Pending(_) => {}
513 RendezvousState::Cancelled | RendezvousState::Detached | RendezvousState::Taken => {
514 unreachable!("a rendezvous task was detached after its receiver was consumed")
515 }
516 }
517 }
518}
519
520impl<T> Drop for RendezvousReceiver<T> {
521 fn drop(&mut self) {
522 let mut state = self.shared.lock();
523 match &*state {
524 RendezvousState::Pending(_) => *state = RendezvousState::Cancelled,
525 RendezvousState::Delivered(_) => {
526 let delivered = std::mem::replace(&mut *state, RendezvousState::Cancelled);
527 drop(state);
528 drop(delivered);
529 }
530 _ => {}
534 }
535 }
536}
537
538pub(crate) struct RendezvousSender<T> {
539 shared: Arc<parking_lot::Mutex<RendezvousState<T>>>,
540}
541
542impl<T> RendezvousSender<T> {
543 pub(crate) fn deliver(self, task: Task<T>) {
546 let mut state = self.shared.lock();
547 match std::mem::replace(&mut *state, RendezvousState::Delivered(task)) {
548 RendezvousState::Pending(waker) => {
549 drop(state);
550 if let Some(waker) = waker {
551 waker.wake();
552 }
553 }
554 RendezvousState::Cancelled => {
555 let delivered = std::mem::replace(&mut *state, RendezvousState::Cancelled);
556 drop(state);
557 drop(delivered);
558 }
559 RendezvousState::Detached => {
560 let RendezvousState::Delivered(task) =
561 std::mem::replace(&mut *state, RendezvousState::Detached)
562 else {
563 unreachable!("the delivered task was just stored");
564 };
565 drop(state);
566 task.detach();
567 }
568 RendezvousState::Delivered(_) | RendezvousState::Taken => {
569 unreachable!("a rendezvous task was delivered twice")
570 }
571 }
572 }
573}
574
575impl<T> Task<T> {
576 pub fn ready(val: T) -> Self {
578 Task(TaskState::Ready(Some(val)))
579 }
580
581 pub(crate) fn rendezvous() -> (Self, RendezvousSender<T>) {
586 let shared = Arc::new(parking_lot::Mutex::new(RendezvousState::Pending(None)));
587 (
588 Task(TaskState::Rendezvous(RendezvousReceiver {
589 shared: shared.clone(),
590 })),
591 RendezvousSender { shared },
592 )
593 }
594
595 pub fn from_async_task(task: async_task::Task<T, RunnableMeta>) -> Self {
597 Task(TaskState::Spawned(task))
598 }
599
600 pub fn is_ready(&self) -> bool {
601 match &self.0 {
602 TaskState::Ready(_) => true,
603 TaskState::Spawned(task) => task.is_finished(),
604 TaskState::Downcast { inner, .. } => inner.is_ready(),
605 TaskState::Rendezvous(receiver) => receiver.is_ready(),
606 }
607 }
608
609 pub fn detach(self) {
611 match self {
612 Task(TaskState::Ready(_)) => {}
613 Task(TaskState::Spawned(task)) => task.detach(),
614 Task(TaskState::Downcast { inner, .. }) => inner.detach(),
615 Task(TaskState::Rendezvous(receiver)) => receiver.detach(),
616 }
617 }
618
619 pub fn fallible(self) -> FallibleTask<T> {
621 FallibleTask(match self.0 {
622 TaskState::Ready(val) => FallibleTaskState::Ready(val),
623 TaskState::Spawned(task) => FallibleTaskState::Spawned(task.fallible()),
624 TaskState::Downcast { inner, .. } => FallibleTaskState::Downcast {
625 inner: Box::new(inner.fallible()),
626 marker: PhantomData,
627 },
628 TaskState::Rendezvous(receiver) => FallibleTaskState::Rendezvous(receiver),
629 })
630 }
631}
632
633impl Task<Box<dyn Any + Send + Sync>> {
634 pub fn downcast<T: Send + Sync + 'static>(self) -> Task<T> {
643 Task(TaskState::Downcast {
644 inner: Box::new(self),
645 marker: PhantomData,
646 })
647 }
648}
649
650impl<T> std::fmt::Debug for Task<T> {
651 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
652 match &self.0 {
653 TaskState::Ready(_) => f.debug_tuple("Task::Ready").finish(),
654 TaskState::Spawned(task) => f.debug_tuple("Task::Spawned").field(task).finish(),
655 TaskState::Downcast { inner, .. } => {
656 f.debug_tuple("Task::Downcast").field(inner).finish()
657 }
658 TaskState::Rendezvous(_) => f.debug_tuple("Task::Rendezvous").finish(),
659 }
660 }
661}
662
663#[must_use]
665pub struct FallibleTask<T>(FallibleTaskState<T>);
666
667enum FallibleTaskState<T> {
668 Ready(Option<T>),
670
671 Spawned(async_task::FallibleTask<T, RunnableMeta>),
673
674 Downcast {
676 inner: Box<FallibleTask<Box<dyn Any + Send + Sync>>>,
677 marker: PhantomData<fn() -> T>,
678 },
679
680 Rendezvous(RendezvousReceiver<T>),
682}
683
684impl<T> FallibleTask<T> {
685 pub fn ready(val: T) -> Self {
687 FallibleTask(FallibleTaskState::Ready(Some(val)))
688 }
689
690 pub fn detach(self) {
692 match self.0 {
693 FallibleTaskState::Ready(_) => {}
694 FallibleTaskState::Spawned(task) => task.detach(),
695 FallibleTaskState::Downcast { inner, .. } => inner.detach(),
696 FallibleTaskState::Rendezvous(receiver) => receiver.detach(),
697 }
698 }
699}
700
701impl<T: 'static> Future for FallibleTask<T> {
702 type Output = Option<T>;
703
704 fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
705 let this = unsafe { self.get_unchecked_mut() };
706 loop {
707 match &mut this.0 {
708 FallibleTaskState::Ready(val) => return Poll::Ready(val.take()),
709 FallibleTaskState::Spawned(task) => return Pin::new(task).poll(cx),
710 FallibleTaskState::Downcast { inner, .. } => {
711 return match Pin::new(inner.as_mut()).poll(cx) {
712 Poll::Ready(Some(boxed_any)) => Poll::Ready(Some(
713 *boxed_any
714 .downcast::<T>()
715 .expect("FallibleTask::poll: downcast type mismatch"),
716 )),
717 Poll::Ready(None) => Poll::Ready(None),
718 Poll::Pending => Poll::Pending,
719 };
720 }
721 FallibleTaskState::Rendezvous(receiver) => match receiver.poll_take(cx) {
722 Poll::Ready(task) => {
723 this.0 = task.fallible().0;
724 continue;
725 }
726 Poll::Pending => return Poll::Pending,
727 },
728 }
729 }
730 }
731}
732
733impl<T> std::fmt::Debug for FallibleTask<T> {
734 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
735 match &self.0 {
736 FallibleTaskState::Ready(_) => f.debug_tuple("FallibleTask::Ready").finish(),
737 FallibleTaskState::Spawned(task) => {
738 f.debug_tuple("FallibleTask::Spawned").field(task).finish()
739 }
740 FallibleTaskState::Downcast { inner, .. } => f
741 .debug_tuple("FallibleTask::Downcast")
742 .field(inner)
743 .finish(),
744 FallibleTaskState::Rendezvous(_) => f.debug_tuple("FallibleTask::Rendezvous").finish(),
745 }
746 }
747}
748
749impl<T: 'static> Future for Task<T> {
750 type Output = T;
751
752 fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
753 let this = unsafe { self.get_unchecked_mut() };
754 loop {
755 match &mut this.0 {
756 TaskState::Ready(val) => return Poll::Ready(val.take().unwrap()),
757 TaskState::Spawned(task) => return Pin::new(task).poll(cx),
758 TaskState::Downcast { inner, .. } => {
759 return match Pin::new(inner.as_mut()).poll(cx) {
760 Poll::Ready(boxed_any) => Poll::Ready(
761 *boxed_any
762 .downcast::<T>()
763 .expect("Task::poll: downcast type mismatch"),
764 ),
765 Poll::Pending => Poll::Pending,
766 };
767 }
768 TaskState::Rendezvous(receiver) => match receiver.poll_take(cx) {
769 Poll::Ready(task) => {
770 this.0 = task.0;
771 continue;
772 }
773 Poll::Pending => return Poll::Pending,
774 },
775 }
776 }
777 }
778}
779
780#[track_caller]
782fn spawn_local_with_source_location<Fut, S>(
783 future: Fut,
784 schedule: S,
785 metadata: RunnableMeta,
786) -> (
787 async_task::Runnable<RunnableMeta>,
788 async_task::Task<Fut::Output, RunnableMeta>,
789)
790where
791 Fut: Future + 'static,
792 Fut::Output: 'static,
793 S: async_task::Schedule<RunnableMeta> + Send + Sync + 'static,
794{
795 #[inline]
796 fn thread_id() -> ThreadId {
797 std::thread_local! {
798 static ID: ThreadId = thread::current().id();
799 }
800 ID.try_with(|id| *id)
801 .unwrap_or_else(|_| thread::current().id())
802 }
803
804 struct Checked<F> {
805 id: ThreadId,
806 inner: ManuallyDrop<F>,
807 location: &'static Location<'static>,
808 }
809
810 impl<F> Drop for Checked<F> {
811 fn drop(&mut self) {
812 assert_eq!(
813 self.id,
814 thread_id(),
815 "local task dropped by a thread that didn't spawn it. Task spawned at {}",
816 self.location
817 );
818 unsafe { ManuallyDrop::drop(&mut self.inner) };
822 }
823 }
824
825 impl<F: Future> Future for Checked<F> {
826 type Output = F::Output;
827
828 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
829 let this = unsafe { self.get_unchecked_mut() };
833 assert!(
834 this.id == thread_id(),
835 "local task polled by a thread that didn't spawn it. Task spawned at {}",
836 this.location
837 );
838 unsafe { Pin::new_unchecked(&mut *this.inner).poll(cx) }
843 }
844 }
845
846 let location = metadata.location;
847
848 let future = move |_| Checked {
849 id: thread_id(),
850 inner: ManuallyDrop::new(future),
851 location,
852 };
853
854 let builder = async_task::Builder::new().metadata(metadata);
855 unsafe { builder.spawn_unchecked(future, schedule) }
859}