workflow_core/task.rs
1//!
2//! [`task`](self) module provides helper functions for use with async closures that *operate uniformly*
3//! in native ([`tokio`](https://crates.io/crates/tokio)-backed) and WASM
4//! (`wasm-bindgen-futures`-backed) environments
5//! (i.e. a web browser).
6//!
7//! Following functions are are available:
8//! - [`spawn()`] - non-blocking spawn of the supplied async closure
9//! - [`sleep()`] - suspends the task for a given Duration
10//! - [`yield_now()`] - yields rust executor
11//! - [`yield_executor()`] - yields to top-level executor (browser async loop)
12//!
13//! <div class="example-wrap compile_fail"><pre class="compile_fail" style="white-space:normal;font:inherit;">
14//! Blocking spawn is not available as a part of this framework as WASM-browser environment can
15//! not block task execution due to a single-threaded async application environment.
16//! </pre></div>
17//!
18
19#[allow(unused_imports)]
20use cfg_if::cfg_if;
21use futures::Future;
22
23/// Runtime-agnostic cooperative yield, equivalent to the async-std /
24/// futures-lite `yield_now`: re-schedules the task to the back of the
25/// executor queue, giving other tasks room to progress. No runtime coupling.
26#[doc(hidden)]
27pub mod __yield {
28 use std::future::Future;
29 use std::pin::Pin;
30 use std::task::{Context, Poll};
31
32 pub async fn yield_now() {
33 struct YieldNow(bool);
34 impl Future for YieldNow {
35 type Output = ();
36 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
37 if self.0 {
38 Poll::Ready(())
39 } else {
40 self.0 = true;
41 cx.waker().wake_by_ref();
42 Poll::Pending
43 }
44 }
45 }
46 YieldNow(false).await
47 }
48}
49
50cfg_if! {
51 if #[cfg(not(any(target_arch = "wasm32", target_arch = "bpf")))] {
52
53 pub mod native {
54 //! native implementation
55 pub use super::*;
56
57 // yield_executor functionality is browser-specific
58 // hence we create a stub in a form of `yield_now()`
59 // for native platforms
60 pub use tokio::task::yield_now as yield_executor;
61 pub use tokio::task::yield_now;
62 pub use tokio::time::sleep;
63 pub use crate::native::interval::{interval,Interval};
64
65 /// Spawns a `Send` future onto the tokio runtime, detaching the
66 /// resulting task.
67 pub fn spawn<F, T>(future: F)
68 where
69 F: Future<Output = T> + Send + 'static,
70 T: Send + 'static,
71 {
72 tokio::task::spawn(future);
73 }
74
75 /// Not supported on native targets; provided for API parity with
76 /// the WASM implementation and never reached.
77 pub fn dispatch<F, T>(_future: F)
78 where
79 F: Future<Output = T> + 'static,
80 T: 'static,
81 {
82 unreachable!()
83 }
84
85 pub use workflow_core_macros::call_async_no_send;
86 }
87
88 pub use native::*;
89 }
90}
91
92pub mod wasm {
93 //! WASM implementation
94 pub use super::*;
95
96 /// Spawns a `Send` future onto the browser event loop on the `wasm32`
97 /// target; panics on non-WASM targets.
98 pub fn spawn<F, T>(_future: F)
99 where
100 F: Future<Output = T> + Send + 'static,
101 T: Send + 'static,
102 {
103 cfg_if::cfg_if! {
104 if #[cfg(target_arch = "wasm32")] {
105 // wasm32 spawn shim: spawn the future onto the browser event loop.
106 // (async-std's wasm `block_on` delegated to
107 // `wasm_bindgen_futures::spawn_local` internally; we call it directly.
108 // async-std's task-local wrapper is dropped - verified unused across
109 // the SDK and its consumers.)
110 wasm_bindgen_futures::spawn_local(async move {
111 let _ = _future.await;
112 });
113 } else {
114 panic!("workflow_core::task::wasm::spawn() is not allowed on non-wasm target");
115 }
116 }
117 }
118
119 /// Spawns a future onto the browser event loop without requiring it to be
120 /// `Send`. Like [`spawn`] but accepts non-`Send` futures on the `wasm32`
121 /// target; panics on non-WASM targets.
122 // `dispatch()` is similar to `spawn()` but does not
123 // impose `Send` requirement on the supplied future
124 // when building for the `wasm32` target.
125 pub fn dispatch<F, T>(_future: F)
126 where
127 F: Future<Output = T> + 'static,
128 T: 'static,
129 {
130 cfg_if::cfg_if! {
131 if #[cfg(target_arch = "wasm32")] {
132 // wasm32 spawn shim: spawn the future onto the browser event loop.
133 // (async-std's wasm `block_on` delegated to
134 // `wasm_bindgen_futures::spawn_local` internally; we call it directly.
135 // async-std's task-local wrapper is dropped - verified unused across
136 // the SDK and its consumers.)
137 wasm_bindgen_futures::spawn_local(async move {
138 let _ = _future.await;
139 });
140 } else {
141 panic!("workflow_core::task::wasm::spawn() is not allowed on non-wasm target");
142 }
143 }
144 }
145
146 cfg_if! {
147 if #[cfg(target_arch = "wasm32")] {
148 pub use crate::wasm::{
149 overrides::disable_persistent_timer_overrides,
150 interval::{interval,Interval},
151 yield_executor::{yield_executor,Yield},
152 sleep::{sleep,Sleep}
153 };
154 pub use crate::task::__yield::yield_now;
155 pub use workflow_core_macros::call_async_no_send;
156 } else {
157 pub use crate::native::{
158 overrides::disable_persistent_timer_overrides,
159 interval::{interval,Interval},
160 };
161 pub use tokio::time::sleep;
162 pub use tokio::task::yield_now;
163 pub use workflow_core_macros::call_async_no_send;
164 }
165 }
166}
167
168#[cfg(target_arch = "wasm32")]
169pub use wasm::*;