|
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