1 | use std::collections::HashMap; |
2 | #[cfg (not(feature = "noop" ))] |
3 | use std::collections::HashSet; |
4 | use std::ffi::CStr; |
5 | use std::ptr; |
6 | #[cfg (all(not(target_family = "wasm" ), feature = "napi4" , feature = "tokio_rt" ))] |
7 | use std::sync::atomic::AtomicUsize; |
8 | #[cfg (not(feature = "noop" ))] |
9 | use std::sync::atomic::{AtomicBool, Ordering}; |
10 | use std::sync::RwLock; |
11 | use std::thread::ThreadId; |
12 | |
13 | use once_cell::sync::Lazy; |
14 | |
15 | use crate::{check_status, sys, Env, JsFunction, Property, Result, Value, ValueType}; |
16 | #[cfg (not(feature = "noop" ))] |
17 | use crate::{check_status_or_throw, JsError}; |
18 | |
19 | pub type ExportRegisterCallback = unsafe fn(sys::napi_env) -> Result<sys::napi_value>; |
20 | pub type ModuleExportsCallback = |
21 | unsafe fn(env: sys::napi_env, exports: sys::napi_value) -> Result<()>; |
22 | |
23 | #[repr (transparent)] |
24 | pub(crate) struct PersistedPerInstanceHashMap<K, V>(RwLock<HashMap<K, V>>); |
25 | |
26 | impl<K, V> PersistedPerInstanceHashMap<K, V> { |
27 | #[cfg (not(feature = "noop" ))] |
28 | pub(crate) fn from_hashmap(hashmap: HashMap<K, V>) -> Self { |
29 | Self(RwLock::new(hashmap)) |
30 | } |
31 | |
32 | #[allow (clippy::mut_from_ref)] |
33 | pub(crate) fn borrow_mut<F, R>(&self, f: F) -> R |
34 | where |
35 | F: FnOnce(&mut HashMap<K, V>) -> R, |
36 | { |
37 | let mut write_lock: RwLockWriteGuard<'_, HashMap<…, …>> = self.0.write().unwrap(); |
38 | f(&mut *write_lock) |
39 | } |
40 | } |
41 | |
42 | impl<K, V> Default for PersistedPerInstanceHashMap<K, V> { |
43 | fn default() -> Self { |
44 | Self(RwLock::new(HashMap::default())) |
45 | } |
46 | } |
47 | |
48 | type ModuleRegisterCallback = |
49 | RwLock<Vec<(Option<&'static str>, (&'static str, ExportRegisterCallback))>>; |
50 | |
51 | type ModuleClassProperty = PersistedPerInstanceHashMap< |
52 | &'static str, |
53 | HashMap<Option<&'static str>, (&'static str, Vec<Property>)>, |
54 | >; |
55 | |
56 | unsafe impl<K, V> Send for PersistedPerInstanceHashMap<K, V> {} |
57 | unsafe impl<K, V> Sync for PersistedPerInstanceHashMap<K, V> {} |
58 | |
59 | type FnRegisterMap = |
60 | PersistedPerInstanceHashMap<ExportRegisterCallback, (sys::napi_callback, &'static str)>; |
61 | type RegisteredClassesMap = PersistedPerInstanceHashMap<ThreadId, RegisteredClasses>; |
62 | |
63 | static MODULE_REGISTER_CALLBACK: Lazy<ModuleRegisterCallback> = Lazy::new(Default::default); |
64 | static MODULE_CLASS_PROPERTIES: Lazy<ModuleClassProperty> = Lazy::new(Default::default); |
65 | #[cfg (not(feature = "noop" ))] |
66 | static IS_FIRST_MODULE: AtomicBool = AtomicBool::new(true); |
67 | #[cfg (not(feature = "noop" ))] |
68 | static FIRST_MODULE_REGISTERED: AtomicBool = AtomicBool::new(false); |
69 | static REGISTERED_CLASSES: Lazy<RegisteredClassesMap> = Lazy::new(Default::default); |
70 | static FN_REGISTER_MAP: Lazy<FnRegisterMap> = Lazy::new(Default::default); |
71 | #[cfg (all(feature = "napi4" , not(feature = "noop" ), not(target_family = "wasm" )))] |
72 | pub(crate) static CUSTOM_GC_TSFN: std::sync::atomic::AtomicPtr<sys::napi_threadsafe_function__> = |
73 | std::sync::atomic::AtomicPtr::new(ptr::null_mut()); |
74 | #[cfg (all(feature = "napi4" , not(feature = "noop" ), not(target_family = "wasm" )))] |
75 | pub(crate) static CUSTOM_GC_TSFN_DESTROYED: AtomicBool = AtomicBool::new(false); |
76 | #[cfg (all(feature = "napi4" , not(feature = "noop" ), not(target_family = "wasm" )))] |
77 | // Store thread id of the thread that created the CustomGC ThreadsafeFunction. |
78 | pub(crate) static THREADS_CAN_ACCESS_ENV: once_cell::sync::Lazy< |
79 | PersistedPerInstanceHashMap<ThreadId, bool>, |
80 | > = once_cell::sync::Lazy::new(Default::default); |
81 | |
82 | type RegisteredClasses = |
83 | PersistedPerInstanceHashMap</* export name */ String, /* constructor */ sys::napi_ref>; |
84 | |
85 | #[cfg (all(feature = "compat-mode" , not(feature = "noop" )))] |
86 | // compatibility for #[module_exports] |
87 | static MODULE_EXPORTS: Lazy<RwLock<Vec<ModuleExportsCallback>>> = Lazy::new(Default::default); |
88 | |
89 | #[cfg (not(feature = "noop" ))] |
90 | #[inline ] |
91 | fn wait_first_thread_registered() { |
92 | while !FIRST_MODULE_REGISTERED.load(order:Ordering::SeqCst) { |
93 | std::hint::spin_loop(); |
94 | } |
95 | } |
96 | |
97 | #[doc (hidden)] |
98 | pub fn get_class_constructor(js_name: &'static str) -> Option<sys::napi_ref> { |
99 | let current_id: ThreadId = std::thread::current().id(); |
100 | REGISTERED_CLASSES.borrow_mut(|map: &mut HashMap>| { |
101 | mapOption<&PersistedPerInstanceHashMap<…, …>> |
102 | .get(¤t_id) |
103 | .map(|m: &PersistedPerInstanceHashMap<…, …>| m.borrow_mut(|map: &mut HashMap| map.get(js_name).copied())) |
104 | })? |
105 | } |
106 | |
107 | #[doc (hidden)] |
108 | #[cfg (all(feature = "compat-mode" , not(feature = "noop" )))] |
109 | // compatibility for #[module_exports] |
110 | pub fn register_module_exports(callback: ModuleExportsCallback) { |
111 | MODULE_EXPORTS |
112 | .write() |
113 | .expect("Register module exports failed" ) |
114 | .push(callback); |
115 | } |
116 | |
117 | #[doc (hidden)] |
118 | pub fn register_module_export( |
119 | js_mod: Option<&'static str>, |
120 | name: &'static str, |
121 | cb: ExportRegisterCallback, |
122 | ) { |
123 | MODULE_REGISTER_CALLBACKRwLockWriteGuard<'_, Vec<…>> |
124 | .write() |
125 | .expect(msg:"Register module export failed" ) |
126 | .push((js_mod, (name, cb))); |
127 | } |
128 | |
129 | #[doc (hidden)] |
130 | pub fn register_js_function( |
131 | name: &'static str, |
132 | cb: ExportRegisterCallback, |
133 | c_fn: sys::napi_callback, |
134 | ) { |
135 | FN_REGISTER_MAP.borrow_mut(|inner: &mut HashMap …, …>| { |
136 | inner.insert(k:cb, (c_fn, name)); |
137 | }); |
138 | } |
139 | |
140 | #[doc (hidden)] |
141 | pub fn register_class( |
142 | rust_name: &'static str, |
143 | js_mod: Option<&'static str>, |
144 | js_name: &'static str, |
145 | props: Vec<Property>, |
146 | ) { |
147 | MODULE_CLASS_PROPERTIES.borrow_mut(|inner: &mut HashMap<&'static str, HashMap<…, …>>| { |
148 | let val: &mut HashMap = inner.entry(key:rust_name).or_default(); |
149 | let val: &mut (&'static str, Vec) = val.entry(key:js_mod).or_default(); |
150 | val.0 = js_name; |
151 | val.1.extend(iter:props); |
152 | }); |
153 | } |
154 | |
155 | #[inline ] |
156 | /// Get `JsFunction` from defined Rust `fn` |
157 | /// ```rust |
158 | /// #[napi] |
159 | /// fn some_fn() -> u32 { |
160 | /// 1 |
161 | /// } |
162 | /// |
163 | /// #[napi] |
164 | /// fn return_some_fn() -> Result<JsFunction> { |
165 | /// get_js_function(some_fn_js_function) |
166 | /// } |
167 | /// ``` |
168 | /// |
169 | /// ```js |
170 | /// returnSomeFn()(); // 1 |
171 | /// ``` |
172 | /// |
173 | pub fn get_js_function(env: &Env, raw_fn: ExportRegisterCallback) -> Result<JsFunction> { |
174 | FN_REGISTER_MAP.borrow_mut(|inner| { |
175 | inner |
176 | .get(&raw_fn) |
177 | .and_then(|(cb, name)| { |
178 | let mut function = ptr::null_mut(); |
179 | let name_len = name.len() - 1; |
180 | let fn_name = unsafe { CStr::from_bytes_with_nul_unchecked(name.as_bytes()) }; |
181 | check_status!(unsafe { |
182 | sys::napi_create_function( |
183 | env.0, |
184 | fn_name.as_ptr(), |
185 | name_len, |
186 | *cb, |
187 | ptr::null_mut(), |
188 | &mut function, |
189 | ) |
190 | }) |
191 | .ok()?; |
192 | Some(JsFunction(Value { |
193 | env: env.0, |
194 | value: function, |
195 | value_type: ValueType::Function, |
196 | })) |
197 | }) |
198 | .ok_or_else(|| { |
199 | crate::Error::new( |
200 | crate::Status::InvalidArg, |
201 | "JavaScript function does not exist" .to_owned(), |
202 | ) |
203 | }) |
204 | }) |
205 | } |
206 | |
207 | /// Get `C Callback` from defined Rust `fn` |
208 | /// ```rust |
209 | /// #[napi] |
210 | /// fn some_fn() -> u32 { |
211 | /// 1 |
212 | /// } |
213 | /// |
214 | /// #[napi] |
215 | /// fn create_obj(env: Env) -> Result<JsObject> { |
216 | /// let mut obj = env.create_object()?; |
217 | /// obj.define_property(&[Property::new("getter" )?.with_getter(get_c_callback(some_fn_js_function)?)])?; |
218 | /// Ok(obj) |
219 | /// } |
220 | /// ``` |
221 | /// |
222 | /// ```js |
223 | /// console.log(createObj().getter) // 1 |
224 | /// ``` |
225 | /// |
226 | pub fn get_c_callback(raw_fn: ExportRegisterCallback) -> Result<crate::Callback> { |
227 | FN_REGISTER_MAP.borrow_mut(|inner: &mut HashMap …, …>| { |
228 | inner |
229 | .get(&raw_fn) |
230 | .and_then(|(cb, _name)| *cb) |
231 | .ok_or_else(|| { |
232 | crate::Error::new( |
233 | crate::Status::InvalidArg, |
234 | reason:"JavaScript function does not exist" .to_owned(), |
235 | ) |
236 | }) |
237 | }) |
238 | } |
239 | |
240 | #[cfg (all(any(windows, feature = "dyn-symbols" ), not(feature = "noop" )))] |
241 | #[ctor::ctor ] |
242 | fn load_host() { |
243 | unsafe { |
244 | sys::setup(); |
245 | } |
246 | } |
247 | |
248 | #[cfg (all(target_family = "wasm" , not(feature = "noop" )))] |
249 | #[no_mangle ] |
250 | unsafe extern "C" fn napi_register_wasm_v1( |
251 | env: sys::napi_env, |
252 | exports: sys::napi_value, |
253 | ) -> sys::napi_value { |
254 | unsafe { napi_register_module_v1(env, exports) } |
255 | } |
256 | |
257 | #[cfg (not(feature = "noop" ))] |
258 | #[no_mangle ] |
259 | /// Register the n-api module exports. |
260 | /// |
261 | /// # Safety |
262 | /// This method is meant to be called by Node.js while importing the n-api module. |
263 | /// Only call this method if the current module is **not** imported by a node-like runtime. |
264 | /// |
265 | /// Arguments `env` and `exports` must **not** be null. |
266 | pub unsafe extern "C" fn napi_register_module_v1( |
267 | env: sys::napi_env, |
268 | exports: sys::napi_value, |
269 | ) -> sys::napi_value { |
270 | #[cfg (all( |
271 | any(target_env = "msvc" , feature = "dyn-symbols" ), |
272 | not(feature = "noop" ) |
273 | ))] |
274 | unsafe { |
275 | sys::setup(); |
276 | } |
277 | if IS_FIRST_MODULE.load(Ordering::SeqCst) { |
278 | IS_FIRST_MODULE.store(false, Ordering::SeqCst); |
279 | } else { |
280 | wait_first_thread_registered(); |
281 | } |
282 | let mut exports_objects: HashSet<String> = HashSet::default(); |
283 | |
284 | { |
285 | let mut register_callback = MODULE_REGISTER_CALLBACK |
286 | .write() |
287 | .expect("Write MODULE_REGISTER_CALLBACK in napi_register_module_v1 failed" ); |
288 | register_callback |
289 | .iter_mut() |
290 | .fold( |
291 | HashMap::<Option<&'static str>, Vec<(&'static str, ExportRegisterCallback)>>::new(), |
292 | |mut acc, (js_mod, item)| { |
293 | if let Some(k) = acc.get_mut(js_mod) { |
294 | k.push(*item); |
295 | } else { |
296 | acc.insert(*js_mod, vec![*item]); |
297 | } |
298 | acc |
299 | }, |
300 | ) |
301 | .iter() |
302 | .for_each(|(js_mod, items)| { |
303 | let mut exports_js_mod = ptr::null_mut(); |
304 | if let Some(js_mod_str) = js_mod { |
305 | let mod_name_c_str = |
306 | unsafe { CStr::from_bytes_with_nul_unchecked(js_mod_str.as_bytes()) }; |
307 | if exports_objects.contains(*js_mod_str) { |
308 | check_status_or_throw!( |
309 | env, |
310 | unsafe { |
311 | sys::napi_get_named_property( |
312 | env, |
313 | exports, |
314 | mod_name_c_str.as_ptr(), |
315 | &mut exports_js_mod, |
316 | ) |
317 | }, |
318 | "Get mod {} from exports failed" , |
319 | js_mod_str, |
320 | ); |
321 | } else { |
322 | check_status_or_throw!( |
323 | env, |
324 | unsafe { sys::napi_create_object(env, &mut exports_js_mod) }, |
325 | "Create export JavaScript Object [ {}] failed" , |
326 | js_mod_str |
327 | ); |
328 | check_status_or_throw!( |
329 | env, |
330 | unsafe { |
331 | sys::napi_set_named_property(env, exports, mod_name_c_str.as_ptr(), exports_js_mod) |
332 | }, |
333 | "Set exports Object [ {}] into exports object failed" , |
334 | js_mod_str |
335 | ); |
336 | exports_objects.insert(js_mod_str.to_string()); |
337 | } |
338 | } |
339 | for (name, callback) in items { |
340 | unsafe { |
341 | let js_name = CStr::from_bytes_with_nul_unchecked(name.as_bytes()); |
342 | if let Err(e) = callback(env).and_then(|v| { |
343 | let exported_object = if exports_js_mod.is_null() { |
344 | exports |
345 | } else { |
346 | exports_js_mod |
347 | }; |
348 | check_status!( |
349 | sys::napi_set_named_property(env, exported_object, js_name.as_ptr(), v), |
350 | "Failed to register export ` {}`" , |
351 | name, |
352 | ) |
353 | }) { |
354 | JsError::from(e).throw_into(env) |
355 | } |
356 | } |
357 | } |
358 | }); |
359 | } |
360 | |
361 | let mut registered_classes = HashMap::new(); |
362 | |
363 | MODULE_CLASS_PROPERTIES.borrow_mut(|inner| { |
364 | inner.iter().for_each(|(rust_name, js_mods)| { |
365 | for (js_mod, (js_name, props)) in js_mods { |
366 | let mut exports_js_mod = ptr::null_mut(); |
367 | unsafe { |
368 | if let Some(js_mod_str) = js_mod { |
369 | let mod_name_c_str = CStr::from_bytes_with_nul_unchecked(js_mod_str.as_bytes()); |
370 | if exports_objects.contains(*js_mod_str) { |
371 | check_status_or_throw!( |
372 | env, |
373 | sys::napi_get_named_property( |
374 | env, |
375 | exports, |
376 | mod_name_c_str.as_ptr(), |
377 | &mut exports_js_mod, |
378 | ), |
379 | "Get mod {} from exports failed" , |
380 | js_mod_str, |
381 | ); |
382 | } else { |
383 | check_status_or_throw!( |
384 | env, |
385 | sys::napi_create_object(env, &mut exports_js_mod), |
386 | "Create export JavaScript Object [ {}] failed" , |
387 | js_mod_str |
388 | ); |
389 | check_status_or_throw!( |
390 | env, |
391 | sys::napi_set_named_property(env, exports, mod_name_c_str.as_ptr(), exports_js_mod), |
392 | "Set exports Object [ {}] into exports object failed" , |
393 | js_mod_str |
394 | ); |
395 | exports_objects.insert(js_mod_str.to_string()); |
396 | } |
397 | } |
398 | let (ctor, props): (Vec<_>, Vec<_>) = props.iter().partition(|prop| prop.is_ctor); |
399 | |
400 | let ctor = ctor |
401 | .first() |
402 | .map(|c| c.raw().method.unwrap()) |
403 | .unwrap_or(noop); |
404 | let raw_props: Vec<_> = props.iter().map(|prop| prop.raw()).collect(); |
405 | |
406 | let js_class_name = CStr::from_bytes_with_nul_unchecked(js_name.as_bytes()); |
407 | let mut class_ptr = ptr::null_mut(); |
408 | |
409 | check_status_or_throw!( |
410 | env, |
411 | sys::napi_define_class( |
412 | env, |
413 | js_class_name.as_ptr(), |
414 | js_name.len() - 1, |
415 | Some(ctor), |
416 | ptr::null_mut(), |
417 | raw_props.len(), |
418 | raw_props.as_ptr(), |
419 | &mut class_ptr, |
420 | ), |
421 | "Failed to register class ` {}` generate by struct ` {}`" , |
422 | &js_name, |
423 | &rust_name |
424 | ); |
425 | |
426 | let mut ctor_ref = ptr::null_mut(); |
427 | sys::napi_create_reference(env, class_ptr, 1, &mut ctor_ref); |
428 | |
429 | registered_classes.insert(js_name.to_string(), ctor_ref); |
430 | |
431 | check_status_or_throw!( |
432 | env, |
433 | sys::napi_set_named_property( |
434 | env, |
435 | if exports_js_mod.is_null() { |
436 | exports |
437 | } else { |
438 | exports_js_mod |
439 | }, |
440 | js_class_name.as_ptr(), |
441 | class_ptr |
442 | ), |
443 | "Failed to register class ` {}` generate by struct ` {}`" , |
444 | &js_name, |
445 | &rust_name |
446 | ); |
447 | } |
448 | } |
449 | }); |
450 | |
451 | REGISTERED_CLASSES.borrow_mut(|map| { |
452 | map.insert( |
453 | std::thread::current().id(), |
454 | PersistedPerInstanceHashMap::from_hashmap(registered_classes), |
455 | ) |
456 | }); |
457 | }); |
458 | |
459 | #[cfg (feature = "compat-mode" )] |
460 | { |
461 | let module_exports = MODULE_EXPORTS.read().expect("Read MODULE_EXPORTS failed" ); |
462 | module_exports.iter().for_each(|callback| unsafe { |
463 | if let Err(e) = callback(env, exports) { |
464 | JsError::from(e).throw_into(env); |
465 | } |
466 | }) |
467 | } |
468 | |
469 | #[cfg (all(not(target_family = "wasm" ), feature = "napi4" , feature = "tokio_rt" ))] |
470 | { |
471 | crate::tokio_runtime::ensure_runtime(); |
472 | |
473 | static init_counter: AtomicUsize = AtomicUsize::new(0); |
474 | let cleanup_hook_payload = |
475 | init_counter.fetch_add(1, Ordering::Relaxed) as *mut std::ffi::c_void; |
476 | |
477 | unsafe { |
478 | sys::napi_add_env_cleanup_hook( |
479 | env, |
480 | Some(crate::tokio_runtime::drop_runtime), |
481 | cleanup_hook_payload, |
482 | ) |
483 | }; |
484 | } |
485 | #[cfg (all(feature = "napi4" , not(target_family = "wasm" )))] |
486 | create_custom_gc(env); |
487 | FIRST_MODULE_REGISTERED.store(true, Ordering::SeqCst); |
488 | exports |
489 | } |
490 | |
491 | #[cfg (not(feature = "noop" ))] |
492 | pub(crate) unsafe extern "C" fn noop( |
493 | env: sys::napi_env, |
494 | _info: sys::napi_callback_info, |
495 | ) -> sys::napi_value { |
496 | if !crate::bindgen_runtime::___CALL_FROM_FACTORY.with(|s: &AtomicBool| s.load(order:Ordering::Relaxed)) { |
497 | unsafe { |
498 | sys::napi_throw_error( |
499 | env, |
500 | code:ptr::null_mut(), |
501 | msg:CStr::from_bytes_with_nul_unchecked(bytes:b"Class contains no `constructor`, can not new it! \0" ) |
502 | .as_ptr(), |
503 | ); |
504 | } |
505 | } |
506 | ptr::null_mut() |
507 | } |
508 | |
509 | #[cfg (all(feature = "napi4" , not(target_family = "wasm" ), not(feature = "noop" )))] |
510 | fn create_custom_gc(env: sys::napi_env) { |
511 | if !FIRST_MODULE_REGISTERED.load(Ordering::SeqCst) { |
512 | let mut custom_gc_fn = ptr::null_mut(); |
513 | check_status_or_throw!( |
514 | env, |
515 | unsafe { |
516 | sys::napi_create_function( |
517 | env, |
518 | "custom_gc" .as_ptr().cast(), |
519 | 9, |
520 | Some(empty), |
521 | ptr::null_mut(), |
522 | &mut custom_gc_fn, |
523 | ) |
524 | }, |
525 | "Create Custom GC Function in napi_register_module_v1 failed" |
526 | ); |
527 | let mut async_resource_name = ptr::null_mut(); |
528 | check_status_or_throw!( |
529 | env, |
530 | unsafe { |
531 | sys::napi_create_string_utf8(env, "CustomGC" .as_ptr().cast(), 8, &mut async_resource_name) |
532 | }, |
533 | "Create async resource string in napi_register_module_v1" |
534 | ); |
535 | let mut custom_gc_tsfn = ptr::null_mut(); |
536 | check_status_or_throw!( |
537 | env, |
538 | unsafe { |
539 | sys::napi_create_threadsafe_function( |
540 | env, |
541 | custom_gc_fn, |
542 | ptr::null_mut(), |
543 | async_resource_name, |
544 | 0, |
545 | 1, |
546 | ptr::null_mut(), |
547 | Some(custom_gc_finalize), |
548 | ptr::null_mut(), |
549 | Some(custom_gc), |
550 | &mut custom_gc_tsfn, |
551 | ) |
552 | }, |
553 | "Create Custom GC ThreadsafeFunction in napi_register_module_v1 failed" |
554 | ); |
555 | check_status_or_throw!( |
556 | env, |
557 | unsafe { sys::napi_unref_threadsafe_function(env, custom_gc_tsfn) }, |
558 | "Unref Custom GC ThreadsafeFunction in napi_register_module_v1 failed" |
559 | ); |
560 | CUSTOM_GC_TSFN.store(custom_gc_tsfn, Ordering::Relaxed); |
561 | } |
562 | |
563 | let current_thread_id = std::thread::current().id(); |
564 | THREADS_CAN_ACCESS_ENV.borrow_mut(|m| m.insert(current_thread_id, true)); |
565 | check_status_or_throw!( |
566 | env, |
567 | unsafe { |
568 | sys::napi_add_env_cleanup_hook( |
569 | env, |
570 | Some(remove_thread_id), |
571 | Box::into_raw(Box::new(current_thread_id)).cast(), |
572 | ) |
573 | }, |
574 | "Failed to add remove thread id cleanup hook" |
575 | ); |
576 | } |
577 | |
578 | #[cfg (all(feature = "napi4" , not(target_family = "wasm" ), not(feature = "noop" )))] |
579 | unsafe extern "C" fn remove_thread_id(id: *mut std::ffi::c_void) { |
580 | let thread_id: Box = unsafe { Box::from_raw(id.cast::<ThreadId>()) }; |
581 | THREADS_CAN_ACCESS_ENV.borrow_mut(|m: &mut HashMap| m.insert(*thread_id, v:false)); |
582 | } |
583 | |
584 | #[cfg (all(feature = "napi4" , not(target_family = "wasm" ), not(feature = "noop" )))] |
585 | #[allow (unused)] |
586 | unsafe extern "C" fn empty(env: sys::napi_env, info: sys::napi_callback_info) -> sys::napi_value { |
587 | ptr::null_mut() |
588 | } |
589 | |
590 | #[cfg (all(feature = "napi4" , not(target_family = "wasm" ), not(feature = "noop" )))] |
591 | #[allow (unused_variables)] |
592 | unsafe extern "C" fn custom_gc_finalize( |
593 | env: sys::napi_env, |
594 | finalize_data: *mut std::ffi::c_void, |
595 | finalize_hint: *mut std::ffi::c_void, |
596 | ) { |
597 | CUSTOM_GC_TSFN_DESTROYED.store(val:true, order:Ordering::SeqCst); |
598 | } |
599 | |
600 | #[cfg (all(feature = "napi4" , not(target_family = "wasm" ), not(feature = "noop" )))] |
601 | // recycle the ArrayBuffer/Buffer Reference if the ArrayBuffer/Buffer is not dropped on the main thread |
602 | extern "C" fn custom_gc( |
603 | env: sys::napi_env, |
604 | _js_callback: sys::napi_value, |
605 | _context: *mut std::ffi::c_void, |
606 | data: *mut std::ffi::c_void, |
607 | ) { |
608 | // current thread was destroyed |
609 | if THREADS_CAN_ACCESS_ENV.borrow_mut(|m: &mut HashMap| m.get(&std::thread::current().id()) == Some(&false)) |
610 | || data.is_null() |
611 | { |
612 | return; |
613 | } |
614 | let mut ref_count: u32 = 0; |
615 | check_status_or_throw!( |
616 | env, |
617 | unsafe { sys::napi_reference_unref(env, data.cast(), &mut ref_count) }, |
618 | "Failed to unref Buffer reference in Custom GC" |
619 | ); |
620 | debug_assert!( |
621 | ref_count == 0, |
622 | "Buffer reference count in Custom GC is not 0" |
623 | ); |
624 | check_status_or_throw!( |
625 | env, |
626 | unsafe { sys::napi_delete_reference(env, data.cast()) }, |
627 | "Failed to delete Buffer reference in Custom GC" |
628 | ); |
629 | } |
630 | |