rspack_napi/js_values/
threadsafe_one_shot_value_ref.rs

1#![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  // env can be null when shutting down
27  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}