1use core::{
2 future::Future,
3 pin::Pin,
4 task::{Context, Poll},
5};
6
7use embassy_executor::{SendSpawner, Spawner};
8use embassy_sync::once_lock::OnceLock;
9use futures_channel::oneshot;
10use futures_util::future::{Either, select};
11
12use crate::{
13 AsyncBlockingSpawner, AsyncLocalSpawner, AsyncSpawner, Yielder, spawner::handle::JoinError,
14};
15
16mod after;
17mod delay;
18mod interval;
19mod sleep;
20mod timeout;
21
22pub use after::*;
23pub use delay::*;
24pub use interval::*;
25pub use sleep::*;
26pub use timeout::*;
27
28pub const TASK_POOL_SIZE: usize = 64;
36
37type DynFuture = Pin<std::boxed::Box<dyn Future<Output = ()> + Send + 'static>>;
38
39#[embassy_executor::task(pool_size = TASK_POOL_SIZE)]
40async fn task_runner(fut: DynFuture) {
41 fut.await
42}
43
44static SPAWNER: OnceLock<SendSpawner> = OnceLock::new();
45
46pub fn init(spawner: Spawner) {
63 let _ = SPAWNER.init(spawner.make_send());
64}
65
66#[inline]
67fn global_spawner() -> SendSpawner {
68 *SPAWNER.try_get().expect(
69 "embassy runtime is not initialized; call `agnostic_lite::embassy::init(spawner)` from within \
70 a running embassy executor before spawning",
71 )
72}
73
74fn spawn_send<F>(future: F) -> JoinHandle<F::Output>
75where
76 F: Future + Send + 'static,
77 F::Output: Send + 'static,
78{
79 let (tx, rx) = oneshot::channel::<F::Output>();
80 let (stop_tx, stop_rx) = oneshot::channel::<bool>();
81
82 let wrapped: DynFuture = std::boxed::Box::pin(async move {
83 let future = core::pin::pin!(future);
84 match select(future, stop_rx).await {
85 Either::Left((output, _)) => {
86 let _ = tx.send(output);
87 }
88 Either::Right((signal, future)) => match signal {
89 Ok(true) => {}
91 Ok(false) | Err(_) => {
93 let _ = future.await;
94 }
95 },
96 }
97 });
98
99 if let Ok(token) = task_runner(wrapped) {
102 global_spawner().spawn(token);
103 }
104
105 JoinHandle { stop_tx, rx }
106}
107
108pub struct JoinHandle<T> {
112 stop_tx: oneshot::Sender<bool>,
113 rx: oneshot::Receiver<T>,
114}
115
116impl<T> JoinHandle<T> {
117 #[inline]
119 pub fn detach(self) {
120 let _ = self.stop_tx.send(false);
121 }
122
123 #[inline]
125 pub fn abort(self) {
126 let _ = self.stop_tx.send(true);
127 }
128}
129
130impl<T> Future for JoinHandle<T> {
131 type Output = Result<T, JoinError>;
132
133 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
134 Pin::new(&mut self.rx)
135 .poll(cx)
136 .map(|res| res.map_err(|_| JoinError::new()))
137 }
138}
139
140impl<O> crate::JoinHandle<O> for JoinHandle<O> {
141 type JoinError = JoinError;
142
143 fn abort(self) {
144 Self::abort(self)
145 }
146
147 fn detach(self) {
148 Self::detach(self)
149 }
150}
151
152impl<O> crate::LocalJoinHandle<O> for JoinHandle<O> {
153 type JoinError = JoinError;
154
155 fn detach(self) {
156 Self::detach(self)
157 }
158}
159
160#[derive(Debug)]
164#[must_use = "futures do nothing unless you `.await` or poll them"]
165struct YieldNow(bool);
166
167impl Future for YieldNow {
168 type Output = ();
169
170 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
171 if !self.0 {
172 self.0 = true;
173 cx.waker().wake_by_ref();
174 Poll::Pending
175 } else {
176 Poll::Ready(())
177 }
178 }
179}
180
181#[derive(Debug, Clone, Copy)]
183pub struct EmbassySpawner;
184
185impl Yielder for EmbassySpawner {
186 async fn yield_now() {
187 YieldNow(false).await
188 }
189
190 async fn yield_now_local() {
191 YieldNow(false).await
192 }
193}
194
195impl AsyncSpawner for EmbassySpawner {
196 type JoinHandle<F>
197 = JoinHandle<F>
198 where
199 F: Send + 'static;
200
201 fn spawn<F>(future: F) -> Self::JoinHandle<F::Output>
202 where
203 F::Output: Send + 'static,
204 F: Future + Send + 'static,
205 {
206 spawn_send(future)
207 }
208}
209
210impl AsyncLocalSpawner for EmbassySpawner {
211 type JoinHandle<F>
212 = JoinHandle<F>
213 where
214 F: 'static;
215
216 fn spawn_local<F>(_future: F) -> Self::JoinHandle<F::Output>
217 where
218 F::Output: 'static,
219 F: Future + 'static,
220 {
221 panic!(
222 "the embassy backend cannot spawn `!Send` local tasks: its spawner is stored in a global \
223 `static`, which can only hold the `Send` spawner. Use `spawn` with a `Send` future instead."
224 )
225 }
226}
227
228impl AsyncBlockingSpawner for EmbassySpawner {
229 type JoinHandle<R>
230 = JoinHandle<R>
231 where
232 R: Send + 'static;
233
234 fn spawn_blocking<F, R>(_f: F) -> Self::JoinHandle<R>
235 where
236 F: FnOnce() -> R + Send + 'static,
237 R: Send + 'static,
238 {
239 panic!("the embassy backend does not support blocking tasks")
240 }
241}
242
243pub fn block_on<F: Future>(future: F) -> F::Output {
249 embassy_futures::block_on(future)
250}
251
252#[inline]
253pub(crate) fn to_embassy_duration(d: core::time::Duration) -> embassy_time::Duration {
254 embassy_time::Duration::from_micros(d.as_micros().min(u64::MAX as u128) as u64)
255}
256
257#[inline]
258pub(crate) fn from_embassy_duration(d: embassy_time::Duration) -> core::time::Duration {
259 core::time::Duration::from_micros(d.as_micros())
260}
261
262#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
266pub struct Instant(embassy_time::Instant);
267
268impl core::hash::Hash for Instant {
269 #[inline]
270 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
271 self.0.as_ticks().hash(state);
272 }
273}
274
275impl Instant {
276 #[inline]
278 #[must_use]
279 pub fn now() -> Self {
280 Self(embassy_time::Instant::now())
281 }
282
283 #[inline]
285 pub const fn into_embassy(self) -> embassy_time::Instant {
286 self.0
287 }
288}
289
290impl From<embassy_time::Instant> for Instant {
291 #[inline]
292 fn from(instant: embassy_time::Instant) -> Self {
293 Self(instant)
294 }
295}
296
297impl From<Instant> for embassy_time::Instant {
298 #[inline]
299 fn from(instant: Instant) -> Self {
300 instant.0
301 }
302}
303
304impl core::ops::Add<core::time::Duration> for Instant {
305 type Output = Self;
306
307 #[inline]
308 fn add(self, rhs: core::time::Duration) -> Self {
309 Self(self.0 + to_embassy_duration(rhs))
310 }
311}
312
313impl core::ops::AddAssign<core::time::Duration> for Instant {
314 #[inline]
315 fn add_assign(&mut self, rhs: core::time::Duration) {
316 self.0 = self.0 + to_embassy_duration(rhs);
317 }
318}
319
320impl core::ops::Sub<core::time::Duration> for Instant {
321 type Output = Self;
322
323 #[inline]
324 fn sub(self, rhs: core::time::Duration) -> Self {
325 Self(self.0 - to_embassy_duration(rhs))
326 }
327}
328
329impl core::ops::SubAssign<core::time::Duration> for Instant {
330 #[inline]
331 fn sub_assign(&mut self, rhs: core::time::Duration) {
332 self.0 = self.0 - to_embassy_duration(rhs);
333 }
334}
335
336impl core::ops::Sub<Instant> for Instant {
337 type Output = core::time::Duration;
338
339 #[inline]
340 fn sub(self, rhs: Instant) -> core::time::Duration {
341 from_embassy_duration(self.0 - rhs.0)
342 }
343}
344
345impl crate::time::Instant for Instant {
346 #[inline]
347 fn now() -> Self {
348 Self(embassy_time::Instant::now())
349 }
350
351 #[inline]
352 fn elapsed(&self) -> core::time::Duration {
353 from_embassy_duration(self.0.elapsed())
354 }
355
356 #[inline]
357 fn checked_add(&self, duration: core::time::Duration) -> Option<Self> {
358 self.0.checked_add(to_embassy_duration(duration)).map(Self)
359 }
360
361 #[inline]
362 fn checked_sub(&self, duration: core::time::Duration) -> Option<Self> {
363 self.0.checked_sub(to_embassy_duration(duration)).map(Self)
364 }
365
366 #[inline]
367 fn checked_duration_since(&self, earlier: Self) -> Option<core::time::Duration> {
368 self
369 .0
370 .checked_duration_since(earlier.0)
371 .map(from_embassy_duration)
372 }
373}
374
375#[cfg(feature = "std")]
380const _: () = {
381 use std::time::Instant as StdInstant;
382
383 impl From<StdInstant> for Instant {
384 fn from(value: StdInstant) -> Self {
385 let now_std = StdInstant::now();
386 let now = embassy_time::Instant::now();
387 Self(if value >= now_std {
388 now + to_embassy_duration(value - now_std)
389 } else {
390 now
391 .checked_sub(to_embassy_duration(now_std - value))
392 .unwrap_or(now)
393 })
394 }
395 }
396
397 impl From<Instant> for StdInstant {
398 fn from(value: Instant) -> Self {
399 let now = embassy_time::Instant::now();
400 let now_std = StdInstant::now();
401 if value.0 >= now {
402 now_std + from_embassy_duration(value.0 - now)
403 } else {
404 now_std
405 .checked_sub(from_embassy_duration(now - value.0))
406 .unwrap_or(now_std)
407 }
408 }
409 }
410};
411
412#[derive(Debug, Clone, Copy)]
422pub struct EmbassyRuntime;
423
424impl core::fmt::Display for EmbassyRuntime {
425 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
426 write!(f, "embassy")
427 }
428}
429
430impl crate::LocalRuntimeLite for EmbassyRuntime {
431 type LocalSpawner = EmbassySpawner;
432 type BlockingSpawner = EmbassySpawner;
433
434 type Instant = Instant;
435
436 type LocalInterval = EmbassyInterval;
437
438 type LocalSleep = EmbassySleep;
439
440 type LocalDelay<F>
441 = EmbassyDelay<F>
442 where
443 F: Future;
444
445 type LocalTimeout<F>
446 = EmbassyTimeout<F>
447 where
448 F: Future;
449
450 fn new() -> Self {
451 Self
452 }
453
454 fn name() -> &'static str {
455 "embassy"
456 }
457
458 fn fqname() -> &'static str {
459 "embassy-executor"
460 }
461
462 fn block_on<F: Future>(f: F) -> F::Output {
463 block_on(f)
464 }
465
466 fn interval_local(interval: core::time::Duration) -> Self::LocalInterval {
467 use crate::time::AsyncIntervalExt;
468
469 EmbassyInterval::interval(interval)
470 }
471
472 fn interval_local_at(start: Instant, period: core::time::Duration) -> Self::LocalInterval {
473 use crate::time::AsyncIntervalExt;
474
475 EmbassyInterval::interval_at(start, period)
476 }
477
478 fn sleep_local(duration: core::time::Duration) -> Self::LocalSleep {
479 use crate::time::AsyncSleepExt;
480
481 EmbassySleep::sleep(duration)
482 }
483
484 fn sleep_local_until(instant: Instant) -> Self::LocalSleep {
485 use crate::time::AsyncSleepExt;
486
487 EmbassySleep::sleep_until(instant)
488 }
489
490 fn delay_local<F>(duration: core::time::Duration, fut: F) -> Self::LocalDelay<F>
491 where
492 F: Future,
493 {
494 use crate::time::AsyncLocalDelayExt;
495
496 <EmbassyDelay<F> as AsyncLocalDelayExt<F>>::delay(duration, fut)
497 }
498
499 fn delay_local_at<F>(deadline: Instant, fut: F) -> Self::LocalDelay<F>
500 where
501 F: Future,
502 {
503 use crate::time::AsyncLocalDelayExt;
504
505 <EmbassyDelay<F> as AsyncLocalDelayExt<F>>::delay_at(deadline, fut)
506 }
507
508 fn timeout_local<F>(duration: core::time::Duration, future: F) -> Self::LocalTimeout<F>
509 where
510 F: Future,
511 {
512 use crate::time::AsyncLocalTimeout;
513
514 <EmbassyTimeout<F> as AsyncLocalTimeout<F>>::timeout_local(duration, future)
515 }
516
517 fn timeout_local_at<F>(deadline: Instant, future: F) -> Self::LocalTimeout<F>
518 where
519 F: Future,
520 {
521 use crate::time::AsyncLocalTimeout;
522
523 <EmbassyTimeout<F> as AsyncLocalTimeout<F>>::timeout_local_at(deadline, future)
524 }
525}
526
527impl crate::RuntimeLite for EmbassyRuntime {
528 type Spawner = EmbassySpawner;
529
530 type AfterSpawner = EmbassySpawner;
531
532 type Interval = EmbassyInterval;
533
534 type Sleep = EmbassySleep;
535
536 type Delay<F>
537 = EmbassyDelay<F>
538 where
539 F: Future + Send;
540
541 type Timeout<F>
542 = EmbassyTimeout<F>
543 where
544 F: Future + Send;
545
546 async fn yield_now() {
547 YieldNow(false).await
548 }
549
550 fn interval(interval: core::time::Duration) -> Self::Interval {
551 use crate::time::AsyncIntervalExt;
552
553 EmbassyInterval::interval(interval)
554 }
555
556 fn interval_at(start: Instant, period: core::time::Duration) -> Self::Interval {
557 use crate::time::AsyncIntervalExt;
558
559 EmbassyInterval::interval_at(start, period)
560 }
561
562 fn sleep(duration: core::time::Duration) -> Self::Sleep {
563 use crate::time::AsyncSleepExt;
564
565 EmbassySleep::sleep(duration)
566 }
567
568 fn sleep_until(instant: Instant) -> Self::Sleep {
569 use crate::time::AsyncSleepExt;
570
571 EmbassySleep::sleep_until(instant)
572 }
573
574 fn delay<F>(duration: core::time::Duration, fut: F) -> Self::Delay<F>
575 where
576 F: Future + Send,
577 {
578 use crate::time::AsyncDelayExt;
579
580 <EmbassyDelay<F> as AsyncDelayExt<F>>::delay(duration, fut)
581 }
582
583 fn delay_at<F>(deadline: Instant, fut: F) -> Self::Delay<F>
584 where
585 F: Future + Send,
586 {
587 use crate::time::AsyncDelayExt;
588
589 <EmbassyDelay<F> as AsyncDelayExt<F>>::delay_at(deadline, fut)
590 }
591
592 fn timeout<F>(duration: core::time::Duration, future: F) -> Self::Timeout<F>
593 where
594 F: Future + Send,
595 {
596 use crate::time::AsyncTimeout;
597
598 <EmbassyTimeout<F> as AsyncTimeout<F>>::timeout(duration, future)
599 }
600
601 fn timeout_at<F>(deadline: Instant, future: F) -> Self::Timeout<F>
602 where
603 F: Future + Send,
604 {
605 use crate::time::AsyncTimeout;
606
607 <EmbassyTimeout<F> as AsyncTimeout<F>>::timeout_at(deadline, future)
608 }
609}
610
611#[cfg(test)]
612mod tests {
613 use super::*;
614 use crate::{LocalRuntimeLite, RuntimeLite, time::Instant as _};
615 use core::{
616 sync::atomic::{AtomicBool, Ordering},
617 time::Duration,
618 };
619 use futures::executor::block_on;
620 use futures_util::StreamExt;
621
622 fn ensure_runtime() {
628 static START: std::sync::Once = std::sync::Once::new();
629 START.call_once(|| {
630 let (tx, rx) = std::sync::mpsc::channel();
631 std::thread::Builder::new()
632 .name("embassy-test-executor".into())
633 .spawn(move || {
634 let executor: &'static mut embassy_executor::Executor =
635 std::boxed::Box::leak(std::boxed::Box::new(embassy_executor::Executor::new()));
636 executor.run(|spawner| {
637 init(spawner);
638 let _ = tx.send(());
639 });
640 })
641 .unwrap();
642 rx.recv().unwrap();
643 });
644 }
645
646 #[test]
647 fn spawn_and_join() {
648 ensure_runtime();
649 assert_eq!(
650 block_on(async { EmbassyRuntime::spawn(async { 21 * 2 }).await.unwrap() }),
651 42
652 );
653 }
654
655 #[test]
656 fn spawn_detach_runs() {
657 ensure_runtime();
658 let flag = std::sync::Arc::new(AtomicBool::new(false));
659 let f = flag.clone();
660 EmbassyRuntime::spawn_detach(async move { f.store(true, Ordering::SeqCst) });
661 block_on(EmbassyRuntime::sleep(Duration::from_millis(100)));
662 assert!(flag.load(Ordering::SeqCst));
663 }
664
665 #[test]
666 fn block_on_drives_to_completion() {
667 let out = EmbassyRuntime::block_on(async {
668 EmbassyRuntime::yield_now().await;
669 42
670 });
671 assert_eq!(out, 42);
672 }
673
674 #[test]
675 fn sleep_waits() {
676 block_on(async {
677 let start = EmbassyRuntime::now();
678 EmbassyRuntime::sleep(Duration::from_millis(50)).await;
679 assert!(start.elapsed() >= Duration::from_millis(40));
680 });
681 }
682
683 #[test]
684 fn timeout_expires_and_completes() {
685 block_on(async {
686 let expired = EmbassyRuntime::timeout(Duration::from_millis(20), async {
687 EmbassyRuntime::sleep(Duration::from_millis(500)).await;
688 })
689 .await;
690 assert!(expired.is_err());
691
692 let done = EmbassyRuntime::timeout(Duration::from_millis(200), async { 7 }).await;
693 assert_eq!(done.unwrap(), 7);
694 });
695 }
696
697 #[test]
698 fn interval_first_tick_is_immediate() {
699 block_on(async {
700 let start = EmbassyRuntime::now();
701 let mut interval = EmbassyRuntime::interval(Duration::from_millis(50));
702 interval.next().await;
703 assert!(start.elapsed() < Duration::from_millis(40));
704 interval.next().await;
705 assert!(start.elapsed() >= Duration::from_millis(40));
706 });
707 }
708
709 #[test]
710 fn delay_runs_after_duration() {
711 block_on(async {
712 let start = EmbassyRuntime::now();
713 let out = EmbassyRuntime::delay(Duration::from_millis(50), async { 9 })
714 .await
715 .unwrap();
716 assert_eq!(out, 9);
717 assert!(start.elapsed() >= Duration::from_millis(40));
718 });
719 }
720
721 #[test]
722 fn spawn_after() {
723 ensure_runtime();
724 block_on(crate::tests::spawn_after_unittest::<EmbassyRuntime>());
725 }
726
727 #[test]
728 fn spawn_after_cancel() {
729 ensure_runtime();
730 block_on(crate::tests::spawn_after_cancel_unittest::<EmbassyRuntime>());
731 }
732
733 #[test]
734 fn spawn_after_drop() {
735 ensure_runtime();
736 block_on(crate::tests::spawn_after_drop_unittest::<EmbassyRuntime>());
737 }
738
739 #[test]
740 fn spawn_after_abort() {
741 ensure_runtime();
742 block_on(crate::tests::spawn_after_abort_unittest::<EmbassyRuntime>());
743 }
744
745 #[test]
746 fn spawn_after_reset_to_pass() {
747 ensure_runtime();
748 block_on(crate::tests::spawn_after_reset_to_pass_unittest::<
749 EmbassyRuntime,
750 >());
751 }
752
753 #[test]
754 fn spawn_after_reset_to_future() {
755 ensure_runtime();
756 block_on(crate::tests::spawn_after_reset_to_future_unittest::<
757 EmbassyRuntime,
758 >());
759 }
760}