|
| 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); |
|