Skip to main content

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::*;