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    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    /// Block until the future completes or timeout occurs.
125    /// Returns Ok(output) if completed, Err(future) if timed out.
126    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    /// Spawn a closure on a fresh session pinned to its own [`LocalExecutor`].
163    /// The closure runs on a new OS thread under `PlatformScheduler`, or on
164    /// the test scheduler's loop under `TestScheduler`.
165    ///
166    /// The returned `Task` represents the dedicated work: dropping it cancels
167    /// the dedicated closure, `.await`ing it yields the closure's return
168    /// value, `.detach()`ing it lets the dedicated work run independently of
169    /// the caller.
170    #[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
184/// Boxes the user-supplied dedicated closure into the type-erased shape
185/// expected by [`Scheduler::spawn_dedicated`]. The user's `Fut::Output` is
186/// boxed as `Box<dyn Any + Send + Sync>` on the dedicated side and downcast
187/// back to `Fut::Output` by [`Task::downcast`] in the wrapper.
188fn 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    /// Spawns a future on a dedicated realtime thread for audio processing.
250    #[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    /// Spawn a closure on a fresh session pinned to its own [`LocalExecutor`].
294    /// The closure runs on a new OS thread under `PlatformScheduler`, or on
295    /// the test scheduler's loop under `TestScheduler`.
296    ///
297    /// The returned `Task` represents the dedicated work: dropping it cancels
298    /// the dedicated closure, `.await`ing it yields the closure's return
299    /// value, `.detach()`ing it lets the dedicated work run independently of
300    /// the caller.
301    #[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/// A long-lived handle to one dedicated session: every future spawned on it
316/// runs on that session's single thread, never on the background pool.
317///
318/// Use this when tasks rely on thread-local state being coherent across
319/// spawns, or when the per-thread setup cost of the work is high enough that
320/// it should be paid once. Creating the session costs a thread (on the web, a
321/// worker — expensive); each spawn afterwards is just a channel send.
322///
323/// Dropping the handle cancels the session: queued and future spawns resolve
324/// to cancelled tasks.
325pub struct DedicatedExecutor {
326    sender: flume::Sender<Runnable<RunnableMeta>>,
327    _session: Task<()>,
328}
329
330impl DedicatedExecutor {
331    /// Starts a dedicated session on `executor` and returns a handle that
332    /// spawns futures onto it. Under `TestScheduler` the session runs on the
333    /// deterministic test loop; no real thread is created.
334    #[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    /// Spawns a future onto the dedicated session's thread.
349    ///
350    /// The returned task has the usual semantics: dropping it cancels the
351    /// future, `.await`ing it yields the output, `.detach()`ing it lets it
352    /// run to completion on its own.
353    #[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/// Task is a primitive that allows work to happen in the background.
374///
375/// It implements [`Future`] so you can `.await` on it.
376///
377/// If you drop a task it will be cancelled immediately. Calling [`Task::detach`] allows
378/// the task to continue running, but with no way to return a value.
379#[must_use]
380pub struct Task<T>(TaskState<T>);
381
382enum TaskState<T> {
383    /// A task that is ready to return a value
384    Ready(Option<T>),
385
386    /// A task that is currently running.
387    Spawned(async_task::Task<T, RunnableMeta>),
388
389    /// A typed view of a [`Task<Box<dyn Any + Send + Sync>>`] obtained via
390    /// [`Task::downcast`]. The inner task drives the actual work; the
391    /// downcast layer just unwraps the `Box<dyn Any + Send + Sync>` on poll.
392    Downcast {
393        inner: Box<Task<Box<dyn Any + Send + Sync>>>,
394        marker: PhantomData<fn() -> T>,
395    },
396
397    /// A task whose real handle is delivered later by another thread (see
398    /// [`Task::rendezvous`]). Once delivered, polling replaces this state
399    /// with the delivered task's state.
400    Rendezvous(RendezvousReceiver<T>),
401}
402
403/// State shared between the two halves of a [`Task::rendezvous`] pair.
404enum RendezvousState<T> {
405    /// No task delivered yet; holds the consumer's waker if it polled.
406    Pending(Option<Waker>),
407    /// The producer delivered before the consumer consumed the task.
408    Delivered(Task<T>),
409    /// The consumer was dropped before delivery; delivery cancels the task.
410    Cancelled,
411    /// The consumer was detached before delivery; delivery detaches the task.
412    Detached,
413    /// The consumer took the delivered task.
414    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            // The producer applies the detached disposition on delivery.
452            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            // `Taken`, `Detached`, and `Cancelled` record dispositions that
471            // this drop must not overwrite: the receiver is also dropped as a
472            // normal side effect of taking or detaching.
473            _ => {}
474        }
475    }
476}
477
478pub(crate) struct RendezvousSender<T> {
479    shared: Arc<parking_lot::Mutex<RendezvousState<T>>>,
480}
481
482impl<T> RendezvousSender<T> {
483    /// Hands the real task to the rendezvous, applying the consumer's
484    /// disposition if it was dropped or detached before delivery.
485    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    /// Creates a new task that will resolve with the value
517    pub fn ready(val: T) -> Self {
518        Task(TaskState::Ready(Some(val)))
519    }
520
521    /// Creates a task whose real handle arrives later through the returned
522    /// sender, typically from another thread. Until delivery the task is
523    /// pending; afterwards it behaves exactly like the delivered task.
524    /// Dropping or detaching the task before delivery is honored on delivery.
525    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    /// Creates a Task from an async_task::Task
536    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    /// Detaching a task runs it to completion in the background
550    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    /// Converts this task into a fallible task that returns `Option<T>`.
560    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    /// Reinterprets the boxed output as a concrete `T` via downcast on
575    /// completion. Used by [`LocalExecutor::spawn_dedicated`] and
576    /// [`BackgroundExecutor::spawn_dedicated`] to recover the user closure's
577    /// `Fut::Output` from the dyn-safe [`Scheduler::spawn_dedicated`].
578    ///
579    /// Panics on poll if the inner output is not in fact a `T` -- a logic
580    /// error in whatever produced the inner task, since the downcast type is
581    /// chosen by the caller of `downcast`.
582    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/// A task that returns `Option<T>` instead of panicking when cancelled.
604#[must_use]
605pub struct FallibleTask<T>(FallibleTaskState<T>);
606
607enum FallibleTaskState<T> {
608    /// A task that is ready to return a value
609    Ready(Option<T>),
610
611    /// A task that is currently running (wraps async_task::FallibleTask).
612    Spawned(async_task::FallibleTask<T, RunnableMeta>),
613
614    /// Mirror of [`TaskState::Downcast`] for fallible tasks.
615    Downcast {
616        inner: Box<FallibleTask<Box<dyn Any + Send + Sync>>>,
617        marker: PhantomData<fn() -> T>,
618    },
619
620    /// Mirror of [`TaskState::Rendezvous`] for fallible tasks.
621    Rendezvous(RendezvousReceiver<T>),
622}
623
624impl<T> FallibleTask<T> {
625    /// Creates a new fallible task that will resolve with the value.
626    pub fn ready(val: T) -> Self {
627        FallibleTask(FallibleTaskState::Ready(Some(val)))
628    }
629
630    /// Detaching a task runs it to completion in the background.
631    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/// Variant of `async_task::spawn_local` that includes the source location of the spawn in panics.
721#[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            // SAFETY: `inner` is wrapped in `ManuallyDrop`, so this is the only
759            // place it is dropped. The thread check above ensures local futures
760            // are dropped on the thread that created them.
761            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            // SAFETY: We don't move any fields out of `self`; this mutable
770            // reference is only used to check metadata and to project the pin to
771            // `inner` below.
772            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            // SAFETY: `inner` is structurally pinned by `Checked`; after
779            // `Checked` is pinned, `inner` is never moved. The thread check
780            // above ensures the local future is only polled by its spawning
781            // thread.
782            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    // SAFETY: `Checked` enforces the invariants required by `spawn_unchecked`:
796    // the non-`Send` future is only polled and dropped on the thread that
797    // spawned it.
798    unsafe { builder.spawn_unchecked(future, schedule) }
799}