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},
13 thread::{self, ThreadId},
14 time::Duration,
15};
16
17pub type DedicatedFn = Box<
21 dyn FnOnce(LocalExecutor) -> Pin<Box<dyn Future<Output = Box<dyn Any + Send + Sync>> + 'static>>
22 + Send
23 + 'static,
24>;
25
26#[derive(Clone)]
31pub struct LocalExecutor {
32 session_id: SessionId,
33 scheduler: Arc<dyn Scheduler>,
34 dispatch: Arc<dyn Fn(Runnable<RunnableMeta>) + Send + Sync>,
38 not_send: PhantomData<Rc<()>>,
39}
40
41impl LocalExecutor {
42 pub fn new(
51 session_id: SessionId,
52 scheduler: Arc<dyn Scheduler>,
53 dispatch: impl Fn(Runnable<RunnableMeta>) + Send + Sync + 'static,
54 ) -> Self {
55 Self {
56 session_id,
57 scheduler,
58 dispatch: Arc::new(dispatch),
59 not_send: PhantomData,
60 }
61 }
62
63 pub fn session_id(&self) -> SessionId {
64 self.session_id
65 }
66
67 pub fn scheduler(&self) -> &Arc<dyn Scheduler> {
68 &self.scheduler
69 }
70
71 #[track_caller]
72 pub fn spawn<F>(&self, future: F) -> Task<F::Output>
73 where
74 F: Future + 'static,
75 F::Output: 'static,
76 {
77 let dispatch = self.dispatch.clone();
78 let location = Location::caller();
79 let (runnable, task) = spawn_local_with_source_location(
80 future,
81 move |runnable| dispatch(runnable),
82 RunnableMeta {
83 location,
84 spawned: crate::SpawnTime(Instant::now()),
85 },
86 );
87 runnable.schedule();
88 Task(TaskState::Spawned(task))
89 }
90
91 #[track_caller]
96 pub fn spawn_with_dispatch<F>(
97 &self,
98 future: F,
99 dispatch: impl Fn(Runnable<RunnableMeta>) + Send + Sync + 'static,
100 ) -> Task<F::Output>
101 where
102 F: Future + 'static,
103 F::Output: 'static,
104 {
105 let location = Location::caller();
106 let (runnable, task) = spawn_local_with_source_location(
107 future,
108 dispatch,
109 RunnableMeta {
110 location,
111 spawned: crate::SpawnTime(Instant::now()),
112 },
113 );
114 runnable.schedule();
115 Task(TaskState::Spawned(task))
116 }
117
118 pub fn block_on<Fut: Future>(&self, future: Fut) -> Fut::Output {
119 use std::cell::Cell;
120
121 let output = Cell::new(None);
122 let future = async {
123 output.set(Some(future.await));
124 };
125 let mut future = std::pin::pin!(future);
126
127 self.scheduler
128 .block(Some(self.session_id), future.as_mut(), None);
129
130 output.take().expect("block_on future did not complete")
131 }
132
133 pub fn block_with_timeout<Fut: Future>(
136 &self,
137 timeout: Duration,
138 future: Fut,
139 ) -> Result<Fut::Output, impl Future<Output = Fut::Output> + use<Fut>> {
140 use std::cell::Cell;
141
142 let output = Cell::new(None);
143 let mut future = Box::pin(future);
144
145 {
146 let future_ref = &mut future;
147 let wrapper = async {
148 output.set(Some(future_ref.await));
149 };
150 let mut wrapper = std::pin::pin!(wrapper);
151
152 self.scheduler
153 .block(Some(self.session_id), wrapper.as_mut(), Some(timeout));
154 }
155
156 match output.take() {
157 Some(value) => Ok(value),
158 None => Err(future),
159 }
160 }
161
162 #[track_caller]
163 pub fn timer(&self, duration: Duration) -> Timer {
164 self.scheduler.timer(duration)
165 }
166
167 pub fn now(&self) -> Instant {
168 self.scheduler.clock().now()
169 }
170
171 #[track_caller]
180 pub fn spawn_dedicated<F, Fut>(&self, f: F) -> Task<Fut::Output>
181 where
182 F: FnOnce(LocalExecutor) -> Fut + Send + 'static,
183 Fut: Future + 'static,
184 Fut::Output: Send + Sync + 'static,
185 {
186 self.scheduler
187 .clone()
188 .spawn_dedicated(box_dedicated(f))
189 .downcast::<Fut::Output>()
190 }
191}
192
193fn box_dedicated<F, Fut>(f: F) -> DedicatedFn
198where
199 F: FnOnce(LocalExecutor) -> Fut + Send + 'static,
200 Fut: Future + 'static,
201 Fut::Output: Send + Sync + 'static,
202{
203 Box::new(move |executor| {
204 Box::pin(async move { Box::new(f(executor).await) as Box<dyn Any + Send + Sync> })
205 })
206}
207
208#[derive(Clone)]
209pub struct BackgroundExecutor {
210 scheduler: Arc<dyn Scheduler>,
211}
212
213impl BackgroundExecutor {
214 pub fn new(scheduler: Arc<dyn Scheduler>) -> Self {
215 Self { scheduler }
216 }
217
218 #[track_caller]
219 pub fn spawn<F>(&self, future: F) -> Task<F::Output>
220 where
221 F: Future + Send + 'static,
222 F::Output: Send + 'static,
223 {
224 self.spawn_with_priority(Priority::default(), future)
225 }
226
227 #[track_caller]
228 pub fn spawn_with_priority<F>(&self, priority: Priority, future: F) -> Task<F::Output>
229 where
230 F: Future + Send + 'static,
231 F::Output: Send + 'static,
232 {
233 let scheduler = Arc::downgrade(&self.scheduler);
234 let location = Location::caller();
235 let (runnable, task) = async_task::Builder::new()
236 .metadata(RunnableMeta {
237 location,
238 spawned: crate::SpawnTime(Instant::now()),
239 })
240 .spawn(
241 move |_| future,
242 move |runnable| {
243 if let Some(scheduler) = scheduler.upgrade() {
244 scheduler.schedule_background_with_priority(runnable, priority);
245 }
246 },
247 );
248 runnable.schedule();
249 Task(TaskState::Spawned(task))
250 }
251
252 #[track_caller]
254 pub fn spawn_realtime<F>(&self, future: F) -> Task<F::Output>
255 where
256 F: Future + Send + 'static,
257 F::Output: Send + 'static,
258 {
259 let location = Location::caller();
260 let (tx, rx) = flume::bounded::<async_task::Runnable<RunnableMeta>>(1);
261
262 self.scheduler.spawn_realtime(Box::new(move || {
263 while let Ok(runnable) = rx.recv() {
264 runnable.run();
265 }
266 }));
267
268 let (runnable, task) = async_task::Builder::new()
269 .metadata(RunnableMeta {
270 location,
271 spawned: crate::SpawnTime(Instant::now()),
272 })
273 .spawn(
274 move |_| future,
275 move |runnable| {
276 let _ = tx.send(runnable);
277 },
278 );
279 runnable.schedule();
280 Task(TaskState::Spawned(task))
281 }
282
283 #[track_caller]
284 pub fn timer(&self, duration: Duration) -> Timer {
285 self.scheduler.timer(duration)
286 }
287
288 pub fn now(&self) -> Instant {
289 self.scheduler.clock().now()
290 }
291
292 pub fn scheduler(&self) -> &Arc<dyn Scheduler> {
293 &self.scheduler
294 }
295
296 #[track_caller]
305 pub fn spawn_dedicated<F, Fut>(&self, f: F) -> Task<Fut::Output>
306 where
307 F: FnOnce(LocalExecutor) -> Fut + Send + 'static,
308 Fut: Future + 'static,
309 Fut::Output: Send + Sync + 'static,
310 {
311 self.scheduler
312 .clone()
313 .spawn_dedicated(box_dedicated(f))
314 .downcast::<Fut::Output>()
315 }
316}
317
318#[must_use]
325pub struct Task<T>(TaskState<T>);
326
327enum TaskState<T> {
328 Ready(Option<T>),
330
331 Spawned(async_task::Task<T, RunnableMeta>),
333
334 Downcast {
338 inner: Box<Task<Box<dyn Any + Send + Sync>>>,
339 marker: PhantomData<fn() -> T>,
340 },
341}
342
343impl<T> Task<T> {
344 pub fn ready(val: T) -> Self {
346 Task(TaskState::Ready(Some(val)))
347 }
348
349 pub fn from_async_task(task: async_task::Task<T, RunnableMeta>) -> Self {
351 Task(TaskState::Spawned(task))
352 }
353
354 pub fn is_ready(&self) -> bool {
355 match &self.0 {
356 TaskState::Ready(_) => true,
357 TaskState::Spawned(task) => task.is_finished(),
358 TaskState::Downcast { inner, .. } => inner.is_ready(),
359 }
360 }
361
362 pub fn detach(self) {
364 match self {
365 Task(TaskState::Ready(_)) => {}
366 Task(TaskState::Spawned(task)) => task.detach(),
367 Task(TaskState::Downcast { inner, .. }) => inner.detach(),
368 }
369 }
370
371 pub fn fallible(self) -> FallibleTask<T> {
373 FallibleTask(match self.0 {
374 TaskState::Ready(val) => FallibleTaskState::Ready(val),
375 TaskState::Spawned(task) => FallibleTaskState::Spawned(task.fallible()),
376 TaskState::Downcast { inner, .. } => FallibleTaskState::Downcast {
377 inner: Box::new(inner.fallible()),
378 marker: PhantomData,
379 },
380 })
381 }
382}
383
384impl Task<Box<dyn Any + Send + Sync>> {
385 pub fn downcast<T: Send + Sync + 'static>(self) -> Task<T> {
394 Task(TaskState::Downcast {
395 inner: Box::new(self),
396 marker: PhantomData,
397 })
398 }
399}
400
401impl<T> std::fmt::Debug for Task<T> {
402 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
403 match &self.0 {
404 TaskState::Ready(_) => f.debug_tuple("Task::Ready").finish(),
405 TaskState::Spawned(task) => f.debug_tuple("Task::Spawned").field(task).finish(),
406 TaskState::Downcast { inner, .. } => {
407 f.debug_tuple("Task::Downcast").field(inner).finish()
408 }
409 }
410 }
411}
412
413#[must_use]
415pub struct FallibleTask<T>(FallibleTaskState<T>);
416
417enum FallibleTaskState<T> {
418 Ready(Option<T>),
420
421 Spawned(async_task::FallibleTask<T, RunnableMeta>),
423
424 Downcast {
426 inner: Box<FallibleTask<Box<dyn Any + Send + Sync>>>,
427 marker: PhantomData<fn() -> T>,
428 },
429}
430
431impl<T> FallibleTask<T> {
432 pub fn ready(val: T) -> Self {
434 FallibleTask(FallibleTaskState::Ready(Some(val)))
435 }
436
437 pub fn detach(self) {
439 match self.0 {
440 FallibleTaskState::Ready(_) => {}
441 FallibleTaskState::Spawned(task) => task.detach(),
442 FallibleTaskState::Downcast { inner, .. } => inner.detach(),
443 }
444 }
445}
446
447impl<T: 'static> Future for FallibleTask<T> {
448 type Output = Option<T>;
449
450 fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
451 match unsafe { self.get_unchecked_mut() } {
452 FallibleTask(FallibleTaskState::Ready(val)) => Poll::Ready(val.take()),
453 FallibleTask(FallibleTaskState::Spawned(task)) => Pin::new(task).poll(cx),
454 FallibleTask(FallibleTaskState::Downcast { inner, .. }) => {
455 match Pin::new(inner.as_mut()).poll(cx) {
456 Poll::Ready(Some(boxed_any)) => Poll::Ready(Some(
457 *boxed_any
458 .downcast::<T>()
459 .expect("FallibleTask::poll: downcast type mismatch"),
460 )),
461 Poll::Ready(None) => Poll::Ready(None),
462 Poll::Pending => Poll::Pending,
463 }
464 }
465 }
466 }
467}
468
469impl<T> std::fmt::Debug for FallibleTask<T> {
470 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
471 match &self.0 {
472 FallibleTaskState::Ready(_) => f.debug_tuple("FallibleTask::Ready").finish(),
473 FallibleTaskState::Spawned(task) => {
474 f.debug_tuple("FallibleTask::Spawned").field(task).finish()
475 }
476 FallibleTaskState::Downcast { inner, .. } => f
477 .debug_tuple("FallibleTask::Downcast")
478 .field(inner)
479 .finish(),
480 }
481 }
482}
483
484impl<T: 'static> Future for Task<T> {
485 type Output = T;
486
487 fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
488 match unsafe { self.get_unchecked_mut() } {
489 Task(TaskState::Ready(val)) => Poll::Ready(val.take().unwrap()),
490 Task(TaskState::Spawned(task)) => Pin::new(task).poll(cx),
491 Task(TaskState::Downcast { inner, .. }) => match Pin::new(inner.as_mut()).poll(cx) {
492 Poll::Ready(boxed_any) => Poll::Ready(
493 *boxed_any
494 .downcast::<T>()
495 .expect("Task::poll: downcast type mismatch"),
496 ),
497 Poll::Pending => Poll::Pending,
498 },
499 }
500 }
501}
502
503#[track_caller]
505fn spawn_local_with_source_location<Fut, S>(
506 future: Fut,
507 schedule: S,
508 metadata: RunnableMeta,
509) -> (
510 async_task::Runnable<RunnableMeta>,
511 async_task::Task<Fut::Output, RunnableMeta>,
512)
513where
514 Fut: Future + 'static,
515 Fut::Output: 'static,
516 S: async_task::Schedule<RunnableMeta> + Send + Sync + 'static,
517{
518 #[inline]
519 fn thread_id() -> ThreadId {
520 std::thread_local! {
521 static ID: ThreadId = thread::current().id();
522 }
523 ID.try_with(|id| *id)
524 .unwrap_or_else(|_| thread::current().id())
525 }
526
527 struct Checked<F> {
528 id: ThreadId,
529 inner: ManuallyDrop<F>,
530 location: &'static Location<'static>,
531 }
532
533 impl<F> Drop for Checked<F> {
534 fn drop(&mut self) {
535 assert_eq!(
536 self.id,
537 thread_id(),
538 "local task dropped by a thread that didn't spawn it. Task spawned at {}",
539 self.location
540 );
541 unsafe { ManuallyDrop::drop(&mut self.inner) };
545 }
546 }
547
548 impl<F: Future> Future for Checked<F> {
549 type Output = F::Output;
550
551 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
552 let this = unsafe { self.get_unchecked_mut() };
556 assert!(
557 this.id == thread_id(),
558 "local task polled by a thread that didn't spawn it. Task spawned at {}",
559 this.location
560 );
561 unsafe { Pin::new_unchecked(&mut *this.inner).poll(cx) }
566 }
567 }
568
569 let location = metadata.location;
570
571 let future = move |_| Checked {
572 id: thread_id(),
573 inner: ManuallyDrop::new(future),
574 location,
575 };
576
577 let builder = async_task::Builder::new().metadata(metadata);
578 unsafe { builder.spawn_unchecked(future, schedule) }
582}