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 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
315pub struct DedicatedExecutor {
326 sender: flume::Sender<Runnable<RunnableMeta>>,
327 _session: Task<()>,
328}
329
330impl DedicatedExecutor {
331 #[track_caller]
335 pub fn new(executor: &BackgroundExecutor) -> Self {
336 let (sender, receiver) = flume::unbounded::<Runnable<RunnableMeta>>();
337 let session = executor.spawn_dedicated(move |_executor| async move {
338 while let Ok(runnable) = receiver.recv_async().await {
339 runnable.run();
340 }
341 });
342 Self {
343 sender,
344 _session: session,
345 }
346 }
347
348 #[track_caller]
354 pub fn spawn<F>(&self, future: F) -> Task<F::Output>
355 where
356 F: Future + Send + 'static,
357 F::Output: Send + 'static,
358 {
359 let sender = self.sender.clone();
360 let (runnable, task) = async_task::Builder::new()
361 .metadata(RunnableMeta::new_with_callers_location())
362 .spawn(
363 move |_| future,
364 move |runnable| {
365 let _ = sender.send(runnable);
366 },
367 );
368 runnable.schedule();
369 Task(TaskState::Spawned(task))
370 }
371}
372
373#[must_use]
380pub struct Task<T>(TaskState<T>);
381
382enum TaskState<T> {
383 Ready(Option<T>),
385
386 Spawned(async_task::Task<T, RunnableMeta>),
388
389 Downcast {
393 inner: Box<Task<Box<dyn Any + Send + Sync>>>,
394 marker: PhantomData<fn() -> T>,
395 },
396
397 Rendezvous(RendezvousReceiver<T>),
401}
402
403enum RendezvousState<T> {
405 Pending(Option<Waker>),
407 Delivered(Task<T>),
409 Cancelled,
411 Detached,
413 Taken,
415}
416
417pub(crate) struct RendezvousReceiver<T> {
418 shared: Arc<parking_lot::Mutex<RendezvousState<T>>>,
419}
420
421impl<T> RendezvousReceiver<T> {
422 fn poll_take(&self, cx: &mut Context) -> Poll<Task<T>> {
423 let mut state = self.shared.lock();
424 match std::mem::replace(&mut *state, RendezvousState::Taken) {
425 RendezvousState::Delivered(task) => Poll::Ready(task),
426 RendezvousState::Pending(_) => {
427 *state = RendezvousState::Pending(Some(cx.waker().clone()));
428 Poll::Pending
429 }
430 RendezvousState::Cancelled | RendezvousState::Detached | RendezvousState::Taken => {
431 unreachable!("a rendezvous task was polled after its receiver was consumed")
432 }
433 }
434 }
435
436 fn is_ready(&self) -> bool {
437 match &*self.shared.lock() {
438 RendezvousState::Delivered(task) => task.is_ready(),
439 _ => false,
440 }
441 }
442
443 fn detach(self) {
444 let mut state = self.shared.lock();
445 match std::mem::replace(&mut *state, RendezvousState::Detached) {
446 RendezvousState::Delivered(task) => {
447 *state = RendezvousState::Taken;
448 drop(state);
449 task.detach();
450 }
451 RendezvousState::Pending(_) => {}
453 RendezvousState::Cancelled | RendezvousState::Detached | RendezvousState::Taken => {
454 unreachable!("a rendezvous task was detached after its receiver was consumed")
455 }
456 }
457 }
458}
459
460impl<T> Drop for RendezvousReceiver<T> {
461 fn drop(&mut self) {
462 let mut state = self.shared.lock();
463 match &*state {
464 RendezvousState::Pending(_) => *state = RendezvousState::Cancelled,
465 RendezvousState::Delivered(_) => {
466 let delivered = std::mem::replace(&mut *state, RendezvousState::Cancelled);
467 drop(state);
468 drop(delivered);
469 }
470 _ => {}
474 }
475 }
476}
477
478pub(crate) struct RendezvousSender<T> {
479 shared: Arc<parking_lot::Mutex<RendezvousState<T>>>,
480}
481
482impl<T> RendezvousSender<T> {
483 pub(crate) fn deliver(self, task: Task<T>) {
486 let mut state = self.shared.lock();
487 match std::mem::replace(&mut *state, RendezvousState::Delivered(task)) {
488 RendezvousState::Pending(waker) => {
489 drop(state);
490 if let Some(waker) = waker {
491 waker.wake();
492 }
493 }
494 RendezvousState::Cancelled => {
495 let delivered = std::mem::replace(&mut *state, RendezvousState::Cancelled);
496 drop(state);
497 drop(delivered);
498 }
499 RendezvousState::Detached => {
500 let RendezvousState::Delivered(task) =
501 std::mem::replace(&mut *state, RendezvousState::Detached)
502 else {
503 unreachable!("the delivered task was just stored");
504 };
505 drop(state);
506 task.detach();
507 }
508 RendezvousState::Delivered(_) | RendezvousState::Taken => {
509 unreachable!("a rendezvous task was delivered twice")
510 }
511 }
512 }
513}
514
515impl<T> Task<T> {
516 pub fn ready(val: T) -> Self {
518 Task(TaskState::Ready(Some(val)))
519 }
520
521 pub(crate) fn rendezvous() -> (Self, RendezvousSender<T>) {
526 let shared = Arc::new(parking_lot::Mutex::new(RendezvousState::Pending(None)));
527 (
528 Task(TaskState::Rendezvous(RendezvousReceiver {
529 shared: shared.clone(),
530 })),
531 RendezvousSender { shared },
532 )
533 }
534
535 pub fn from_async_task(task: async_task::Task<T, RunnableMeta>) -> Self {
537 Task(TaskState::Spawned(task))
538 }
539
540 pub fn is_ready(&self) -> bool {
541 match &self.0 {
542 TaskState::Ready(_) => true,
543 TaskState::Spawned(task) => task.is_finished(),
544 TaskState::Downcast { inner, .. } => inner.is_ready(),
545 TaskState::Rendezvous(receiver) => receiver.is_ready(),
546 }
547 }
548
549 pub fn detach(self) {
551 match self {
552 Task(TaskState::Ready(_)) => {}
553 Task(TaskState::Spawned(task)) => task.detach(),
554 Task(TaskState::Downcast { inner, .. }) => inner.detach(),
555 Task(TaskState::Rendezvous(receiver)) => receiver.detach(),
556 }
557 }
558
559 pub fn fallible(self) -> FallibleTask<T> {
561 FallibleTask(match self.0 {
562 TaskState::Ready(val) => FallibleTaskState::Ready(val),
563 TaskState::Spawned(task) => FallibleTaskState::Spawned(task.fallible()),
564 TaskState::Downcast { inner, .. } => FallibleTaskState::Downcast {
565 inner: Box::new(inner.fallible()),
566 marker: PhantomData,
567 },
568 TaskState::Rendezvous(receiver) => FallibleTaskState::Rendezvous(receiver),
569 })
570 }
571}
572
573impl Task<Box<dyn Any + Send + Sync>> {
574 pub fn downcast<T: Send + Sync + 'static>(self) -> Task<T> {
583 Task(TaskState::Downcast {
584 inner: Box::new(self),
585 marker: PhantomData,
586 })
587 }
588}
589
590impl<T> std::fmt::Debug for Task<T> {
591 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
592 match &self.0 {
593 TaskState::Ready(_) => f.debug_tuple("Task::Ready").finish(),
594 TaskState::Spawned(task) => f.debug_tuple("Task::Spawned").field(task).finish(),
595 TaskState::Downcast { inner, .. } => {
596 f.debug_tuple("Task::Downcast").field(inner).finish()
597 }
598 TaskState::Rendezvous(_) => f.debug_tuple("Task::Rendezvous").finish(),
599 }
600 }
601}
602
603#[must_use]
605pub struct FallibleTask<T>(FallibleTaskState<T>);
606
607enum FallibleTaskState<T> {
608 Ready(Option<T>),
610
611 Spawned(async_task::FallibleTask<T, RunnableMeta>),
613
614 Downcast {
616 inner: Box<FallibleTask<Box<dyn Any + Send + Sync>>>,
617 marker: PhantomData<fn() -> T>,
618 },
619
620 Rendezvous(RendezvousReceiver<T>),
622}
623
624impl<T> FallibleTask<T> {
625 pub fn ready(val: T) -> Self {
627 FallibleTask(FallibleTaskState::Ready(Some(val)))
628 }
629
630 pub fn detach(self) {
632 match self.0 {
633 FallibleTaskState::Ready(_) => {}
634 FallibleTaskState::Spawned(task) => task.detach(),
635 FallibleTaskState::Downcast { inner, .. } => inner.detach(),
636 FallibleTaskState::Rendezvous(receiver) => receiver.detach(),
637 }
638 }
639}
640
641impl<T: 'static> Future for FallibleTask<T> {
642 type Output = Option<T>;
643
644 fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
645 let this = unsafe { self.get_unchecked_mut() };
646 loop {
647 match &mut this.0 {
648 FallibleTaskState::Ready(val) => return Poll::Ready(val.take()),
649 FallibleTaskState::Spawned(task) => return Pin::new(task).poll(cx),
650 FallibleTaskState::Downcast { inner, .. } => {
651 return match Pin::new(inner.as_mut()).poll(cx) {
652 Poll::Ready(Some(boxed_any)) => Poll::Ready(Some(
653 *boxed_any
654 .downcast::<T>()
655 .expect("FallibleTask::poll: downcast type mismatch"),
656 )),
657 Poll::Ready(None) => Poll::Ready(None),
658 Poll::Pending => Poll::Pending,
659 };
660 }
661 FallibleTaskState::Rendezvous(receiver) => match receiver.poll_take(cx) {
662 Poll::Ready(task) => {
663 this.0 = task.fallible().0;
664 continue;
665 }
666 Poll::Pending => return Poll::Pending,
667 },
668 }
669 }
670 }
671}
672
673impl<T> std::fmt::Debug for FallibleTask<T> {
674 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
675 match &self.0 {
676 FallibleTaskState::Ready(_) => f.debug_tuple("FallibleTask::Ready").finish(),
677 FallibleTaskState::Spawned(task) => {
678 f.debug_tuple("FallibleTask::Spawned").field(task).finish()
679 }
680 FallibleTaskState::Downcast { inner, .. } => f
681 .debug_tuple("FallibleTask::Downcast")
682 .field(inner)
683 .finish(),
684 FallibleTaskState::Rendezvous(_) => f.debug_tuple("FallibleTask::Rendezvous").finish(),
685 }
686 }
687}
688
689impl<T: 'static> Future for Task<T> {
690 type Output = T;
691
692 fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
693 let this = unsafe { self.get_unchecked_mut() };
694 loop {
695 match &mut this.0 {
696 TaskState::Ready(val) => return Poll::Ready(val.take().unwrap()),
697 TaskState::Spawned(task) => return Pin::new(task).poll(cx),
698 TaskState::Downcast { inner, .. } => {
699 return match Pin::new(inner.as_mut()).poll(cx) {
700 Poll::Ready(boxed_any) => Poll::Ready(
701 *boxed_any
702 .downcast::<T>()
703 .expect("Task::poll: downcast type mismatch"),
704 ),
705 Poll::Pending => Poll::Pending,
706 };
707 }
708 TaskState::Rendezvous(receiver) => match receiver.poll_take(cx) {
709 Poll::Ready(task) => {
710 this.0 = task.0;
711 continue;
712 }
713 Poll::Pending => return Poll::Pending,
714 },
715 }
716 }
717 }
718}
719
720#[track_caller]
722fn spawn_local_with_source_location<Fut, S>(
723 future: Fut,
724 schedule: S,
725 metadata: RunnableMeta,
726) -> (
727 async_task::Runnable<RunnableMeta>,
728 async_task::Task<Fut::Output, RunnableMeta>,
729)
730where
731 Fut: Future + 'static,
732 Fut::Output: 'static,
733 S: async_task::Schedule<RunnableMeta> + Send + Sync + 'static,
734{
735 #[inline]
736 fn thread_id() -> ThreadId {
737 std::thread_local! {
738 static ID: ThreadId = thread::current().id();
739 }
740 ID.try_with(|id| *id)
741 .unwrap_or_else(|_| thread::current().id())
742 }
743
744 struct Checked<F> {
745 id: ThreadId,
746 inner: ManuallyDrop<F>,
747 location: &'static Location<'static>,
748 }
749
750 impl<F> Drop for Checked<F> {
751 fn drop(&mut self) {
752 assert_eq!(
753 self.id,
754 thread_id(),
755 "local task dropped by a thread that didn't spawn it. Task spawned at {}",
756 self.location
757 );
758 unsafe { ManuallyDrop::drop(&mut self.inner) };
762 }
763 }
764
765 impl<F: Future> Future for Checked<F> {
766 type Output = F::Output;
767
768 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
769 let this = unsafe { self.get_unchecked_mut() };
773 assert!(
774 this.id == thread_id(),
775 "local task polled by a thread that didn't spawn it. Task spawned at {}",
776 this.location
777 );
778 unsafe { Pin::new_unchecked(&mut *this.inner).poll(cx) }
783 }
784 }
785
786 let location = metadata.location;
787
788 let future = move |_| Checked {
789 id: thread_id(),
790 inner: ManuallyDrop::new(future),
791 location,
792 };
793
794 let builder = async_task::Builder::new().metadata(metadata);
795 unsafe { builder.spawn_unchecked(future, schedule) }
799}