use std::{sync::Arc, thread, time::Duration}; use napi::{ bindgen_prelude::*, threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode, UnknownReturnValue}, }; #[napi] pub fn call_threadsafe_function( tsfn: Arc>, ) -> Result<()> { for n in 0..100 { let tsfn = tsfn.clone(); thread::spawn(move || { tsfn.call(Ok(n), ThreadsafeFunctionCallMode::NonBlocking); }); } Ok(()) } #[napi] pub fn call_long_threadsafe_function( tsfn: ThreadsafeFunction, ) -> Result<()> { thread::spawn(move || { for n in 0..10 { thread::sleep(Duration::from_millis(100)); tsfn.call(Ok(n), ThreadsafeFunctionCallMode::NonBlocking); } }); Ok(()) } #[napi] pub fn threadsafe_function_throw_error( cb: ThreadsafeFunction, ) -> Result<()> { thread::spawn(move || { cb.call( Err(Error::new( Status::GenericFailure, "ThrowFromNative".to_owned(), )), ThreadsafeFunctionCallMode::Blocking, ); }); Ok(()) } #[napi] pub fn threadsafe_function_fatal_mode( cb: ThreadsafeFunction, ) -> Result<()> { thread::spawn(move || { cb.call(true, ThreadsafeFunctionCallMode::Blocking); }); Ok(()) } #[napi] pub fn threadsafe_function_fatal_mode_error( cb: ThreadsafeFunction, ) -> Result<()> { thread::spawn(move || { cb.call_with_return_value(true, ThreadsafeFunctionCallMode::Blocking, |ret, _| { ret.map(|_| ()) }); }); Ok(()) } #[napi] fn threadsafe_function_closure_capture(func: Function) -> napi::Result<()> { let str = "test"; let tsfn = func .build_threadsafe_function::<()>() .build_callback(move |_| { println!("Captured in ThreadsafeFunction {}", str); // str is NULL at this point Ok(String::new()) })?; tsfn.call((), ThreadsafeFunctionCallMode::NonBlocking); Ok(()) } #[napi] pub fn tsfn_call_with_callback(tsfn: ThreadsafeFunction<(), String>) -> napi::Result<()> { tsfn.call_with_return_value( Ok(()), ThreadsafeFunctionCallMode::NonBlocking, |value: Result, _| { let value = value.expect("Failed to retrieve value from JS"); println!("{}", value); assert_eq!(value, "ReturnFromJavaScriptRawCallback".to_owned()); Ok(()) }, ); Ok(()) } #[napi(ts_return_type = "Promise")] pub fn tsfn_async_call( env: Env, func: Function<(u32, u32, u32), String>, ) -> napi::Result> { let tsfn = func.build_threadsafe_function().build()?; env.spawn_future(async move { let msg = tsfn.call_async((0, 1, 2)).await?; assert_eq!(msg, "ReturnFromJavaScriptRawCallback".to_owned()); Ok(()) }) } #[napi] pub fn accept_threadsafe_function(func: ThreadsafeFunction) { thread::spawn(move || { func.call(Ok(1), ThreadsafeFunctionCallMode::NonBlocking); }); } #[napi] pub fn accept_threadsafe_function_fatal(func: ThreadsafeFunction) { thread::spawn(move || { func.call(1, ThreadsafeFunctionCallMode::NonBlocking); }); } #[napi] pub fn accept_threadsafe_function_tuple_args(func: ThreadsafeFunction<(u32, bool, String)>) { thread::spawn(move || { func.call( Ok((1, false, "NAPI-RS".into())), ThreadsafeFunctionCallMode::NonBlocking, ); }); } #[napi] pub async fn tsfn_return_promise(func: ThreadsafeFunction>) -> Result { let val = func.call_async(Ok(1)).await?.await?; Ok(val + 2) } #[napi] pub async fn tsfn_return_promise_timeout( func: ThreadsafeFunction>, ) -> Result { use tokio::time::{self, Duration}; let promise = func.call_async(Ok(1)).await?; let sleep = time::sleep(Duration::from_nanos(1)); tokio::select! { _ = sleep => { Err(Error::new(Status::GenericFailure, "Timeout".to_owned())) } value = promise => { Ok(value? + 2) } } } #[napi] pub async fn tsfn_throw_from_js(tsfn: ThreadsafeFunction>) -> napi::Result { tsfn.call_async(Ok(42)).await?.await } #[napi] pub fn spawn_thread_in_thread(tsfn: ThreadsafeFunction) { std::thread::spawn(move || { std::thread::spawn(move || { tsfn.call(Ok(42), ThreadsafeFunctionCallMode::NonBlocking); }); }); } #[napi(object, object_to_js = false)] pub struct Pet { pub name: String, pub kind: u32, pub either_tsfn: Either>, } #[napi] pub fn tsfn_in_either(pet: Pet) { if let Either::B(tsfn) = pet.either_tsfn { thread::spawn(move || { tsfn.call(Ok(42), ThreadsafeFunctionCallMode::NonBlocking); }); } }