1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
/// On WASM, [JsValue][wasm_bindgen::JsValue]s cannot be shared between scopes but instead can be
/// ["transferred"]. Rust however is not aware of transferables and therefore cannot
/// capture these values. This macro wraps a closure and returns a [TransferClosure][crate::ffi::TransferClosure] on WASM platforms
/// which will capture these special values, or a normal [FnOnce] on other platforms.
/// Note that the parameter names must match available variables/bindings from the outer scope.
///
/// ["transferred"]: https://developer.mozilla.org/en-US/docs/Glossary/Transferable_objects
#[macro_export]
macro_rules! transfer {
(|| $block:block) => {{
#[cfg(not(target_family = "wasm"))]
{ move || $block }
#[cfg(target_family = "wasm")]
{
$crate::ffi::TransferClosure::new(vec![], vec![], move |_: &[JsValue]| $block)
}
}};
(|$($param:ident: $ty:ty),*| $block:block) => {{
#[cfg(not(target_family = "wasm"))]
{
move || $block
}
#[cfg(target_family = "wasm")]
{
use wasm_bindgen::JsValue;
use $crate::ffi::Transfer;
#[allow(unused_variables)]
let worker = move |transfer: &[JsValue]| {
let idx = 0;
$(
let $param = <$ty>::deserialize(&transfer[idx]);
let idx = idx + 1;
)*
$block
};
let transferables = [$($param.transferables()),*].concat();
$crate::ffi::TransferClosure::new(vec![$($param.serialize()),*], transferables, worker)
}
}};
}
/// Spawn a task using the internal thread pool.
/// Interprets the parameters as a list of captured transferables to
/// send to this thread.
///
/// Also see [`transfer`].
///
/// Example:
/// ```
/// let (tx, rx) = std::sync::mpsc::channel();
/// flutter_rust_bridge::spawn!(|| {
/// tx.send(true).unwrap();
/// });
/// assert_eq!(rx.recv(), Ok(true));
/// ```
///
/// Sending a JS transferable:
///
/// ```
/// # #[cfg(target_family = "wasm")] fn main() {
/// use web_sys::{MessagePort, MessageEvent};
/// use wasm_bindgen::prelude::*;
///
/// let channel = web_sys::MessageChannel::new().unwrap();
///
/// let onmessage = Closure::new(move |event: MessageEvent| {
/// assert!(event.data() == true);
/// });
/// channel.port1().set_onmessage(Some(onmessage.as_ref().unchecked_ref()));
/// onmessage.forget();
/// let port2 = channel.port2();
/// // Declare the transferable with the same name and type
/// flutter_rust_bridge::spawn!(|port2: MessagePort| {
/// port2.post_message(&JsValue::from(true)).unwrap();
/// });
/// # } #[cfg(not(target_family = "wasm"))] fn main() {}
/// ```
#[macro_export]
macro_rules! spawn {
($($tt:tt)*) => {{
let worker = $crate::transfer!($($tt)*);
#[cfg(not(target_family = "wasm"))]
{
$crate::thread::THREAD_POOL.lock().execute(worker)
}
#[cfg(target_family = "wasm")]
{
use anyhow::anyhow;
let res = $crate::thread::WORKER_POOL.with(|pool| {
if let Some(pool) = pool.as_ref() {
pool.run(worker).map_err(|err| anyhow!("worker error: {:?}", err))
} else {
Err(anyhow!("Worker was not initialized."))
}
});
if let Err(err) = res {
$crate::console_error!("worker error: {:?}", err);
}
}
}};
}
#[macro_export]
macro_rules! console_error {
($lit:literal) => {
$crate::error($lit)
};
($($tt:tt)*) => {
$crate::error(&format!($($tt)*))
};
}