1use std::marker::PhantomData;
2use std::os::raw::c_void;
3use std::ptr;
4
5#[cfg(feature = "deferred_trace")]
6use crate::{bindgen_runtime::JsObjectValue, JsValue};
7use crate::{
8 bindgen_runtime::{Object, ToNapiValue},
9 check_status, sys, Env, Error, Result,
10};
11
12#[cfg(feature = "deferred_trace")]
13#[repr(transparent)]
21#[derive(Clone)]
22struct DeferredTrace(sys::napi_ref);
23
24#[cfg(feature = "deferred_trace")]
25impl DeferredTrace {
26 fn new(raw_env: sys::napi_env) -> Result<Self> {
27 let env = Env::from_raw(raw_env);
28 let reason = env.create_string("none")?;
29
30 let mut js_error = ptr::null_mut();
31 check_status!(
32 unsafe { sys::napi_create_error(raw_env, ptr::null_mut(), reason.raw(), &mut js_error) },
33 "Create error in DeferredTrace failed"
34 )?;
35
36 let mut result = ptr::null_mut();
37 check_status!(
38 unsafe { sys::napi_create_reference(raw_env, js_error, 1, &mut result) },
39 "Create reference in DeferredTrace failed"
40 )?;
41
42 Ok(Self(result))
43 }
44
45 fn into_rejected(self, raw_env: sys::napi_env, mut err: Error) -> Result<sys::napi_value> {
46 let env = Env::from_raw(raw_env);
47 let mut raw = ptr::null_mut();
48 check_status!(
49 unsafe { sys::napi_get_reference_value(raw_env, self.0, &mut raw) },
50 "Failed to get referenced value in DeferredTrace"
51 )?;
52
53 let mut obj = Object::from_raw(raw_env, raw);
54 let err_value = if !err.maybe_raw.is_null() {
55 let mut err_raw_value = std::ptr::null_mut();
56 check_status!(
57 unsafe { sys::napi_get_reference_value(raw_env, err.maybe_raw, &mut err_raw_value) },
58 "Get error reference in `to_napi_value` failed"
59 )?;
60 let err_obj = Object::from_raw(raw_env, err_raw_value);
61
62 let err_value = if err_obj.has_named_property("message")? {
63 Ok(err_obj.raw())
65 } else {
66 obj.set_named_property("message", "")?;
67 obj.set_named_property("code", "")?;
68 Ok(raw)
69 };
70 let mut ref_count = 0;
71 check_status!(
72 unsafe { sys::napi_reference_unref(raw_env, err.maybe_raw, &mut ref_count) },
73 "Unref error reference in `to_napi_value` failed"
74 )?;
75 if ref_count == 0 {
76 check_status!(
77 unsafe { sys::napi_delete_reference(raw_env, err.maybe_raw) },
78 "Delete error reference in `to_napi_value` failed"
79 )?;
80 }
81 err.maybe_env = ptr::null_mut();
83 err.maybe_raw = ptr::null_mut();
84 err_value
85 } else {
86 obj.set_named_property("message", &err.reason)?;
87 obj.set_named_property(
88 "code",
89 env.create_string_from_std(format!("{}", err.status))?,
90 )?;
91 Ok(raw)
92 };
93 check_status!(
94 unsafe { sys::napi_delete_reference(raw_env, self.0) },
95 "Failed to get referenced value in DeferredTrace"
96 )?;
97 err_value
98 }
99}
100
101struct DeferredData<Data: ToNapiValue, Resolver: FnOnce(Env) -> Result<Data>> {
102 resolver: Result<Resolver>,
103 #[cfg(feature = "deferred_trace")]
104 trace: DeferredTrace,
105 tsfn: sys::napi_threadsafe_function,
106}
107
108pub struct JsDeferred<Data: ToNapiValue, Resolver: FnOnce(Env) -> Result<Data>> {
109 pub(crate) tsfn: sys::napi_threadsafe_function,
110 #[cfg(feature = "deferred_trace")]
111 trace: DeferredTrace,
112 _data: PhantomData<Data>,
113 _resolver: PhantomData<Resolver>,
114}
115
116impl<Data: ToNapiValue, Resolver: FnOnce(Env) -> Result<Data>> Clone
119 for JsDeferred<Data, Resolver>
120{
121 fn clone(&self) -> Self {
122 Self {
123 tsfn: self.tsfn,
124 #[cfg(feature = "deferred_trace")]
125 trace: self.trace.clone(),
126 _data: PhantomData,
127 _resolver: PhantomData,
128 }
129 }
130}
131
132unsafe impl<Data: ToNapiValue, Resolver: FnOnce(Env) -> Result<Data>> Send
133 for JsDeferred<Data, Resolver>
134{
135}
136
137impl<Data: ToNapiValue, Resolver: FnOnce(Env) -> Result<Data>> JsDeferred<Data, Resolver> {
138 pub(crate) fn new(env: &Env) -> Result<(Self, Object<'_>)> {
139 let (tsfn, promise) = js_deferred_new_raw(env, Some(napi_resolve_deferred::<Data, Resolver>))?;
140
141 let deferred = Self {
142 tsfn,
143 #[cfg(feature = "deferred_trace")]
144 trace: DeferredTrace::new(env.0)?,
145 _data: PhantomData,
146 _resolver: PhantomData,
147 };
148
149 Ok((deferred, promise))
150 }
151
152 pub fn resolve(self, resolver: Resolver) {
155 self.call_tsfn(Ok(resolver))
156 }
157
158 pub fn reject(self, error: Error) {
160 self.call_tsfn(Err(error))
161 }
162
163 fn call_tsfn(self, result: Result<Resolver>) {
164 let data = DeferredData {
165 resolver: result,
166 #[cfg(feature = "deferred_trace")]
167 trace: self.trace,
168 tsfn: self.tsfn,
169 };
170
171 let status = unsafe {
173 sys::napi_call_threadsafe_function(
174 self.tsfn,
175 Box::into_raw(Box::from(data)).cast(),
176 sys::ThreadsafeFunctionCallMode::blocking,
177 )
178 };
179 debug_assert!(
180 status == sys::Status::napi_ok,
181 "Call threadsafe function in JsDeferred failed"
182 );
183 }
184}
185
186fn js_deferred_new_raw(
187 env: &Env,
188 resolve_deferred: sys::napi_threadsafe_function_call_js,
189) -> Result<(sys::napi_threadsafe_function, Object<'_>)> {
190 let mut raw_promise = ptr::null_mut();
191 let mut raw_deferred = ptr::null_mut();
192 check_status!(
193 unsafe { sys::napi_create_promise(env.0, &mut raw_deferred, &mut raw_promise) },
194 "Create promise in JsDeferred failed"
195 )?;
196
197 let mut async_resource_name = ptr::null_mut();
199 check_status!(
200 unsafe {
201 sys::napi_create_string_utf8(
202 env.0,
203 c"napi_resolve_deferred".as_ptr().cast(),
204 22,
205 &mut async_resource_name,
206 )
207 },
208 "Create async resource name in JsDeferred failed"
209 )?;
210
211 let mut tsfn = ptr::null_mut();
212 check_status!(
213 unsafe {
214 sys::napi_create_threadsafe_function(
215 env.0,
216 ptr::null_mut(),
217 ptr::null_mut(),
218 async_resource_name,
219 0,
220 1,
221 ptr::null_mut(),
222 None,
223 raw_deferred.cast(),
224 resolve_deferred,
225 &mut tsfn,
226 )
227 },
228 "Create threadsafe function in JsDeferred failed"
229 )?;
230
231 let promise = Object::from_raw(env.0, raw_promise);
232
233 Ok((tsfn, promise))
234}
235
236extern "C" fn napi_resolve_deferred<Data: ToNapiValue, Resolver: FnOnce(Env) -> Result<Data>>(
237 env: sys::napi_env,
238 _js_callback: sys::napi_value,
239 context: *mut c_void,
240 data: *mut c_void,
241) {
242 let deferred = context.cast();
243 let deferred_data: Box<DeferredData<Data, Resolver>> = unsafe { Box::from_raw(data.cast()) };
244 let tsfn = deferred_data.tsfn;
245 let result = deferred_data
246 .resolver
247 .and_then(|resolver| resolver(Env::from_raw(env)))
248 .and_then(|res| unsafe { ToNapiValue::to_napi_value(env, res) });
249
250 let release_tsfn_result = check_status!(
251 unsafe {
252 sys::napi_release_threadsafe_function(tsfn, sys::ThreadsafeFunctionReleaseMode::release)
253 },
254 "Release threadsafe function in JsDeferred failed"
255 );
256
257 if let Err(e) = release_tsfn_result.and(result).and_then(|res| {
258 check_status!(
259 unsafe { sys::napi_resolve_deferred(env, deferred, res) },
260 "Resolve deferred value failed"
261 )
262 .map(|_| {
263 #[cfg(feature = "deferred_trace")]
264 {
265 let _status = unsafe { sys::napi_delete_reference(env, deferred_data.trace.0) };
266 if _status != sys::Status::napi_ok && cfg!(debug_assertions) {
267 eprintln!(
268 "Failed to delete reference in deferred {}",
269 crate::Status::from(_status)
270 );
271 }
272 }
273 })
274 }) {
275 #[cfg(feature = "deferred_trace")]
276 let error = deferred_data.trace.into_rejected(env, e);
277 #[cfg(not(feature = "deferred_trace"))]
278 let error = Ok::<sys::napi_value, Error>(unsafe { crate::JsError::from(e).into_value(env) });
279
280 match error {
281 Ok(error) => {
282 unsafe { sys::napi_reject_deferred(env, deferred, error) };
283 }
284 Err(err) => {
285 if cfg!(debug_assertions) {
286 eprintln!("Failed to reject deferred: {err:?}");
287 let mut err = ptr::null_mut();
288 let mut err_msg = ptr::null_mut();
289 unsafe {
290 sys::napi_create_string_utf8(env, c"Rejection failed".as_ptr().cast(), 0, &mut err_msg);
291 sys::napi_create_error(env, ptr::null_mut(), err_msg, &mut err);
292 sys::napi_reject_deferred(env, deferred, err);
293 }
294 }
295 }
296 }
297 }
298}