| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 7691b67 commit d16e2fa
10 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5189,19 +5189,31 @@ napi_status napi_async_init(napi_env env, | |||
| 5189 | 5189 | ||
| 5190 | 5190 | * `[in] env`: The environment that the API is invoked under. | |
| 5191 | 5191 | * `[in] async_resource`: Object associated with the async work | |
| 5192 | - that will be passed to possible `async_hooks` [`init` hooks][]. | ||
| 5193 | - In order to retain ABI compatibility with previous versions, | ||
| 5194 | - passing `NULL` for `async_resource` does not result in an error. However, | ||
| 5195 | - this results in incorrect operation of async hooks for the | ||
| 5196 | - napi_async_context created. Potential issues include | ||
| 5197 | - loss of async context when using the AsyncLocalStorage API. | ||
| 5198 | - * `[in] async_resource_name`: Identifier for the kind of resource | ||
| 5199 | - that is being provided for diagnostic information exposed by the | ||
| 5200 | - `async_hooks` API. | ||
| 5192 | + that will be passed to possible `async_hooks` [`init` hooks][] and can be | ||
| 5193 | + accessed by [`async_hooks.executionAsyncResource()`][]. | ||
| 5194 | + * `[in] async_resource_name`: Identifier for the kind of resource that is being | ||
| 5195 | + provided for diagnostic information exposed by the `async_hooks` API. | ||
| 5201 | 5196 | * `[out] result`: The initialized async context. | |
| 5202 | 5197 | ||
| 5203 | 5198 | Returns `napi_ok` if the API succeeded. | |
| 5204 | 5199 | ||
| 5200 | + The `async_resource` object needs to be kept alive until | ||
| 5201 | + [`napi_async_destroy`][] to keep `async_hooks` related API acts correctly. In | ||
| 5202 | + order to retain ABI compatibility with previous versions, `napi_async_context`s | ||
| 5203 | + are not maintaining the strong reference to the `async_resource` objects to | ||
| 5204 | + avoid introducing causing memory leaks. However, if the `async_resource` is | ||
| 5205 | + garbage collected by JavaScript engine before the `napi_async_context` was | ||
| 5206 | + destroyed by `napi_async_destroy`, calling `napi_async_context` related APIs | ||
| 5207 | + like [`napi_open_callback_scope`][] and [`napi_make_callback`][] can cause | ||
| 5208 | + problems like loss of async context when using the `AsyncLocalStoage` API. | ||
| 5209 | + | ||
| 5210 | + In order to retain ABI compatibility with previous versions, passing `NULL` | ||
| 5211 | + for `async_resource` does not result in an error. However, this is not | ||
| 5212 | + recommended as this will result poor results with `async_hooks` | ||
| 5213 | + [`init` hooks][] and `async_hooks.executionAsyncResource()` as the resource is | ||
| 5214 | + now required by the underlying `async_hooks` implementation in order to provide | ||
| 5215 | + the linkage between async callbacks. | ||
| 5216 | + | ||
| 5205 | 5217 | ### napi_async_destroy | |
| 5206 | 5218 | <!-- YAML | |
| 5207 | 5219 | added: v8.6.0 | |
@@ -5288,7 +5300,9 @@ NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env, | |||
| 5288 | 5300 | ||
| 5289 | 5301 | * `[in] env`: The environment that the API is invoked under. | |
| 5290 | 5302 | * `[in] resource_object`: An object associated with the async work | |
| 5291 | - that will be passed to possible `async_hooks` [`init` hooks][]. | ||
| 5303 | + that will be passed to possible `async_hooks` [`init` hooks][]. This | ||
| 5304 | + parameter has been deprecated and is ignored at runtime. Use the | ||
| 5305 | + `async_resource` parameter in [`napi_async_init`][] instead. | ||
| 5292 | 5306 | * `[in] context`: Context for the async operation that is invoking the callback. | |
| 5293 | 5307 | This should be a value previously obtained from [`napi_async_init`][]. | |
| 5294 | 5308 | * `[out] result`: The newly created scope. | |
@@ -5985,13 +5999,15 @@ This API may only be called from the main thread. | |||
| 5985 | 5999 | [`Number.MAX_SAFE_INTEGER`]: https://tc39.github.io/ecma262/#sec-number.max_safe_integer | |
| 5986 | 6000 | [`Number.MIN_SAFE_INTEGER`]: https://tc39.github.io/ecma262/#sec-number.min_safe_integer | |
| 5987 | 6001 | [`Worker`]: worker_threads.md#worker_threads_class_worker | |
| 6002 | + [`async_hooks.executionAsyncResource()`]: async_hooks.md#async_hooks_async_hooks_executionasyncresource | ||
| 5988 | 6003 | [`global`]: globals.md#globals_global | |
| 5989 | 6004 | [`init` hooks]: async_hooks.md#async_hooks_init_asyncid_type_triggerasyncid_resource | |
| 5990 | 6005 | [`napi_add_async_cleanup_hook`]: #n_api_napi_add_async_cleanup_hook | |
| 5991 | 6006 | [`napi_add_env_cleanup_hook`]: #n_api_napi_add_env_cleanup_hook | |
| 5992 | 6007 | [`napi_add_finalizer`]: #n_api_napi_add_finalizer | |
| 5993 | 6008 | [`napi_async_cleanup_hook`]: #n_api_napi_async_cleanup_hook | |
| 5994 | 6009 | [`napi_async_complete_callback`]: #n_api_napi_async_complete_callback | |
| 6010 | + [`napi_async_destroy`]: #n_api_napi_async_destroy | ||
| 5995 | 6011 | [`napi_async_init`]: #n_api_napi_async_init | |
| 5996 | 6012 | [`napi_callback`]: #n_api_napi_callback | |
| 5997 | 6013 | [`napi_cancel_async_work`]: #n_api_napi_cancel_async_work | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,12 +1,15 @@ | |||
| 1 | + #include "async_wrap-inl.h" | ||
| 1 | 2 | #include "env-inl.h" | |
| 2 | 3 | #define NAPI_EXPERIMENTAL | |
| 3 | 4 | #include "js_native_api_v8.h" | |
| 5 | + #include "memory_tracker-inl.h" | ||
| 4 | 6 | #include "node_api.h" | |
| 5 | 7 | #include "node_binding.h" | |
| 6 | 8 | #include "node_buffer.h" | |
| 7 | 9 | #include "node_errors.h" | |
| 8 | 10 | #include "node_internals.h" | |
| 9 | 11 | #include "threadpoolwork-inl.h" | |
| 12 | + #include "tracing/traced_value.h" | ||
| 10 | 13 | #include "util-inl.h" | |
| 11 | 14 | ||
| 12 | 15 | #include <memory> | |
@@ -104,16 +107,6 @@ static inline napi_env NewEnv(v8::Local<v8::Context> context) { | |||
| 104 | 107 | return result; | |
| 105 | 108 | } | |
| 106 | 109 | ||
| 107 | - static inline napi_callback_scope | ||
| 108 | - JsCallbackScopeFromV8CallbackScope(node::CallbackScope* s) { | ||
| 109 | - return reinterpret_cast<napi_callback_scope>(s); | ||
| 110 | - } | ||
| 111 | - | ||
| 112 | - static inline node::CallbackScope* | ||
| 113 | - V8CallbackScopeFromJsCallbackScope(napi_callback_scope s) { | ||
| 114 | - return reinterpret_cast<node::CallbackScope*>(s); | ||
| 115 | - } | ||
| 116 | - | ||
| 117 | 110 | static inline void trigger_fatal_exception( | |
| 118 | 111 | napi_env env, v8::Local<v8::Value> local_err) { | |
| 119 | 112 | v8::Local<v8::Message> local_msg = | |
@@ -435,6 +428,111 @@ class ThreadSafeFunction : public node::AsyncResource { | |||
| 435 | 428 | bool handles_closing; | |
| 436 | 429 | }; | |
| 437 | 430 | ||
| 431 | + /** | ||
| 432 | + * Compared to node::AsyncResource, the resource object in AsyncContext is | ||
| 433 | + * gc-able. AsyncContext holds a weak reference to the resource object. | ||
| 434 | + * AsyncContext::MakeCallback doesn't implicitly set the receiver of the | ||
| 435 | + * callback to the resource object. | ||
| 436 | + */ | ||
| 437 | + class AsyncContext { | ||
| 438 | + public: | ||
| 439 | + AsyncContext(node_napi_env env, | ||
| 440 | + v8::Local<v8::Object> resource_object, | ||
| 441 | + const v8::Local<v8::String> resource_name, | ||
| 442 | + bool externally_managed_resource) | ||
| 443 | + : env_(env) { | ||
| 444 | + async_id_ = node_env()->new_async_id(); | ||
| 445 | + trigger_async_id_ = node_env()->get_default_trigger_async_id(); | ||
| 446 | + resource_.Reset(node_env()->isolate(), resource_object); | ||
| 447 | + lost_reference_ = false; | ||
| 448 | + if (externally_managed_resource) { | ||
| 449 | + resource_.SetWeak( | ||
| 450 | + this, AsyncContext::WeakCallback, v8::WeakCallbackType::kParameter); | ||
| 451 | + } | ||
| 452 | + | ||
| 453 | + node::AsyncWrap::EmitAsyncInit(node_env(), | ||
| 454 | + resource_object, | ||
| 455 | + resource_name, | ||
| 456 | + async_id_, | ||
| 457 | + trigger_async_id_); | ||
| 458 | + } | ||
| 459 | + | ||
| 460 | + ~AsyncContext() { | ||
| 461 | + resource_.Reset(); | ||
| 462 | + lost_reference_ = true; | ||
| 463 | + node::AsyncWrap::EmitDestroy(node_env(), async_id_); | ||
| 464 | + } | ||
| 465 | + | ||
| 466 | + inline v8::MaybeLocal<v8::Value> MakeCallback( | ||
| 467 | + v8::Local<v8::Object> recv, | ||
| 468 | + const v8::Local<v8::Function> callback, | ||
| 469 | + int argc, | ||
| 470 | + v8::Local<v8::Value> argv[]) { | ||
| 471 | + EnsureReference(); | ||
| 472 | + return node::InternalMakeCallback(node_env(), | ||
| 473 | + resource(), | ||
| 474 | + recv, | ||
| 475 | + callback, | ||
| 476 | + argc, | ||
| 477 | + argv, | ||
| 478 | + {async_id_, trigger_async_id_}); | ||
| 479 | + } | ||
| 480 | + | ||
| 481 | + inline napi_callback_scope OpenCallbackScope() { | ||
| 482 | + EnsureReference(); | ||
| 483 | + napi_callback_scope it = | ||
| 484 | + reinterpret_cast<napi_callback_scope>(new CallbackScope(this)); | ||
| 485 | + env_->open_callback_scopes++; | ||
| 486 | + return it; | ||
| 487 | + } | ||
| 488 | + | ||
| 489 | + inline void EnsureReference() { | ||
| 490 | + if (lost_reference_) { | ||
| 491 | + const v8::HandleScope handle_scope(node_env()->isolate()); | ||
| 492 | + resource_.Reset(node_env()->isolate(), | ||
| 493 | + v8::Object::New(node_env()->isolate())); | ||
| 494 | + lost_reference_ = false; | ||
| 495 | + } | ||
| 496 | + } | ||
| 497 | + | ||
| 498 | + inline node::Environment* node_env() { return env_->node_env(); } | ||
| 499 | + inline v8::Local<v8::Object> resource() { | ||
| 500 | + return resource_.Get(node_env()->isolate()); | ||
| 501 | + } | ||
| 502 | + inline node::async_context async_context() { | ||
| 503 | + return {async_id_, trigger_async_id_}; | ||
| 504 | + } | ||
| 505 | + | ||
| 506 | + static inline void CloseCallbackScope(node_napi_env env, | ||
| 507 | + napi_callback_scope s) { | ||
| 508 | + CallbackScope* callback_scope = reinterpret_cast<CallbackScope*>(s); | ||
| 509 | + delete callback_scope; | ||
| 510 | + env->open_callback_scopes--; | ||
| 511 | + } | ||
| 512 | + | ||
| 513 | + static void WeakCallback(const v8::WeakCallbackInfo<AsyncContext>& data) { | ||
| 514 | + AsyncContext* async_context = data.GetParameter(); | ||
| 515 | + async_context->resource_.Reset(); | ||
| 516 | + async_context->lost_reference_ = true; | ||
| 517 | + } | ||
| 518 | + | ||
| 519 | + private: | ||
| 520 | + class CallbackScope : public node::CallbackScope { | ||
| 521 | + public: | ||
| 522 | + explicit CallbackScope(AsyncContext* async_context) | ||
| 523 | + : node::CallbackScope(async_context->node_env()->isolate(), | ||
| 524 | + async_context->resource_.Get( | ||
| 525 | + async_context->node_env()->isolate()), | ||
| 526 | + async_context->async_context()) {} | ||
| 527 | + }; | ||
| 528 | + | ||
| 529 | + node_napi_env env_; | ||
| 530 | + double async_id_; | ||
| 531 | + double trigger_async_id_; | ||
| 532 | + v8::Global<v8::Object> resource_; | ||
| 533 | + bool lost_reference_; | ||
| 534 | + }; | ||
| 535 | + | ||
| 438 | 536 | } // end of anonymous namespace | |
| 439 | 537 | ||
| 440 | 538 | } // end of namespace v8impl | |
@@ -627,28 +725,19 @@ NAPI_NO_RETURN void napi_fatal_error(const char* location, | |||
| 627 | 725 | } | |
| 628 | 726 | ||
| 629 | 727 | napi_status napi_open_callback_scope(napi_env env, | |
| 630 | - napi_value resource_object, | ||
| 728 | + napi_value /** ignored */, | ||
| 631 | 729 | napi_async_context async_context_handle, | |
| 632 | 730 | napi_callback_scope* result) { | |
| 633 | 731 | // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw | |
| 634 | 732 | // JS exceptions. | |
| 635 | 733 | CHECK_ENV(env); | |
| 636 | 734 | CHECK_ARG(env, result); | |
| 637 | 735 | ||
| 638 | - v8::Local<v8::Context> context = env->context(); | ||
| 639 | - | ||
| 640 | - node::async_context* node_async_context = | ||
| 641 | - reinterpret_cast<node::async_context*>(async_context_handle); | ||
| 642 | - | ||
| 643 | - v8::Local<v8::Object> resource; | ||
| 644 | - CHECK_TO_OBJECT(env, context, resource, resource_object); | ||
| 736 | + v8impl::AsyncContext* node_async_context = | ||
| 737 | + reinterpret_cast<v8impl::AsyncContext*>(async_context_handle); | ||
| 645 | 738 | ||
| 646 | - *result = v8impl::JsCallbackScopeFromV8CallbackScope( | ||
| 647 | - new node::CallbackScope(env->isolate, | ||
| 648 | - resource, | ||
| 649 | - *node_async_context)); | ||
| 739 | + *result = node_async_context->OpenCallbackScope(); | ||
| 650 | 740 | ||
| 651 | - env->open_callback_scopes++; | ||
| 652 | 741 | return napi_clear_last_error(env); | |
| 653 | 742 | } | |
| 654 | 743 | ||
@@ -661,8 +750,9 @@ napi_status napi_close_callback_scope(napi_env env, napi_callback_scope scope) { | |||
| 661 | 750 | return napi_callback_scope_mismatch; | |
| 662 | 751 | } | |
| 663 | 752 | ||
| 664 | - env->open_callback_scopes--; | ||
| 665 | - delete v8impl::V8CallbackScopeFromJsCallbackScope(scope); | ||
| 753 | + v8impl::AsyncContext::CloseCallbackScope(reinterpret_cast<node_napi_env>(env), | ||
| 754 | + scope); | ||
| 755 | + | ||
| 666 | 756 | return napi_clear_last_error(env); | |
| 667 | 757 | } | |
| 668 | 758 | ||
@@ -678,20 +768,24 @@ napi_status napi_async_init(napi_env env, | |||
| 678 | 768 | v8::Local<v8::Context> context = env->context(); | |
| 679 | 769 | ||
| 680 | 770 | v8::Local<v8::Object> v8_resource; | |
| 771 | + bool externally_managed_resource; | ||
| 681 | 772 | if (async_resource != nullptr) { | |
| 682 | 773 | CHECK_TO_OBJECT(env, context, v8_resource, async_resource); | |
| 774 | + externally_managed_resource = true; | ||
| 683 | 775 | } else { | |
| 684 | 776 | v8_resource = v8::Object::New(isolate); | |
| 777 | + externally_managed_resource = false; | ||
| 685 | 778 | } | |
| 686 | 779 | ||
| 687 | 780 | v8::Local<v8::String> v8_resource_name; | |
| 688 | 781 | CHECK_TO_STRING(env, context, v8_resource_name, async_resource_name); | |
| 689 | 782 | ||
| 690 | - // TODO(jasongin): Consider avoiding allocation here by using | ||
| 691 | - // a tagged pointer with 2×31 bit fields instead. | ||
| 692 | - node::async_context* async_context = new node::async_context(); | ||
| 783 | + auto async_context = | ||
| 784 | + new v8impl::AsyncContext(reinterpret_cast<node_napi_env>(env), | ||
| 785 | + v8_resource, | ||
| 786 | + v8_resource_name, | ||
| 787 | + externally_managed_resource); | ||
| 693 | 788 | ||
| 694 | - *async_context = node::EmitAsyncInit(isolate, v8_resource, v8_resource_name); | ||
| 695 | 789 | *result = reinterpret_cast<napi_async_context>(async_context); | |
| 696 | 790 | ||
| 697 | 791 | return napi_clear_last_error(env); | |
@@ -702,11 +796,8 @@ napi_status napi_async_destroy(napi_env env, | |||
| 702 | 796 | CHECK_ENV(env); | |
| 703 | 797 | CHECK_ARG(env, async_context); | |
| 704 | 798 | ||
| 705 | - node::async_context* node_async_context = | ||
| 706 | - reinterpret_cast<node::async_context*>(async_context); | ||
| 707 | - node::EmitAsyncDestroy( | ||
| 708 | - reinterpret_cast<node_napi_env>(env)->node_env(), | ||
| 709 | - *node_async_context); | ||
| 799 | + v8impl::AsyncContext* node_async_context = | ||
| 800 | + reinterpret_cast<v8impl::AsyncContext*>(async_context); | ||
| 710 | 801 | ||
| 711 | 802 | delete node_async_context; | |
| 712 | 803 | ||
@@ -734,17 +825,25 @@ napi_status napi_make_callback(napi_env env, | |||
| 734 | 825 | v8::Local<v8::Function> v8func; | |
| 735 | 826 | CHECK_TO_FUNCTION(env, v8func, func); | |
| 736 | 827 | ||
| 737 | - node::async_context* node_async_context = | ||
| 738 | - reinterpret_cast<node::async_context*>(async_context); | ||
| 739 | - if (node_async_context == nullptr) { | ||
| 740 | - static node::async_context empty_context = { 0, 0 }; | ||
| 741 | - node_async_context = &empty_context; | ||
| 742 | - } | ||
| 828 | + v8::MaybeLocal<v8::Value> callback_result; | ||
| 743 | 829 | ||
| 744 | - v8::MaybeLocal<v8::Value> callback_result = node::MakeCallback( | ||
| 745 | - env->isolate, v8recv, v8func, argc, | ||
| 746 | - reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)), | ||
| 747 | - *node_async_context); | ||
| 830 | + if (async_context == nullptr) { | ||
| 831 | + callback_result = node::MakeCallback( | ||
| 832 | + env->isolate, | ||
| 833 | + v8recv, | ||
| 834 | + v8func, | ||
| 835 | + argc, | ||
| 836 | + reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)), | ||
| 837 | + {0, 0}); | ||
| 838 | + } else { | ||
| 839 | + auto node_async_context = | ||
| 840 | + reinterpret_cast<v8impl::AsyncContext*>(async_context); | ||
| 841 | + callback_result = node_async_context->MakeCallback( | ||
| 842 | + v8recv, | ||
| 843 | + v8func, | ||
| 844 | + argc, | ||
| 845 | + reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv))); | ||
| 846 | + } | ||
| 748 | 847 | ||
| 749 | 848 | if (try_catch.HasCaught()) { | |
| 750 | 849 | return napi_set_last_error(env, napi_pending_exception); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments