* The MIT License (MIT)
* Copyright (C) 2024 Huawei Device Co., Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
use napi::threadsafe_function::ThreadSafeCallContext;
use napi::{
bindgen_prelude::*,
threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode},
JsBoolean, JsString,
};
use napi_derive::napi;
use std::thread;
#[napi]
pub fn call_threadsafe_function(callback: JsFunction) -> Result<()> {
let tsfn: ThreadsafeFunction<u32, ErrorStrategy::CalleeHandled> =
callback.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value + 1]))?;
for n in 0..100 {
let tsfn = tsfn.clone();
thread::spawn(move || {
tsfn.call(Ok(n), ThreadsafeFunctionCallMode::NonBlocking);
});
}
Ok(())
}
#[napi]
pub fn call_threadsafe_function_multi_args1(callback: JsFunction) -> Result<()> {
let tsfn: ThreadsafeFunction<(u8, String), ErrorStrategy::Fatal> = callback
.create_threadsafe_function(0, |ctx: ThreadSafeCallContext<(u8, String)>| {
let arg1 = ctx.env.create_uint32(ctx.value.0 as u32)?.into_unknown();
let arg2 = ctx.env.create_string(ctx.value.1.as_str())?.into_unknown();
Ok(vec![arg1, arg2])
})?;
let tsfn = tsfn.clone();
thread::spawn(move || {
tsfn.call(
(100, "hello napi 1".to_string()),
ThreadsafeFunctionCallMode::Blocking,
);
});
Ok(())
}
#[napi]
pub fn call_threadsafe_function_multi_args2(
func: ThreadsafeFunction<(u8, String), ErrorStrategy::Fatal>,
) -> Result<()> {
thread::spawn(move || {
func.call(
(100, "hello napi 2".to_string()),
ThreadsafeFunctionCallMode::Blocking,
);
});
Ok(())
}
#[napi]
pub fn threadsafe_function_throw_error(cb: JsFunction) -> Result<()> {
let tsfn: ThreadsafeFunction<bool, ErrorStrategy::CalleeHandled> =
cb.create_threadsafe_function(0, |ctx| ctx.env.get_boolean(ctx.value).map(|v| vec![v]))?;
thread::spawn(move || {
tsfn.call(
Err(Error::new(
Status::GenericFailure,
"ThrowFromNative".to_owned(),
)),
ThreadsafeFunctionCallMode::Blocking,
);
});
Ok(())
}
#[napi]
pub fn threadsafe_function_fatal_mode(cb: JsFunction) -> Result<()> {
let tsfn: ThreadsafeFunction<bool, ErrorStrategy::Fatal> =
cb.create_threadsafe_function(0, |ctx| ctx.env.get_boolean(ctx.value).map(|v| vec![v]))?;
thread::spawn(move || {
tsfn.call(true, ThreadsafeFunctionCallMode::Blocking);
});
Ok(())
}
#[napi]
pub fn threadsafe_function_fatal_mode_error(cb: JsFunction) -> Result<()> {
let tsfn: ThreadsafeFunction<bool, ErrorStrategy::Fatal> =
cb.create_threadsafe_function(0, |_ctx| {
Err::<Vec<JsBoolean>, Error>(Error::new(
Status::GenericFailure,
"Generic tsfn error".to_owned(),
))
})?;
thread::spawn(move || {
tsfn.call(true, ThreadsafeFunctionCallMode::Blocking);
});
Ok(())
}
#[napi]
fn threadsafe_function_closure_capture(func: JsFunction) -> napi::Result<()> {
let str = "threadsafe_function_closure_capture";
let tsfn: ThreadsafeFunction<()> = func.create_threadsafe_function(0, move |ctx| {
let x = ctx.env.create_string(str)?;
Ok(vec![x])
})?;
tsfn.call(Ok(()), ThreadsafeFunctionCallMode::NonBlocking);
Ok(())
}
#[napi]
pub fn tsfn_call_with_callback(func: JsFunction) -> napi::Result<()> {
let tsfn: ThreadsafeFunction<()> =
func.create_threadsafe_function(0, move |_| Ok(Vec::<JsString>::new()))?;
tsfn.call_with_return_value(
Ok(()),
ThreadsafeFunctionCallMode::NonBlocking,
|value: String| {
assert_eq!(value, "ReturnFromJavaScriptRawCallback".to_owned());
Ok(())
},
);
Ok(())
}
#[napi(ts_return_type = "Promise<string>")]
pub fn tsfn_async_call(env: Env, func: JsFunction) -> napi::Result<Object> {
let tsfn: ThreadsafeFunction<()> =
func.create_threadsafe_function(0, move |_| Ok(vec![0u32, 1u32, 2u32]))?;
env.spawn_future(async move {
let msg: String = tsfn.call_async(Ok(())).await?;
assert_eq!(msg, "ReturnFromJavaScriptRawCallback".to_owned());
Ok(msg + "[rust]")
})
}
#[napi]
pub fn accept_threadsafe_function(func: ThreadsafeFunction<u32>) {
thread::spawn(move || {
func.call(Ok(1), ThreadsafeFunctionCallMode::NonBlocking);
});
}
#[napi]
pub fn accept_threadsafe_function_fatal(func: ThreadsafeFunction<u32, ErrorStrategy::Fatal>) {
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<u32>) -> Result<u32> {
let val = func.call_async::<Promise<u32>>(Ok(1)).await?.await?;
Ok(val + 2)
}
#[napi]
pub async fn tsfn_return_promise_timeout(func: ThreadsafeFunction<u32>) -> Result<u32> {
use tokio::time::{self, Duration};
let promise = func.call_async::<Promise<u32>>(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<u32>) -> napi::Result<u32> {
tsfn.call_async::<Promise<u32>>(Ok(42)).await?.await
}