|  | 
| 18 | 18 | #include "node_api.h" | 
| 19 | 19 | #include "node_internals.h" | 
| 20 | 20 | 
 | 
| 21 |  | -#define NAPI_VERSION  2 | 
|  | 21 | +#define NAPI_VERSION  3 | 
| 22 | 22 | 
 | 
| 23 | 23 | static | 
| 24 | 24 | napi_status napi_set_last_error(napi_env env, napi_status error_code, | 
| @@ -3514,3 +3514,189 @@ napi_status napi_run_script(napi_env env, | 
| 3514 | 3514 |   *result = v8impl::JsValueFromV8LocalValue(script_result.ToLocalChecked()); | 
| 3515 | 3515 |   return GET_RETURN_STATUS(env); | 
| 3516 | 3516 | } | 
|  | 3517 | + | 
|  | 3518 | +struct napi_threadsafe_function__ { | 
|  | 3519 | +  uv_async_t async; | 
|  | 3520 | +  napi_ref ref; | 
|  | 3521 | +  napi_env env; | 
|  | 3522 | +  size_t argc; | 
|  | 3523 | +  void* data; | 
|  | 3524 | +  void* context; | 
|  | 3525 | +  napi_threadsafe_function_marshal marshal_cb; | 
|  | 3526 | +  napi_threadsafe_function_process_result process_result_cb; | 
|  | 3527 | +}; | 
|  | 3528 | + | 
|  | 3529 | +static napi_value napi_threadsafe_function_error(napi_env env, | 
|  | 3530 | +                                                 const char* message) { | 
|  | 3531 | +  napi_value result, js_message; | 
|  | 3532 | +  if (napi_create_string_utf8(env, message, NAPI_AUTO_LENGTH, &js_message) == | 
|  | 3533 | +      napi_ok) { | 
|  | 3534 | +    if (napi_create_error(env, nullptr, js_message, &result) == napi_ok) { | 
|  | 3535 | +      return result; | 
|  | 3536 | +    } | 
|  | 3537 | +  } | 
|  | 3538 | + | 
|  | 3539 | +  napi_fatal_error("N-API thread-safe function", NAPI_AUTO_LENGTH, | 
|  | 3540 | +      (std::string("Failed to create JS error: ") + | 
|  | 3541 | +      std::string(message)).c_str(), NAPI_AUTO_LENGTH); | 
|  | 3542 | +  return nullptr; | 
|  | 3543 | +} | 
|  | 3544 | + | 
|  | 3545 | +static void napi_threadsafe_function_cb(uv_async_t* uv_async) { | 
|  | 3546 | +  napi_threadsafe_function async = | 
|  | 3547 | +      node::ContainerOf(&napi_threadsafe_function__::async, uv_async); | 
|  | 3548 | +  v8::HandleScope handle_scope(async->env->isolate); | 
|  | 3549 | + | 
|  | 3550 | +  napi_value js_cb; | 
|  | 3551 | +  napi_value recv; | 
|  | 3552 | +  napi_value js_result = nullptr; | 
|  | 3553 | +  napi_value exception = nullptr; | 
|  | 3554 | +  std::vector<napi_value> argv(async->argc); | 
|  | 3555 | + | 
|  | 3556 | +  napi_status status = napi_get_reference_value(async->env, async->ref, &js_cb); | 
|  | 3557 | +  if (status != napi_ok) { | 
|  | 3558 | +    exception = napi_threadsafe_function_error(async->env, | 
|  | 3559 | +        "Failed to retrieve JS callback"); | 
|  | 3560 | +    goto done; | 
|  | 3561 | +  } | 
|  | 3562 | + | 
|  | 3563 | +  status = async->marshal_cb(async->env, async->data, &recv, async->argc, | 
|  | 3564 | +      argv.data()); | 
|  | 3565 | +  if (status != napi_ok) { | 
|  | 3566 | +    exception = napi_threadsafe_function_error(async->env, | 
|  | 3567 | +        "Failed to marshal JS callback arguments"); | 
|  | 3568 | +    goto done; | 
|  | 3569 | +  } | 
|  | 3570 | + | 
|  | 3571 | +  status = napi_make_callback(async->env, nullptr, recv, js_cb, async->argc, | 
|  | 3572 | +      argv.data(), &js_result); | 
|  | 3573 | +  if (status != napi_ok) { | 
|  | 3574 | +    if (status == napi_pending_exception) { | 
|  | 3575 | +      status = napi_get_and_clear_last_exception(async->env, &exception); | 
|  | 3576 | +      if (status != napi_ok) { | 
|  | 3577 | +        exception = napi_threadsafe_function_error(async->env, | 
|  | 3578 | +            "Failed to retrieve JS callback exception"); | 
|  | 3579 | +        goto done; | 
|  | 3580 | +      } | 
|  | 3581 | +    } else { | 
|  | 3582 | +      exception = napi_threadsafe_function_error(async->env, | 
|  | 3583 | +          "Failed to call JS callback"); | 
|  | 3584 | +      goto done; | 
|  | 3585 | +    } | 
|  | 3586 | +  } | 
|  | 3587 | + | 
|  | 3588 | +done: | 
|  | 3589 | +  async->process_result_cb(async->env, async->data, exception, js_result); | 
|  | 3590 | +} | 
|  | 3591 | + | 
|  | 3592 | +static napi_status napi_threadsafe_function_default_marshal(napi_env env, | 
|  | 3593 | +                                                            void* data, | 
|  | 3594 | +                                                            napi_value* recv, | 
|  | 3595 | +                                                            size_t argc, | 
|  | 3596 | +                                                            napi_value* argv) { | 
|  | 3597 | +  napi_status status; | 
|  | 3598 | +  for (size_t index = 0; index < argc; index++) { | 
|  | 3599 | +    status = napi_get_undefined(env, &argv[index]); | 
|  | 3600 | +    if (status != napi_ok) { | 
|  | 3601 | +      return status; | 
|  | 3602 | +    } | 
|  | 3603 | +  } | 
|  | 3604 | +  return napi_get_global(env, recv); | 
|  | 3605 | +} | 
|  | 3606 | + | 
|  | 3607 | +static void napi_threadsafe_function_default_process_result(napi_env env, | 
|  | 3608 | +                                                            void* data, | 
|  | 3609 | +                                                            napi_value error, | 
|  | 3610 | +                                                            napi_value result) { | 
|  | 3611 | +  if (error != nullptr) { | 
|  | 3612 | +    napi_throw(env, error); | 
|  | 3613 | +  } | 
|  | 3614 | +} | 
|  | 3615 | + | 
|  | 3616 | +NAPI_EXTERN napi_status | 
|  | 3617 | +napi_create_threadsafe_function(napi_env env, | 
|  | 3618 | +                                napi_value func, | 
|  | 3619 | +                                void* data, | 
|  | 3620 | +                                size_t argc, | 
|  | 3621 | +                                napi_threadsafe_function_marshal marshal_cb, | 
|  | 3622 | +                                napi_threadsafe_function_process_result | 
|  | 3623 | +                                    process_result_cb, | 
|  | 3624 | +                                napi_threadsafe_function* result) { | 
|  | 3625 | +  CHECK_ENV(env); | 
|  | 3626 | +  CHECK_ARG(env, func); | 
|  | 3627 | +  CHECK_ARG(env, result); | 
|  | 3628 | + | 
|  | 3629 | +  napi_valuetype func_type; | 
|  | 3630 | +  napi_status status = napi_typeof(env, func, &func_type); | 
|  | 3631 | +  if (status != napi_ok) { | 
|  | 3632 | +    return status; | 
|  | 3633 | +  } | 
|  | 3634 | + | 
|  | 3635 | +  if (func_type != napi_function) { | 
|  | 3636 | +    return napi_set_last_error(env, napi_function_expected); | 
|  | 3637 | +  } | 
|  | 3638 | + | 
|  | 3639 | +  napi_threadsafe_function async = new napi_threadsafe_function__; | 
|  | 3640 | +  if (async == nullptr) { | 
|  | 3641 | +    return napi_set_last_error(env, napi_generic_failure); | 
|  | 3642 | +  } | 
|  | 3643 | + | 
|  | 3644 | +  status = napi_create_reference(env, func, 1, &async->ref); | 
|  | 3645 | +  if (status != napi_ok) { | 
|  | 3646 | +    delete async; | 
|  | 3647 | +    return status; | 
|  | 3648 | +  } | 
|  | 3649 | + | 
|  | 3650 | +  if (uv_async_init(uv_default_loop(), &async->async, | 
|  | 3651 | +      napi_threadsafe_function_cb) != 0) { | 
|  | 3652 | +    napi_delete_reference(env, async->ref); | 
|  | 3653 | +    delete async; | 
|  | 3654 | +    return napi_set_last_error(env, napi_generic_failure); | 
|  | 3655 | +  } | 
|  | 3656 | + | 
|  | 3657 | +  async->argc = argc; | 
|  | 3658 | +  async->marshal_cb = marshal_cb == nullptr ? | 
|  | 3659 | +      napi_threadsafe_function_default_marshal : marshal_cb; | 
|  | 3660 | +  async->process_result_cb = | 
|  | 3661 | +      process_result_cb == nullptr ? | 
|  | 3662 | +          napi_threadsafe_function_default_process_result : process_result_cb; | 
|  | 3663 | +  async->data = data; | 
|  | 3664 | +  async->env = env; | 
|  | 3665 | + | 
|  | 3666 | +  *result = async; | 
|  | 3667 | +  return napi_clear_last_error(env); | 
|  | 3668 | +} | 
|  | 3669 | + | 
|  | 3670 | +NAPI_EXTERN napi_status | 
|  | 3671 | +napi_get_threadsafe_function_data(napi_threadsafe_function async, | 
|  | 3672 | +                                  void** data) { | 
|  | 3673 | +  if (data != nullptr) { | 
|  | 3674 | +    *data = async->data; | 
|  | 3675 | +  } | 
|  | 3676 | +  return napi_ok; | 
|  | 3677 | +} | 
|  | 3678 | + | 
|  | 3679 | +NAPI_EXTERN napi_status | 
|  | 3680 | +napi_call_threadsafe_function(napi_threadsafe_function async) { | 
|  | 3681 | +  return uv_async_send(&async->async) == 0 ? | 
|  | 3682 | +      napi_ok : napi_generic_failure; | 
|  | 3683 | +} | 
|  | 3684 | + | 
|  | 3685 | +NAPI_EXTERN napi_status | 
|  | 3686 | +napi_delete_threadsafe_function(napi_env env, | 
|  | 3687 | +                                napi_threadsafe_function async) { | 
|  | 3688 | +  CHECK_ENV(env); | 
|  | 3689 | +  CHECK_ARG(env, async); | 
|  | 3690 | + | 
|  | 3691 | +  napi_status status = napi_delete_reference(env, async->ref); | 
|  | 3692 | +  if (status != napi_ok) { | 
|  | 3693 | +    return status; | 
|  | 3694 | +  } | 
|  | 3695 | + | 
|  | 3696 | +  uv_close(reinterpret_cast<uv_handle_t*>(&async->async), | 
|  | 3697 | +      [] (uv_handle_t* handle) -> void { | 
|  | 3698 | +        delete handle; | 
|  | 3699 | +      }); | 
|  | 3700 | + | 
|  | 3701 | +  return napi_clear_last_error(env); | 
|  | 3702 | +} | 
0 commit comments