1use crate::prelude::*;
2use std::marker::PhantomData;
3
4#[derive(Copy, Clone, Debug)]
5pub struct NapiThreadsafeFunction<Data, const N: usize>(
6 NapiEnv,
7 napi_threadsafe_function,
8 PhantomData<Data>,
9);
10
11unsafe impl<Data, const N: usize> Send for NapiThreadsafeFunction<Data, N> {}
12unsafe impl<Data, const N: usize> Sync for NapiThreadsafeFunction<Data, N> {}
13
14impl<Data, const N: usize> NapiThreadsafeFunction<Data, N> {
15 pub(crate) fn from_raw(env: NapiEnv, tsfn: napi_threadsafe_function) -> Self {
16 NapiThreadsafeFunction(env, tsfn, PhantomData)
17 }
18
19 pub fn env(&self) -> NapiEnv {
20 self.0
21 }
22
23 pub fn raw(&self) -> napi_threadsafe_function {
24 self.1
25 }
26
27 #[allow(clippy::type_complexity)]
28 pub fn new<R: NapiValueT>(
33 env: NapiEnv,
34 name: impl AsRef<str>,
35 func: Function<R>,
36 finalizer: impl FnOnce(NapiEnv) -> NapiResult<()>,
37 callback: impl FnMut(Function<R>, Data) -> NapiResult<()>,
38 ) -> NapiResult<NapiThreadsafeFunction<Data, N>>
39 where
40 R: NapiValueT,
41 {
42 unsafe extern "C" fn finalizer_trampoline(
43 env: NapiEnv,
44 finalizer: DataPointer,
45 _: DataPointer,
46 ) {
47 let finalizer: Box<Box<dyn FnOnce(NapiEnv) -> NapiResult<()>>> =
48 Box::from_raw(finalizer as _);
49
50 if let Err(err) = finalizer(env) {
51 log::error!("NapiThreadsafeFunction::finalizer(): {}", err);
52 }
53 }
54
55 unsafe extern "C" fn call_js_trampoline<R: NapiValueT, Data>(
56 env: NapiEnv,
57 cb: napi_value,
58 context: DataPointer,
59 data: DataPointer,
60 ) {
61 let context: &mut Box<dyn FnMut(Function<R>, Data) -> NapiResult<()>> =
62 std::mem::transmute(&mut *(context as *mut _));
63 let data: Box<Data> = Box::from_raw(data as _);
64
65 if let Err(e) = context(Function::<R>::from_raw(env, cb), *data) {
66 log::error!("NapiThreadsafeFunction::call_js_trampoline: {}", e);
67 }
68 }
69
70 let context: Box<Box<dyn FnMut(Function<R>, Data) -> NapiResult<()>>> =
71 Box::new(Box::new(callback));
72 let context = Box::into_raw(context);
74 let finalizer: Box<Box<dyn FnOnce(NapiEnv) -> NapiResult<()>>> =
75 Box::new(Box::new(move |env| -> NapiResult<()> {
76 unsafe {
77 Box::from_raw(context);
78 }
79 finalizer(env)
80 }));
81
82 let tsfn = napi_call!(
83 =napi_create_threadsafe_function,
84 env,
85 func.raw(),
86 std::ptr::null_mut(),
87 env.string(name.as_ref())?.raw(),
88 N,
89 1,
90 Box::into_raw(finalizer) as _,
91 Some(finalizer_trampoline),
92 context as _,
93 Some(call_js_trampoline::<R, Data>),
94 );
95
96 Ok(NapiThreadsafeFunction(env, tsfn, PhantomData))
97 }
98
99 pub fn call(&self, data: Data, mode: NapiThreadsafeFunctionCallMode) -> NapiResult<()> {
113 napi_call!(
114 napi_call_threadsafe_function,
115 self.raw(),
116 Box::into_raw(Box::new(data)) as _,
117 mode,
118 )
119 }
120
121 #[inline]
122 pub fn blocking(&self, data: Data) -> NapiResult<()> {
123 self.call(data, NapiThreadsafeFunctionCallMode::Blocking)
124 }
125
126 #[inline]
127 pub fn non_blocking(&self, data: Data) -> NapiResult<()> {
128 self.call(data, NapiThreadsafeFunctionCallMode::Nonblocking)
129 }
130
131 pub fn acquire(&self) -> NapiResult<()> {
137 napi_call!(napi_acquire_threadsafe_function, self.raw())
138 }
139
140 pub fn release(self) -> NapiResult<()> {
146 napi_call!(
147 napi_release_threadsafe_function,
148 self.raw(),
149 NapiTsfnReleaseMode::Release
150 )
151 }
152
153 pub fn abort(self) -> NapiResult<()> {
159 napi_call!(
160 napi_release_threadsafe_function,
161 self.raw(),
162 NapiTsfnReleaseMode::Abort
163 )
164 }
165
166 pub fn refer(&self) -> NapiResult<()> {
176 napi_call!(napi_ref_threadsafe_function, self.env(), self.raw())
177 }
178
179 pub fn unref(&self) -> NapiResult<()> {
184 napi_call!(napi_unref_threadsafe_function, self.env(), self.raw())
185 }
186}
187
188pub type NapiTsfn<Data> = NapiThreadsafeFunction<Data, 0>;