Skip to main content

scheduler/
executor.rs

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/// A `!Send` executor pinned to a single session. Tasks spawned on it run in
18/// order on whichever thread drains the dispatch destination supplied at
19/// construction time — typically the main thread for the default session, or
20/// a dedicated OS thread for sessions created by `spawn_dedicated_thread`.
21#[derive(Clone)]
22pub struct LocalExecutor {
23    session_id: SessionId,
24    scheduler: Arc<dyn Scheduler>,
25    // Spawned tasks' schedule callbacks each hold an `Arc` clone of this
26    // closure, so the destination it captures stays alive as long as work
27    // could still land on it.
28    dispatch: Arc<dyn Fn(Runnable<RunnableMeta>) + Send + Sync>,
29    not_send: PhantomData<Rc<()>>,
30}
31
32impl LocalExecutor {
33    /// Constructs a local executor that runs spawned tasks by sending their
34    /// runnables through `dispatch`. The `scheduler` is retained for access to
35    /// clocks, timers, and other scheduler-level services.
36    ///
37    /// For the common case of routing runnables through
38    /// `Scheduler::schedule_local`, callers pass a closure that does exactly
39    /// that. `spawn_dedicated_thread` instead passes a closure that sends to
40    /// the dedicated thread's channel.
41    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    /// Like [`Self::spawn`], but routes the task's runnables through the
83    /// given dispatch destination instead of this executor's own. The
84    /// destination must deliver runnables to this executor's thread: the
85    /// future is polled and dropped on the spawning thread.
86    #[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    /// Block until the future completes or timeout occurs.
126    /// Returns Ok(output) if completed, Err(future) if timed out.
127    #[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    /// Spawn a closure on a fresh session pinned to its own [`LocalExecutor`].
165    /// The closure runs on a new OS thread under `PlatformScheduler`, or on
166    /// the test scheduler's loop under `TestScheduler`.
167    ///
168    /// The returned `Task` represents the dedicated work: dropping it cancels
169    /// the dedicated closure, `.await`ing it yields the closure's return
170    /// value, `.detach()`ing it lets the dedicated work run independently of
171    /// the caller.
172    #[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
186/// Boxes the user-supplied dedicated closure into the type-erased shape
187/// expected by [`Scheduler::spawn_dedicated`]. The user's `Fut::Output` is
188/// boxed as `Box<dyn Any + Send + Sync>` on the dedicated side and downcast
189/// back to `Fut::Output` by [`Task::downcast`] in the wrapper.
190fn 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    /// Spawns a future on a dedicated realtime thread for audio processing.
252    #[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    /// Spawn a closure on a fresh session pinned to its own [`LocalExecutor`].
296    /// The closure runs on a new OS thread under `PlatformScheduler`, or on
297    /// the test scheduler's loop under `TestScheduler`.
298    ///
299    /// The returned `Task` represents the dedicated work: dropping it cancels
300    /// the dedicated closure, `.await`ing it yields the closure's return
301    /// value, `.detach()`ing it lets the dedicated work run independently of
302    /// the caller.
303    #[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
317/// A long-lived handle to one dedicated session: every future spawned on it
318/// runs on that session's single thread, never on the background pool.
319///
320/// Use this when tasks rely on thread-local state being coherent across
321/// spawns, or when the per-thread setup cost of the work is high enough that
322/// it should be paid once. Creating the session costs a thread (on the web, a
323/// worker — expensive); each spawn afterwards is just a channel send.
324///
325/// Dropping the handle cancels the session: queued and future spawns resolve
326/// to cancelled tasks.
327pub struct DedicatedExecutor {
328    sender: flume::Sender<Runnable<RunnableMeta>>,
329    _session: Task<()>,
330}
331
332impl DedicatedExecutor {
333    /// Starts a dedicated session on `executor` and returns a handle that
334    /// spawns futures onto it. Under `TestScheduler` the session runs on the
335    /// deterministic test loop; no real thread is created.
336    #[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    /// Spawns a future onto the dedicated session's thread.
351    ///
352    /// The returned task has the usual semantics: dropping it cancels the
353    /// future, `.await`ing it yields the output, `.detach()`ing it lets it
354    /// run to completion on its own.
355    #[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/// Task is a primitive that allows work to happen in the background.
376///
377/// It implements [`Future`] so you can `.await` on it.
378///
379/// If you drop a task it will be cancelled immediately. Calling [`Task::detach`] allows
380/// the task to continue running, but with no way to return a value.
381#[must_use]
382pub struct Task<T>(TaskState<T>);
383
384enum TaskState<T> {
385    /// A task that is ready to return a value
386    Ready(Option<T>),
387
388    /// A task that is currently running.
389    Spawned(async_task::Task<T, RunnableMeta>),
390
391    /// A typed view of a [`Task<Box<dyn Any + Send + Sync>>`] obtained via
392    /// [`Task::downcast`]. The inner task drives the actual work; the
393    /// downcast layer just unwraps the `Box<dyn Any + Send + Sync>` on poll.
394    Downcast {
395        inner: Box<Task<Box<dyn Any + Send + Sync>>>,
396        marker: PhantomData<fn() -> T>,
397    },
398
399    /// A task whose real handle is delivered later by another thread (see
400    /// [`Task::rendezvous`]). Once delivered, polling replaces this state
401    /// with the delivered task's state.
402    Rendezvous(RendezvousReceiver<T>),
403}
404
405/// State shared between the two halves of a [`Task::rendezvous`] pair.
406enum RendezvousState<T> {
407    /// No task delivered yet; holds the consumer's waker if it polled.
408    Pending(Option<Waker>),
409    /// The producer delivered before the consumer consumed the task.
410    Delivered(Task<T>),
411    /// The consumer was dropped before delivery; delivery cancels the task.
412    Cancelled,
413    /// The consumer was detached before delivery; delivery detaches the task.
414    Detached,
415    /// The consumer took the delivered task.
416    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            // The producer applies the detached disposition on delivery.
454            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            // `Taken`, `Detached`, and `Cancelled` record dispositions that
473            // this drop must not overwrite: the receiver is also dropped as a
474            // normal side effect of taking or detaching.
475            _ => {}
476        }
477    }
478}
479
480pub(crate) struct RendezvousSender<T> {
481    shared: Arc<parking_lot::Mutex<RendezvousState<T>>>,
482}
483
484impl<T> RendezvousSender<T> {
485    /// Hands the real task to the rendezvous, applying the consumer's
486    /// disposition if it was dropped or detached before delivery.
487    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    /// Creates a new task that will resolve with the value
519    pub fn ready(val: T) -> Self {
520        Task(TaskState::Ready(Some(val)))
521    }
522
523    /// Creates a task whose real handle arrives later through the returned
524    /// sender, typically from another thread. Until delivery the task is
525    /// pending; afterwards it behaves exactly like the delivered task.
526    /// Dropping or detaching the task before delivery is honored on delivery.
527    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    /// Creates a Task from an async_task::Task
538    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    /// Detaching a task runs it to completion in the background
552    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    /// Converts this task into a fallible task that returns `Option<T>`.
562    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    /// Reinterprets the boxed output as a concrete `T` via downcast on
577    /// completion. Used by [`LocalExecutor::spawn_dedicated`] and
578    /// [`BackgroundExecutor::spawn_dedicated`] to recover the user closure's
579    /// `Fut::Output` from the dyn-safe [`Scheduler::spawn_dedicated`].
580    ///
581    /// Panics on poll if the inner output is not in fact a `T` -- a logic
582    /// error in whatever produced the inner task, since the downcast type is
583    /// chosen by the caller of `downcast`.
584    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/// A task that returns `Option<T>` instead of panicking when cancelled.
606#[must_use]
607pub struct FallibleTask<T>(FallibleTaskState<T>);
608
609enum FallibleTaskState<T> {
610    /// A task that is ready to return a value
611    Ready(Option<T>),
612
613    /// A task that is currently running (wraps async_task::FallibleTask).
614    Spawned(async_task::FallibleTask<T, RunnableMeta>),
615
616    /// Mirror of [`TaskState::Downcast`] for fallible tasks.
617    Downcast {
618        inner: Box<FallibleTask<Box<dyn Any + Send + Sync>>>,
619        marker: PhantomData<fn() -> T>,
620    },
621
622    /// Mirror of [`TaskState::Rendezvous`] for fallible tasks.
623    Rendezvous(RendezvousReceiver<T>),
624}
625
626impl<T> FallibleTask<T> {
627    /// Creates a new fallible task that will resolve with the value.
628    pub fn ready(val: T) -> Self {
629        FallibleTask(FallibleTaskState::Ready(Some(val)))
630    }
631
632    /// Detaching a task runs it to completion in the background.
633    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/// Variant of `async_task::spawn_local` that includes the source location of the spawn in panics.
723#[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            // SAFETY: `inner` is wrapped in `ManuallyDrop`, so this is the only
761            // place it is dropped. The thread check above ensures local futures
762            // are dropped on the thread that created them.
763            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            // SAFETY: We don't move any fields out of `self`; this mutable
772            // reference is only used to check metadata and to project the pin to
773            // `inner` below.
774            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            // SAFETY: `inner` is structurally pinned by `Checked`; after
781            // `Checked` is pinned, `inner` is never moved. The thread check
782            // above ensures the local future is only polled by its spawning
783            // thread.
784            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    // SAFETY: `Checked` enforces the invariants required by `spawn_unchecked`:
798    // the non-`Send` future is only polled and dropped on the thread that
799    // spawned it.
800    unsafe { builder.spawn_unchecked(future, schedule) }
801}