@@ -17,6 +17,7 @@ use napi::{Env, Error, Result, Status, sys};
1717use napi_derive:: napi;
1818
1919use crate :: errors:: { ConvertedError , ConvertedResult , JsResult , with_custom_error_sync} ;
20+ use crate :: napi_helpers:: { DeferredPtr , ResolveOrReject } ;
2021
2122/// JsPromise — lightweight wrapper over the promise pointer that indicates the type used to resolve the promise
2223/// The promise can be either resolved with type T or rejected with any error value (`ConvertedError` when used with `submit_future`).
@@ -30,14 +31,14 @@ impl<T> ToNapiValue for JsPromise<T> {
3031 }
3132}
3233
33- type SettleCallback = Box < dyn FnOnce ( Env , sys :: napi_deferred ) + Send > ;
34+ type SettleCallback = Box < dyn FnOnce ( Env , DeferredPtr ) + Send > ;
3435type BridgedFuture = Pin < Box < dyn Future < Output = SettleCallback > + Send > > ;
3536
3637struct FutureEntry {
3738 future : BridgedFuture ,
3839 /// Raw deferred handle — resolved/rejected in `poll_woken` on the
3940 /// main thread where we have a valid `napi_env`.
40- deferred : sys :: napi_deferred ,
41+ deferred : DeferredPtr ,
4142 waker : Waker ,
4243}
4344
@@ -122,12 +123,7 @@ impl FutureRegistry {
122123 }
123124 }
124125
125- fn insert (
126- & mut self ,
127- env : & Env ,
128- future : BridgedFuture ,
129- deferred : sys:: napi_deferred ,
130- ) -> Result < u64 > {
126+ fn insert ( & mut self , env : & Env , future : BridgedFuture , deferred : DeferredPtr ) -> Result < u64 > {
131127 let was_empty = self . futures . is_empty ( ) ;
132128
133129 let id = self . next_id ;
@@ -234,7 +230,7 @@ thread_local! {
234230 static REGISTRY : RefCell <FutureRegistry > = RefCell :: new( FutureRegistry :: new( ) ) ;
235231}
236232
237- fn create_promise ( env : & Env ) -> Result < ( sys :: napi_deferred , sys:: napi_value ) > {
233+ fn create_promise ( env : & Env ) -> Result < ( DeferredPtr , sys:: napi_value ) > {
238234 let mut deferred = ptr:: null_mut ( ) ;
239235 let mut promise = ptr:: null_mut ( ) ;
240236 // SAFETY: `raw_env` is taken from Env, which is guaranteed to be valid for the lifetime of the current napi call.
@@ -245,21 +241,21 @@ fn create_promise(env: &Env) -> Result<(sys::napi_deferred, sys::napi_value)> {
245241 & mut promise
246242 ) ) ?
247243 } ;
248- Ok ( ( deferred, promise) )
244+ // SAFETY: deferred is assigned to valid value in napi_create_promise call, that have just succeeded.
245+ // This promise had no chance to be resolved yet.
246+ let deferred_ptr = unsafe { DeferredPtr :: new ( deferred) } ;
247+ Ok ( ( deferred_ptr, promise) )
249248}
250249
251- /// # Safety
252- /// The deferred must not have been resolved or rejected yet
253- unsafe fn reject_with_reason ( env : Env , deferred : sys:: napi_deferred , reason : & str ) -> Result < ( ) > {
250+ fn reject_with_reason ( env : Env , deferred : DeferredPtr , reason : & str ) -> Result < ( ) > {
254251 // We can unwrap in the second place, because the only case when Cstring::new can fail is when the string contains a null byte.
255252 let c_reason = CString :: new ( reason) . unwrap_or_else ( |_| {
256253 CString :: new ( "[Unknown error] Error message contained illegal null byte" ) . unwrap ( )
257254 } ) ;
258255 let mut msg: sys:: napi_value = std:: ptr:: null_mut ( ) ;
259256 let mut error: sys:: napi_value = std:: ptr:: null_mut ( ) ;
260257
261- // SAFETY: Env guarantees that raw pointer is a valid main-thread env and
262- // caller ensured that `deferred` has not yet been resolved or rejected.
258+ // SAFETY: Env guarantees that raw pointer is a valid main-thread env.
263259 // Remaining arguments are created in this function and are valid for the whole duration.
264260 unsafe {
265261 check_status ! ( sys:: napi_create_string_utf8(
@@ -274,7 +270,7 @@ unsafe fn reject_with_reason(env: Env, deferred: sys::napi_deferred, reason: &st
274270 msg,
275271 & mut error
276272 ) ) ?;
277- check_status ! ( sys :: napi_reject_deferred ( env . raw ( ) , deferred , error ) ) ?;
273+ deferred . resolve ( env , error , ResolveOrReject :: Reject ) ?;
278274 }
279275 Ok ( ( ) )
280276}
@@ -360,33 +356,26 @@ where
360356
361357 let boxed: BridgedFuture = Box :: pin ( async move {
362358 let result = fut. await ;
363- Box :: new ( move |env : Env , deferred| unsafe {
359+ Box :: new ( move |env : Env , deferred : DeferredPtr | unsafe {
364360 // SAFETY: This closure is only ever invoked from `poll_woken`, which runs
365361 // on the Node main thread inside the TSFN callback - the only place where
366362 // `env` is a valid napi_env. `deferred` is consumed exactly once here,
367363 // satisfying the napi contract that each deferred is resolved or rejected
368364 // exactly once. `to_napi_value` receives the same valid `env`.
369365 let ( js_val, resolve) = match result {
370- Ok ( val) => ( T :: to_napi_value ( env. raw ( ) , val) , true ) ,
371- Err ( err) => ( ConvertedError :: to_napi_value ( env. raw ( ) , err) , false ) ,
366+ Ok ( val) => ( T :: to_napi_value ( env. raw ( ) , val) , ResolveOrReject :: Resolve ) ,
367+ Err ( err) => (
368+ ConvertedError :: to_napi_value ( env. raw ( ) , err) ,
369+ ResolveOrReject :: Reject ,
370+ ) ,
371+ } ;
372+
373+ let status = match js_val {
374+ Ok ( v) => deferred. resolve ( env, v, resolve) ,
375+ Err ( e) => reject_with_reason ( env, deferred, & e. reason ) ,
372376 } ;
373- let status = js_val
374- // First we try to accept / reject with converted value / error.
375- . and_then ( |v| {
376- if resolve {
377- check_status ! ( sys:: napi_resolve_deferred( env. raw( ) , deferred, v) )
378- } else {
379- check_status ! ( sys:: napi_reject_deferred( env. raw( ) , deferred, v) )
380- }
381- } )
382- // If this fails, or we failed to convert the value / error into a JS value,
383- // we reject with a fallback reason.
384- . or_else ( |e| reject_with_reason ( env, deferred, & e. reason ) ) ;
385377
386378 if let Err ( e) = status {
387- // If both fail, we assume something terrible has happened. We cannot
388- // inform JS side about the error by regular error handling, so we panic to
389- // avoid silent failures and orphaned promises.
390379 panic ! (
391380 "Failed to settle promise in TSFN callback. This may indicate either a bug in the driver or a severe runtime error.\n Root cause:\n {}" ,
392381 e. reason
0 commit comments