rspack_napi/js_values/
threadsafe_one_shot_value_ref.rs1#![allow(clippy::not_unsafe_ptr_arg_deref)]
2
3use std::{
4 ffi::c_void,
5 ptr,
6 sync::atomic::{AtomicPtr, Ordering},
7 thread::{self, ThreadId},
8};
9
10use napi::{
11 Env, Result,
12 bindgen_prelude::{FromNapiValue, ToNapiValue, check_status},
13 sys::{self, napi_call_threadsafe_function, napi_env, napi_ref, napi_threadsafe_function__},
14};
15
16use crate::{CLEANUP_ENV_HOOK, GLOBAL_CLEANUP_FLAG};
17
18static DELETE_REF_TS_FN: AtomicPtr<napi_threadsafe_function__> = AtomicPtr::new(ptr::null_mut());
19
20extern "C" fn napi_js_callback(
21 env: sys::napi_env,
22 _js_callback: sys::napi_value,
23 _context: *mut c_void,
24 data: *mut c_void,
25) {
26 if env.is_null() {
28 return;
29 }
30 unsafe { sys::napi_delete_reference(env, data as napi_ref) };
31}
32
33pub struct ThreadsafeOneShotRef {
34 env: napi_env,
35 napi_ref: sys::napi_ref,
36 thread_id: ThreadId,
37}
38
39impl ThreadsafeOneShotRef {
40 pub fn new<T: ToNapiValue>(env: napi_env, val: T) -> Result<Self> {
41 let napi_value = unsafe { ToNapiValue::to_napi_value(env, val)? };
42
43 let mut napi_ref = ptr::null_mut();
44 check_status!(unsafe { sys::napi_create_reference(env, napi_value, 1, &mut napi_ref) })?;
45
46 Self::from_napi_ref(env, napi_ref)
47 }
48
49 pub fn from_napi_ref(env: napi_env, r: sys::napi_ref) -> Result<Self> {
50 let env_wrapper = Env::from(env);
51
52 CLEANUP_ENV_HOOK.with(|ref_cell| {
53 if ref_cell.borrow().is_none() {
54 let result = env_wrapper.add_env_cleanup_hook((), move |_| {
55 CLEANUP_ENV_HOOK.with_borrow_mut(|cleanup_env_hook| *cleanup_env_hook = None);
56 GLOBAL_CLEANUP_FLAG.set(true);
57 });
58 if let Ok(cleanup_env_hook) = result {
59 *ref_cell.borrow_mut() = Some(cleanup_env_hook);
60 }
61 }
62 });
63
64 if DELETE_REF_TS_FN.load(Ordering::Relaxed).is_null() {
65 let mut async_resource_name = ptr::null_mut();
66 check_status!(
67 unsafe {
68 sys::napi_create_string_utf8(
69 env,
70 c"delete_reference_ts_fn".as_ptr(),
71 16,
72 &mut async_resource_name,
73 )
74 },
75 "Failed to create async resource name"
76 )?;
77
78 let mut ts_fn = ptr::null_mut();
79 check_status!(
80 unsafe {
81 sys::napi_create_threadsafe_function(
82 env,
83 ptr::null_mut(),
84 ptr::null_mut(),
85 async_resource_name,
86 0,
87 1,
88 ptr::null_mut(),
89 None,
90 ptr::null_mut(),
91 Some(napi_js_callback),
92 &mut ts_fn,
93 )
94 },
95 "Failed to create threadsafe function"
96 )?;
97 check_status!(unsafe { sys::napi_unref_threadsafe_function(env, ts_fn) })?;
98 DELETE_REF_TS_FN.store(ts_fn, Ordering::Relaxed);
99 }
100
101 Ok(Self {
102 thread_id: thread::current().id(),
103 env,
104 napi_ref: r,
105 })
106 }
107}
108
109impl Drop for ThreadsafeOneShotRef {
110 fn drop(&mut self) {
111 if GLOBAL_CLEANUP_FLAG.get() {
112 return;
113 }
114 if self.thread_id == thread::current().id() {
115 unsafe { sys::napi_delete_reference(self.env, self.napi_ref) };
116 } else {
117 let ts_fn = DELETE_REF_TS_FN.load(Ordering::Relaxed);
118 unsafe {
119 let _ = napi_call_threadsafe_function(
120 ts_fn,
121 self.napi_ref.cast(),
122 sys::ThreadsafeFunctionCallMode::nonblocking,
123 );
124 }
125 }
126 }
127}
128
129impl ToNapiValue for &ThreadsafeOneShotRef {
130 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
131 let mut result = ptr::null_mut();
132 check_status!(
133 unsafe { sys::napi_get_reference_value(env, val.napi_ref, &mut result) },
134 "Failed to get reference value"
135 )?;
136 Ok(result)
137 }
138}
139
140impl ToNapiValue for &mut ThreadsafeOneShotRef {
141 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
142 let mut result = ptr::null_mut();
143 check_status!(
144 unsafe { sys::napi_get_reference_value(env, val.napi_ref, &mut result) },
145 "Failed to get reference value"
146 )?;
147 Ok(result)
148 }
149}
150
151impl ToNapiValue for ThreadsafeOneShotRef {
152 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
153 unsafe { ToNapiValue::to_napi_value(env, &val) }
154 }
155}
156
157impl FromNapiValue for ThreadsafeOneShotRef {
158 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
159 let mut napi_ref = ptr::null_mut();
160 check_status!(unsafe { sys::napi_create_reference(env, napi_val, 1, &mut napi_ref) })?;
161
162 Self::from_napi_ref(env, napi_ref)
163 }
164}