@@ -284,12 +284,37 @@ void node_loader_impl_func_call_js_safe(napi_env env, napi_value js_callback, vo
284284 // async_safe->args->node_impl->env = nullptr;
285285}
286286
287+ template <typename T>
288+ void node_loader_impl_func_call_js_async_safe (napi_env env, napi_value js_callback, void *context, void *data)
289+ {
290+ (void )js_callback;
291+
292+ if (env == nullptr || context == nullptr || data == nullptr )
293+ {
294+ log_write (" metacall" , LOG_LEVEL_ERROR, " Invalid arguments passed to js thread async safe function" );
295+ return ;
296+ }
297+
298+ T *args = static_cast <T *>(data);
299+ node_loader_impl_func_call_js_safe_cast<T> safe_cast (context);
300+
301+ /* Store environment for reentrant calls */
302+ args->node_impl ->env = env;
303+
304+ /* Call to the implementation function */
305+ safe_cast.func_ptr (env, args);
306+
307+ /* Clear environment */
308+ // async_safe->args->node_impl->env = nullptr;
309+ }
310+
287311static napi_value node_loader_impl_async_threadsafe_empty (napi_env, napi_callback_info)
288312{
289313 /* This is a dirty hack in order to make the threadsafe API work properly,
290314 * as soon as possible it will be good to return to the old method we used in NodeJS 8,
291315 * it was better than this API
292316 */
317+
293318 return nullptr ;
294319}
295320
@@ -513,18 +538,6 @@ struct loader_impl_async_future_delete_safe_type
513538 node_impl (node_impl), f(f), node_future(node_future) {}
514539};
515540
516- struct loader_impl_async_handle_promise_safe_type
517- {
518- loader_impl_node node_impl;
519- napi_deferred deferred;
520- void *result;
521- napi_status (*deferred_fn)(napi_env, napi_deferred, napi_value);
522- const char *error_str;
523-
524- loader_impl_async_handle_promise_safe_type (loader_impl_node node_impl, napi_deferred deferred, void *result, napi_status (*deferred_fn)(napi_env, napi_deferred, napi_value), const char error_str[]) :
525- node_impl (node_impl), deferred(deferred), result(result), deferred_fn(deferred_fn), error_str(error_str) {}
526- };
527-
528541struct loader_impl_async_destroy_safe_type
529542{
530543 loader_impl_node node_impl;
@@ -552,7 +565,6 @@ struct loader_impl_node_type
552565 loader_impl_threadsafe_type<loader_impl_async_func_destroy_safe_type> threadsafe_func_destroy;
553566 loader_impl_threadsafe_type<loader_impl_async_future_await_safe_type> threadsafe_future_await;
554567 loader_impl_threadsafe_type<loader_impl_async_future_delete_safe_type> threadsafe_future_delete;
555- loader_impl_threadsafe_type<loader_impl_async_handle_promise_safe_type> threadsafe_handle_promise;
556568 loader_impl_threadsafe_type<loader_impl_async_destroy_safe_type> threadsafe_destroy;
557569
558570 uv_thread_t thread;
@@ -584,6 +596,73 @@ struct loader_impl_node_type
584596 loader_impl impl;
585597};
586598
599+ template <typename T>
600+ struct loader_impl_threadsafe_async_type
601+ {
602+ uv_async_t async_handle;
603+ bool initialized;
604+
605+ int initialize (loader_impl_node node_impl, void (*async_cb)(uv_async_t *))
606+ {
607+ int result = uv_async_init (node_impl->thread_loop , &async_handle, async_cb);
608+
609+ initialized = (result == 0 );
610+
611+ return result;
612+ }
613+
614+ void invoke (T *data)
615+ {
616+ if (initialized)
617+ {
618+ async_handle.data = static_cast <T *>(data);
619+ uv_async_send (&async_handle);
620+ }
621+ }
622+
623+ void close (void (*close_cb)(uv_handle_t *handle))
624+ {
625+ if (initialized)
626+ {
627+ union
628+ {
629+ uv_handle_t *handle;
630+ uv_async_t *async;
631+ } handle_cast;
632+
633+ handle_cast.async = &async_handle;
634+
635+ uv_close (handle_cast.handle , close_cb);
636+ }
637+ }
638+ };
639+
640+ struct loader_impl_async_handle_promise_safe_type
641+ {
642+ loader_impl_node node_impl;
643+ napi_env env;
644+ napi_deferred deferred;
645+ void *result;
646+ napi_status (*deferred_fn)(napi_env, napi_deferred, napi_value);
647+ const char *error_str;
648+ loader_impl_threadsafe_async_type<loader_impl_async_handle_promise_safe_type> threadsafe_async;
649+
650+ loader_impl_async_handle_promise_safe_type (loader_impl_node node_impl, napi_env env) :
651+ node_impl (node_impl), env(env), result(NULL ) {}
652+
653+ ~loader_impl_async_handle_promise_safe_type ()
654+ {
655+ threadsafe_async.close ([](uv_handle_t *handle) {
656+ loader_impl_async_handle_promise_safe_type *handle_promise_safe = static_cast <loader_impl_async_handle_promise_safe_type *>(handle->data );
657+
658+ if (handle_promise_safe->result != NULL )
659+ {
660+ metacall_value_destroy (handle_promise_safe->result );
661+ }
662+ });
663+ }
664+ };
665+
587666typedef napi_value (*function_resolve_trampoline)(loader_impl_node, napi_env, function_resolve_callback, napi_value, napi_value, void *);
588667typedef napi_value (*function_reject_trampoline)(loader_impl_node, napi_env, function_reject_callback, napi_value, napi_value, void *);
589668
@@ -3305,6 +3384,9 @@ void node_loader_impl_handle_promise_safe(napi_env env, loader_impl_async_handle
33053384 napi_throw_error (env, nullptr , handle_promise_safe->error_str );
33063385 }
33073386
3387+ /* Close the handle */
3388+ delete handle_promise_safe;
3389+
33083390 /* Close scope */
33093391 status = napi_close_handle_scope (env, handle_scope);
33103392
@@ -3401,7 +3483,6 @@ void *node_loader_impl_register(void *node_impl_ptr, void *env_ptr, void *functi
34013483 node_impl->threadsafe_func_destroy .initialize (env, " node_loader_impl_async_func_destroy_safe" , &node_loader_impl_func_destroy_safe);
34023484 node_impl->threadsafe_future_await .initialize (env, " node_loader_impl_async_future_await_safe" , &node_loader_impl_future_await_safe);
34033485 node_impl->threadsafe_future_delete .initialize (env, " node_loader_impl_async_future_delete_safe" , &node_loader_impl_future_delete_safe);
3404- node_impl->threadsafe_handle_promise .initialize (env, " node_loader_impl_async_handle_promise_safe" , &node_loader_impl_handle_promise_safe);
34053486 node_impl->threadsafe_destroy .initialize (env, " node_loader_impl_async_destroy_safe" , &node_loader_impl_destroy_safe);
34063487 }
34073488
@@ -4071,23 +4152,95 @@ int node_loader_impl_discover(loader_impl impl, loader_handle handle, context ct
40714152 return discover_safe.result ;
40724153}
40734154
4074- void node_loader_impl_handle_promise (loader_impl_node node_impl, napi_env env, napi_deferred deferred , void *result, napi_status (*deferred_fn)(napi_env, napi_deferred, napi_value), const char error_str[])
4155+ void node_loader_impl_handle_promise (loader_impl_async_handle_promise_safe_type *handle_promise_safe , void *result, napi_status (*deferred_fn)(napi_env, napi_deferred, napi_value), const char error_str[])
40754156{
4076- loader_impl_async_handle_promise_safe_type handle_promise_safe (node_impl, deferred, result, deferred_fn, error_str);
4157+ handle_promise_safe->result = metacall_value_copy (result);
4158+ handle_promise_safe->deferred_fn = deferred_fn;
4159+ handle_promise_safe->error_str = error_str;
40774160
40784161 /* Check if we are in the JavaScript thread */
4079- if (node_impl->js_thread_id == std::this_thread::get_id ())
4162+ if (handle_promise_safe-> node_impl ->js_thread_id == std::this_thread::get_id ())
40804163 {
40814164 /* We are already in the V8 thread, we can call safely */
4082- node_loader_impl_handle_promise_safe (env, & handle_promise_safe);
4165+ node_loader_impl_handle_promise_safe (handle_promise_safe-> env , handle_promise_safe);
40834166 }
40844167 else
40854168 {
40864169 /* Submit the task to the async queue */
4087- loader_impl_threadsafe_invoke_type<loader_impl_async_handle_promise_safe_type> invoke (node_impl->threadsafe_handle_promise , handle_promise_safe);
4170+ if (handle_promise_safe->threadsafe_async .initialize (handle_promise_safe->node_impl , [](uv_async_t *handle) {
4171+ loader_impl_async_handle_promise_safe_type *handle_promise_safe = static_cast <loader_impl_async_handle_promise_safe_type *>(handle->data );
4172+ node_loader_impl_handle_promise_safe (handle_promise_safe->env , handle_promise_safe);
4173+ }) != 0 )
4174+ {
4175+ log_write (" metacall" , LOG_LEVEL_ERROR, " Filed to initialize promise safe async handle" );
4176+ }
4177+ else
4178+ {
4179+ handle_promise_safe->threadsafe_async .invoke (handle_promise_safe);
4180+ }
40884181 }
40894182}
40904183
4184+ napi_value node_loader_impl_promise_await (loader_impl_node node_impl, napi_env env, const char *name, value *args, size_t size)
4185+ {
4186+ loader_impl_async_handle_promise_safe_type *handle_promise_safe = new loader_impl_async_handle_promise_safe_type (node_impl, env);
4187+
4188+ if (handle_promise_safe == nullptr )
4189+ {
4190+ napi_throw_error (env, nullptr , " Failed to allocate the promise context" );
4191+
4192+ return nullptr ;
4193+ }
4194+
4195+ napi_value promise;
4196+
4197+ /* Create the promise */
4198+ napi_status status = napi_create_promise (env, &handle_promise_safe->deferred , &promise);
4199+
4200+ if (status != napi_ok)
4201+ {
4202+ napi_throw_error (env, nullptr , " Failed to create the promise" );
4203+
4204+ delete handle_promise_safe;
4205+
4206+ return nullptr ;
4207+ }
4208+
4209+ auto resolve = [](void *result, void *data) -> void * {
4210+ static const char promise_error_str[] = " Failed to resolve the promise" ;
4211+
4212+ loader_impl_async_handle_promise_safe_type *handle_promise_safe = static_cast <loader_impl_async_handle_promise_safe_type *>(data);
4213+
4214+ node_loader_impl_handle_promise (handle_promise_safe, result, &napi_resolve_deferred, promise_error_str);
4215+
4216+ return NULL ;
4217+ };
4218+
4219+ auto reject = [](void *result, void *data) -> void * {
4220+ static const char promise_error_str[] = " Failed to reject the promise" ;
4221+
4222+ loader_impl_async_handle_promise_safe_type *handle_promise_safe = static_cast <loader_impl_async_handle_promise_safe_type *>(data);
4223+
4224+ node_loader_impl_handle_promise (handle_promise_safe, result, &napi_reject_deferred, promise_error_str);
4225+
4226+ return NULL ;
4227+ };
4228+
4229+ /* Await to the function */
4230+ void *ret = metacall_await_s (name, args, size, resolve, reject, handle_promise_safe);
4231+
4232+ if (metacall_value_id (ret) == METACALL_THROWABLE)
4233+ {
4234+ napi_value result = node_loader_impl_value_to_napi (node_impl, env, ret);
4235+
4236+ napi_throw (env, result);
4237+ }
4238+
4239+ node_loader_impl_finalizer (env, promise, ret);
4240+
4241+ return promise;
4242+ }
4243+
40914244#define container_of (ptr, type, member ) \
40924245 (type *)((char *)(ptr) - (char *)&((type *)0 )->member)
40934246
@@ -4311,10 +4464,16 @@ void node_loader_impl_destroy_safe_impl(loader_impl_node node_impl, napi_env env
43114464{
43124465 uint32_t ref_count = 0 ;
43134466 napi_status status;
4467+ napi_handle_scope handle_scope;
43144468
43154469 /* Destroy children loaders */
43164470 loader_unload_children (node_impl->impl );
43174471
4472+ /* Create scope */
4473+ status = napi_open_handle_scope (env, &handle_scope);
4474+
4475+ node_loader_impl_exception (env, status);
4476+
43184477 /* Clear thread safe functions except by destroy one */
43194478 {
43204479 node_impl->threadsafe_initialize .abort (env);
@@ -4328,7 +4487,6 @@ void node_loader_impl_destroy_safe_impl(loader_impl_node node_impl, napi_env env
43284487 node_impl->threadsafe_func_destroy .abort (env);
43294488 node_impl->threadsafe_future_await .abort (env);
43304489 node_impl->threadsafe_future_delete .abort (env);
4331- node_impl->threadsafe_handle_promise .abort (env);
43324490 }
43334491
43344492 /* Clear persistent references */
@@ -4358,48 +4516,10 @@ void node_loader_impl_destroy_safe_impl(loader_impl_node node_impl, napi_env env
43584516
43594517 node_loader_impl_exception (env, status);
43604518
4361- /* Clear event loop */
4362- {
4363- /* Stop event loop */
4364- uv_stop (node_impl->thread_loop );
4365-
4366- /* Clear event loop */
4367- /* uv_walk(node_impl->thread_loop, node_loader_impl_walk, NULL); */
4368-
4369- #if 0
4370- /* TODO: For some reason, this deadlocks in NodeJS benchmark when mixing sync and async calls.
4371- * It should be reviewed carefully and detect if NodeJS is finalizing properly on multiple cases.
4372- * Disable it for now in order to make tests pass.
4373- */
4374- while (uv_run(node_impl->thread_loop, UV_RUN_DEFAULT) != 0)
4375- #if (!defined(NDEBUG) || defined(DEBUG) || defined(_DEBUG) || defined(__DEBUG) || defined(__DEBUG__))
4376- {
4377- node_loader_impl_print_handles(node_impl);
4378- }
4379- #else
4380- ;
4381- #endif
4382-
4383- /* Destroy node loop */
4384- if (uv_loop_alive(node_impl->thread_loop) != 0)
4385- {
4386- /* TODO: Make logs thread safe */
4387- /* log_write("metacall", LOG_LEVEL_ERROR, "NodeJS event loop should not be alive"); */
4388- printf("NodeJS Loader Error: NodeJS event loop should not be alive\n");
4389- fflush(stdout);
4390- }
4391- #endif
4519+ /* Close scope */
4520+ status = napi_close_handle_scope (env, handle_scope);
43924521
4393- /* This evaluates to true always due to stdin and stdout handles,
4394- which are closed anyway on thread join. So it is removed by now. */
4395- if (uv_loop_close (node_impl->thread_loop ) != UV_EBUSY)
4396- {
4397- /* TODO: Make logs thread safe */
4398- /* log_write("metacall", LOG_LEVEL_ERROR, "NodeJS event loop should not be busy"); */
4399- printf (" NodeJS Loader Error: NodeJS event loop should be busy\n " );
4400- fflush (stdout);
4401- }
4402- }
4522+ node_loader_impl_exception (env, status);
44034523
44044524 /* NodeJS Loader needs to register that it is destroyed, because after this step
44054525 * some destructors can be still triggered, before the node_loader->destroy() has
0 commit comments