nodex_api/context.rs
1use crate::{api, prelude::*};
2use std::{marker::PhantomData, mem::MaybeUninit};
3
4#[derive(Clone, Debug)]
5pub struct NapiAsyncContext {
6 env: NapiEnv,
7 context: napi_async_context,
8}
9
10impl NapiAsyncContext {
11 pub(crate) fn from_raw(env: NapiEnv, context: napi_async_context) -> NapiAsyncContext {
12 NapiAsyncContext { env, context }
13 }
14
15 pub fn env(&self) -> NapiEnv {
16 self.env
17 }
18
19 pub fn raw(&self) -> napi_async_context {
20 self.context
21 }
22
23 /// The async_resource object needs to be kept alive until napi_async_destroy to keep async_hooks related API acts correctly. In order to retain ABI compatibility with previous versions, napi_async_contexts are not maintaining the strong reference to the async_resource objects to avoid introducing causing memory leaks. However, if the async_resource is garbage collected by JavaScript engine before the napi_async_context was destroyed by napi_async_destroy, calling napi_async_context related APIs like napi_open_callback_scope and napi_make_callback can cause problems like loss of async context when using the AsyncLocalStorage API.
24 /// In order to retain ABI compatibility with previous versions, passing NULL for async_resource does not result in an error. However, this is not recommended as this will result poor results with async_hooks init hooks and async_hooks.executionAsyncResource() as the resource is now required by the underlying async_hooks implementation in order to provide the linkage between async callbacks.
25 pub fn new(env: NapiEnv, name: impl AsRef<str>) -> NapiResult<NapiAsyncContext> {
26 let context = napi_call!(
27 =napi_async_init,
28 env,
29 env.object()?.raw(),
30 env.string(name)?.raw(),
31 );
32
33 Ok(NapiAsyncContext { env, context })
34 }
35
36 /// This API can be called even if there is a pending JavaScript exception.
37 pub fn destroy(&mut self) -> NapiResult<()> {
38 napi_call!(napi_async_destroy, self.env(), self.raw())
39 }
40
41 /// This method allows a JavaScript function object to be called from a native add-on.
42 /// This API is similar to napi_call_function. However, it is used to call from native
43 /// code back into JavaScript after returning from an async operation (when there is no
44 /// other script on the stack). It is a fairly simple wrapper around node::MakeCallback.
45 ///
46 /// Note it is not necessary to use napi_make_callback from within a napi_async_complete_callback;
47 /// in that situation the callback's async context has already been set up, so a direct call to
48 /// napi_call_function is sufficient and appropriate. Use of the napi_make_callback function may
49 /// be required when implementing custom async behavior that does not use napi_create_async_work.
50 ///
51 /// Any process.nextTicks or Promises scheduled on the microtask queue by JavaScript during
52 /// he callback are ran before returning back to C/C++.
53 pub fn make_callback<R, T>(&self, this: JsObject, func: Function<R>, args: T) -> NapiResult<R>
54 where
55 R: NapiValueT,
56 T: ToJsArgs,
57 {
58 let env = self.env();
59 let args = args
60 .to_js_args(env)?
61 .0
62 .into_iter()
63 .map(|value| value.raw())
64 .collect::<Vec<_>>();
65
66 let value = napi_call!(
67 =napi_make_callback,
68 self.env(),
69 self.raw(),
70 this.raw(),
71 func.raw(),
72 T::len(),
73 args.as_ptr(),
74 );
75
76 Ok(R::from_raw(env, value))
77 }
78
79 #[cfg(feature = "v3")]
80 /// There are cases (for example, resolving promises) where it is necessary to have the
81 /// equivalent of the scope associated with a callback in place when making certain
82 /// Node-API calls. If there is no other script on the stack the napi_open_callback_scope
83 /// and napi_close_callback_scope functions can be used to open/close the required scope.
84 pub fn scope(&self) -> NapiResult<NapiCallbackScope> {
85 let env = self.env();
86 let scope = napi_call!(
87 =napi_open_callback_scope,
88 env,
89 env.object()?.raw(),
90 self.raw(),
91 );
92 Ok(NapiCallbackScope::from_raw(env, scope))
93 }
94}
95
96impl Drop for NapiAsyncContext {
97 fn drop(&mut self) {
98 self.destroy();
99 }
100}