| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 3e79dba commit ce748f6
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,6 +8,7 @@ namespace node { | |||
| 8 | 8 | ||
| 9 | 9 | using v8::Context; | |
| 10 | 10 | using v8::Function; | |
| 11 | + using v8::Global; | ||
| 11 | 12 | using v8::HandleScope; | |
| 12 | 13 | using v8::Isolate; | |
| 13 | 14 | using v8::Local; | |
@@ -26,13 +27,30 @@ CallbackScope::CallbackScope(Isolate* isolate, | |||
| 26 | 27 | CallbackScope::CallbackScope(Environment* env, | |
| 27 | 28 | Local<Object> object, | |
| 28 | 29 | async_context asyncContext) | |
| 29 | - : resource_storage_(object), | ||
| 30 | - private_( | ||
| 31 | - new InternalCallbackScope(env, &resource_storage_, asyncContext)), | ||
| 30 | + : resource_storage_({.local = object}), | ||
| 31 | + private_(new InternalCallbackScope( | ||
| 32 | + env, &resource_storage_.local, asyncContext)), | ||
| 32 | 33 | try_catch_(env->isolate()) { | |
| 33 | 34 | try_catch_.SetVerbose(true); | |
| 34 | 35 | } | |
| 35 | 36 | ||
| 37 | + CallbackScope::CallbackScope(Environment* env, | ||
| 38 | + Global<Object>* object, | ||
| 39 | + async_context asyncContext) | ||
| 40 | + : resource_storage_({.global_ptr = object}), | ||
| 41 | + private_(new InternalCallbackScope( | ||
| 42 | + env, resource_storage_.global_ptr, asyncContext)), | ||
| 43 | + try_catch_(env->isolate()) { | ||
| 44 | + try_catch_.SetVerbose(true); | ||
| 45 | + // These checks can be removed in a future major version -- they ensure | ||
| 46 | + // ABI compatibility with previous Node.js versions. | ||
| 47 | + static_assert(sizeof(resource_storage_) == sizeof(Global<Object>*)); | ||
| 48 | + static_assert(sizeof(resource_storage_) == sizeof(Local<Object>)); | ||
| 49 | + static_assert(alignof(decltype(resource_storage_)) == | ||
| 50 | + alignof(Global<Object>*)); | ||
| 51 | + static_assert(alignof(decltype(resource_storage_)) == alignof(Local<Object>)); | ||
| 52 | + } | ||
| 53 | + | ||
| 36 | 54 | CallbackScope::~CallbackScope() { | |
| 37 | 55 | if (try_catch_.HasCaught()) | |
| 38 | 56 | private_->MarkAsFailed(); | |
@@ -49,7 +67,7 @@ InternalCallbackScope::InternalCallbackScope(AsyncWrap* async_wrap, int flags) | |||
| 49 | 67 | ||
| 50 | 68 | InternalCallbackScope::InternalCallbackScope( | |
| 51 | 69 | Environment* env, | |
| 52 | - std::variant<Local<Object>, Local<Object>*> object, | ||
| 70 | + std::variant<Local<Object>, Local<Object>*, Global<Object>*> object_arg, | ||
| 53 | 71 | const async_context& asyncContext, | |
| 54 | 72 | int flags, | |
| 55 | 73 | Local<Value> context_frame) | |
@@ -59,13 +77,16 @@ InternalCallbackScope::InternalCallbackScope( | |||
| 59 | 77 | skip_task_queues_(flags & kSkipTaskQueues) { | |
| 60 | 78 | CHECK_NOT_NULL(env); | |
| 61 | 79 | ||
| 62 | - if (std::holds_alternative<Local<Object>>(object)) { | ||
| 63 | - object_storage_ = std::get<Local<Object>>(object); | ||
| 64 | - object_ = &object_storage_; | ||
| 80 | + std::variant<v8::Local<v8::Object>*, v8::Global<v8::Object>*> object; | ||
| 81 | + if (std::holds_alternative<Local<Object>>(object_arg)) { | ||
| 82 | + object_storage_ = std::get<Local<Object>>(object_arg); | ||
| 83 | + object = &object_storage_; | ||
| 84 | + } else if (std::holds_alternative<Local<Object>*>(object_arg)) { | ||
| 85 | + object = std::get<Local<Object>*>(object_arg); | ||
| 65 | 86 | } else { | |
| 66 | - object_ = std::get<Local<Object>*>(object); | ||
| 67 | - CHECK_NOT_NULL(object_); | ||
| 87 | + object = std::get<Global<Object>*>(object_arg); | ||
| 68 | 88 | } | |
| 89 | + std::visit([](auto* ptr) { CHECK_NOT_NULL(ptr); }, object); | ||
| 69 | 90 | ||
| 70 | 91 | env->PushAsyncCallbackScope(); | |
| 71 | 92 | ||
@@ -93,7 +114,7 @@ InternalCallbackScope::InternalCallbackScope( | |||
| 93 | 114 | isolate, async_context_frame::exchange(isolate, context_frame)); | |
| 94 | 115 | ||
| 95 | 116 | env->async_hooks()->push_async_context( | |
| 96 | - async_context_.async_id, async_context_.trigger_async_id, object_); | ||
| 117 | + async_context_.async_id, async_context_.trigger_async_id, object); | ||
| 97 | 118 | ||
| 98 | 119 | pushed_ids_ = true; | |
| 99 | 120 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -278,7 +278,7 @@ void AsyncWrap::PushAsyncContext(const FunctionCallbackInfo<Value>& args) { | |||
| 278 | 278 | // then the checks in push_async_ids() and pop_async_id() will. | |
| 279 | 279 | double async_id = args[0]->NumberValue(env->context()).FromJust(); | |
| 280 | 280 | double trigger_async_id = args[1]->NumberValue(env->context()).FromJust(); | |
| 281 | - env->async_hooks()->push_async_context(async_id, trigger_async_id, nullptr); | ||
| 281 | + env->async_hooks()->push_async_context(async_id, trigger_async_id, {}); | ||
| 282 | 282 | } | |
| 283 | 283 | ||
| 284 | 284 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -106,10 +106,19 @@ v8::Local<v8::Array> AsyncHooks::js_execution_async_resources() { | |||
| 106 | 106 | } | |
| 107 | 107 | ||
| 108 | 108 | v8::Local<v8::Object> AsyncHooks::native_execution_async_resource(size_t i) { | |
| 109 | - if (i >= native_execution_async_resources_.size() || | ||
| 110 | - native_execution_async_resources_[i] == nullptr) | ||
| 111 | - return {}; | ||
| 112 | - return *native_execution_async_resources_[i]; | ||
| 109 | + if (i >= native_execution_async_resources_.size()) return {}; | ||
| 110 | + auto resource = native_execution_async_resources_[i]; | ||
| 111 | + if (std::holds_alternative<v8::Global<v8::Object>*>(resource)) [[unlikely]] { | ||
| 112 | + auto* global = std::get<v8::Global<v8::Object>*>(resource); | ||
| 113 | + if (global == nullptr) [[unlikely]] | ||
| 114 | + return {}; | ||
| 115 | + return global->Get(env()->isolate()); | ||
| 116 | + } else { | ||
| 117 | + auto* local = std::get<v8::Local<v8::Object>*>(resource); | ||
| 118 | + if (local == nullptr) [[unlikely]] | ||
| 119 | + return {}; | ||
| 120 | + return *local; | ||
| 121 | + } | ||
| 113 | 122 | } | |
| 114 | 123 | ||
| 115 | 124 | inline v8::Local<v8::String> AsyncHooks::provider_string(int idx) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -48,6 +48,7 @@ using v8::EmbedderGraph; | |||
| 48 | 48 | using v8::EscapableHandleScope; | |
| 49 | 49 | using v8::ExternalMemoryAccounter; | |
| 50 | 50 | using v8::Function; | |
| 51 | + using v8::Global; | ||
| 51 | 52 | using v8::HandleScope; | |
| 52 | 53 | using v8::HeapProfiler; | |
| 53 | 54 | using v8::HeapSpaceStatistics; | |
@@ -120,10 +121,12 @@ void Environment::ResetPromiseHooks(Local<Function> init, | |||
| 120 | 121 | } | |
| 121 | 122 | ||
| 122 | 123 | // Remember to keep this code aligned with pushAsyncContext() in JS. | |
| 123 | - void AsyncHooks::push_async_context(double async_id, | ||
| 124 | - double trigger_async_id, | ||
| 125 | - Local<Object>* resource) { | ||
| 126 | - CHECK_IMPLIES(resource != nullptr, !resource->IsEmpty()); | ||
| 124 | + void AsyncHooks::push_async_context( | ||
| 125 | + double async_id, | ||
| 126 | + double trigger_async_id, | ||
| 127 | + std::variant<Local<Object>*, Global<Object>*> resource) { | ||
| 128 | + std::visit([](auto* ptr) { CHECK_IMPLIES(ptr != nullptr, !ptr->IsEmpty()); }, | ||
| 129 | + resource); | ||
| 127 | 130 | // Since async_hooks is experimental, do only perform the check | |
| 128 | 131 | // when async_hooks is enabled. | |
| 129 | 132 | if (fields_[kCheck] > 0) { | |
@@ -141,12 +144,13 @@ void AsyncHooks::push_async_context(double async_id, | |||
| 141 | 144 | ||
| 142 | 145 | #ifdef DEBUG | |
| 143 | 146 | for (uint32_t i = offset; i < native_execution_async_resources_.size(); i++) | |
| 144 | - CHECK_NULL(native_execution_async_resources_[i]); | ||
| 147 | + std::visit([](auto* ptr) { CHECK_NULL(ptr); }, | ||
| 148 | + native_execution_async_resources_[i]); | ||
| 145 | 149 | #endif | |
| 146 | 150 | ||
| 147 | 151 | // When this call comes from JS (as a way of increasing the stack size), | |
| 148 | 152 | // `resource` will be empty, because JS caches these values anyway. | |
| 149 | - if (resource != nullptr) { | ||
| 153 | + if (std::visit([](auto* ptr) { return ptr != nullptr; }, resource)) { | ||
| 150 | 154 | native_execution_async_resources_.resize(offset + 1); | |
| 151 | 155 | // Caveat: This is a v8::Local<>* assignment, we do not keep a v8::Global<>! | |
| 152 | 156 | native_execution_async_resources_[offset] = resource; | |
@@ -173,11 +177,13 @@ bool AsyncHooks::pop_async_context(double async_id) { | |||
| 173 | 177 | fields_[kStackLength] = offset; | |
| 174 | 178 | ||
| 175 | 179 | if (offset < native_execution_async_resources_.size() && | |
| 176 | - native_execution_async_resources_[offset] != nullptr) [[likely]] { | ||
| 180 | + std::visit([](auto* ptr) { return ptr != nullptr; }, | ||
| 181 | + native_execution_async_resources_[offset])) [[likely]] { | ||
| 177 | 182 | #ifdef DEBUG | |
| 178 | 183 | for (uint32_t i = offset + 1; i < native_execution_async_resources_.size(); | |
| 179 | 184 | i++) { | |
| 180 | - CHECK_NULL(native_execution_async_resources_[i]); | ||
| 185 | + std::visit([](auto* ptr) { CHECK_NULL(ptr); }, | ||
| 186 | + native_execution_async_resources_[i]); | ||
| 181 | 187 | } | |
| 182 | 188 | #endif | |
| 183 | 189 | native_execution_async_resources_.resize(offset); | |
@@ -1821,10 +1827,9 @@ AsyncHooks::SerializeInfo AsyncHooks::Serialize(Local<Context> context, | |||
| 1821 | 1827 | info.native_execution_async_resources.resize( | |
| 1822 | 1828 | native_execution_async_resources_.size()); | |
| 1823 | 1829 | for (size_t i = 0; i < native_execution_async_resources_.size(); i++) { | |
| 1830 | + auto resource = native_execution_async_resource(i); | ||
| 1824 | 1831 | info.native_execution_async_resources[i] = | |
| 1825 | - native_execution_async_resources_[i] == nullptr | ||
| 1826 | - ? SIZE_MAX | ||
| 1827 | - : creator->AddData(context, *native_execution_async_resources_[i]); | ||
| 1832 | + resource.IsEmpty() ? SIZE_MAX : creator->AddData(context, resource); | ||
| 1828 | 1833 | } | |
| 1829 | 1834 | ||
| 1830 | 1835 | // At the moment, promise hooks are not supported in the startup snapshot. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -68,6 +68,7 @@ | |||
| 68 | 68 | #include <string> | |
| 69 | 69 | #include <unordered_map> | |
| 70 | 70 | #include <unordered_set> | |
| 71 | + #include <variant> | ||
| 71 | 72 | #include <vector> | |
| 72 | 73 | ||
| 73 | 74 | namespace node { | |
@@ -323,9 +324,11 @@ class AsyncHooks : public MemoryRetainer { | |||
| 323 | 324 | // NB: This call does not take (co-)ownership of `execution_async_resource`. | |
| 324 | 325 | // The lifetime of the `v8::Local<>` pointee must last until | |
| 325 | 326 | // `pop_async_context()` or `clear_async_id_stack()` are called. | |
| 326 | - void push_async_context(double async_id, | ||
| 327 | - double trigger_async_id, | ||
| 328 | - v8::Local<v8::Object>* execution_async_resource); | ||
| 327 | + void push_async_context( | ||
| 328 | + double async_id, | ||
| 329 | + double trigger_async_id, | ||
| 330 | + std::variant<v8::Local<v8::Object>*, v8::Global<v8::Object>*> | ||
| 331 | + execution_async_resource); | ||
| 329 | 332 | bool pop_async_context(double async_id); | |
| 330 | 333 | void clear_async_id_stack(); // Used in fatal exceptions. | |
| 331 | 334 | ||
@@ -389,7 +392,12 @@ class AsyncHooks : public MemoryRetainer { | |||
| 389 | 392 | ||
| 390 | 393 | // We avoid storing the handles directly here, because they are already | |
| 391 | 394 | // properly allocated on the stack, we just need access to them here. | |
| 392 | - std::deque<v8::Local<v8::Object>*> native_execution_async_resources_; | ||
| 395 | + // The `v8::Global<>` variant is here because the Node-API API design | ||
| 396 | + // does not allow us to make sure that we exclusively store this value | ||
| 397 | + // on the stack, so we accept the small perf hit that comes with | ||
| 398 | + // global handles in that case. | ||
| 399 | + std::deque<std::variant<v8::Local<v8::Object>*, v8::Global<v8::Object>*>> | ||
| 400 | + native_execution_async_resources_; | ||
| 393 | 401 | ||
| 394 | 402 | // Non-empty during deserialization | |
| 395 | 403 | const SerializeInfo* info_ = nullptr; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1444,6 +1444,12 @@ class NODE_EXTERN CallbackScope { | |||
| 1444 | 1444 | CallbackScope(Environment* env, | |
| 1445 | 1445 | v8::Local<v8::Object> resource, | |
| 1446 | 1446 | async_context asyncContext); | |
| 1447 | + // `resource` needs to outlive the scope in this case. | ||
| 1448 | + // This is for the rare situation in which `CallbackScope` cannot be | ||
| 1449 | + // stack-allocated. `resource` needs to outlive this scope. | ||
| 1450 | + CallbackScope(Environment* env, | ||
| 1451 | + v8::Global<v8::Object>* resource, | ||
| 1452 | + async_context asyncContext); | ||
| 1447 | 1453 | ~CallbackScope(); | |
| 1448 | 1454 | ||
| 1449 | 1455 | void operator=(const CallbackScope&) = delete; | |
@@ -1452,8 +1458,11 @@ class NODE_EXTERN CallbackScope { | |||
| 1452 | 1458 | CallbackScope(CallbackScope&&) = delete; | |
| 1453 | 1459 | ||
| 1454 | 1460 | private: | |
| 1455 | - void* reserved_; | ||
| 1456 | - v8::Local<v8::Object> resource_storage_; | ||
| 1461 | + void* resource_storage_global_; | ||
| 1462 | + union { | ||
| 1463 | + v8::Local<v8::Object> local; | ||
| 1464 | + v8::Global<v8::Object>* global_ptr; | ||
| 1465 | + } resource_storage_; | ||
| 1457 | 1466 | InternalCallbackScope* private_; | |
| 1458 | 1467 | v8::TryCatch try_catch_; | |
| 1459 | 1468 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -584,10 +584,9 @@ class AsyncContext { | |||
| 584 | 584 | ||
| 585 | 585 | inline napi_callback_scope OpenCallbackScope() { | |
| 586 | 586 | EnsureReference(); | |
| 587 | - napi_callback_scope it = | ||
| 588 | - reinterpret_cast<napi_callback_scope>(new CallbackScope(this)); | ||
| 587 | + auto scope = new HeapAllocatedCallbackScope(this); | ||
| 589 | 588 | env_->open_callback_scopes++; | |
| 590 | - return it; | ||
| 589 | + return scope->to_opaque(); | ||
| 591 | 590 | } | |
| 592 | 591 | ||
| 593 | 592 | inline void EnsureReference() { | |
@@ -609,8 +608,7 @@ class AsyncContext { | |||
| 609 | 608 | ||
| 610 | 609 | static inline void CloseCallbackScope(node_napi_env env, | |
| 611 | 610 | napi_callback_scope s) { | |
| 612 | - CallbackScope* callback_scope = reinterpret_cast<CallbackScope*>(s); | ||
| 613 | - delete callback_scope; | ||
| 611 | + delete HeapAllocatedCallbackScope::FromOpaque(s); | ||
| 614 | 612 | env->open_callback_scopes--; | |
| 615 | 613 | } | |
| 616 | 614 | ||
@@ -621,13 +619,26 @@ class AsyncContext { | |||
| 621 | 619 | } | |
| 622 | 620 | ||
| 623 | 621 | private: | |
| 624 | - class CallbackScope : public node::CallbackScope { | ||
| 622 | + class HeapAllocatedCallbackScope final { | ||
| 625 | 623 | public: | |
| 626 | - explicit CallbackScope(AsyncContext* async_context) | ||
| 627 | - : node::CallbackScope(async_context->node_env(), | ||
| 628 | - async_context->resource_.Get( | ||
| 629 | - async_context->node_env()->isolate()), | ||
| 630 | - async_context->async_context()) {} | ||
| 624 | + napi_callback_scope to_opaque() { | ||
| 625 | + return reinterpret_cast<napi_callback_scope>(this); | ||
| 626 | + } | ||
| 627 | + static HeapAllocatedCallbackScope* FromOpaque(napi_callback_scope s) { | ||
| 628 | + return reinterpret_cast<HeapAllocatedCallbackScope*>(s); | ||
| 629 | + } | ||
| 630 | + | ||
| 631 | + explicit HeapAllocatedCallbackScope(AsyncContext* async_context) | ||
| 632 | + : resource_storage_(async_context->node_env()->isolate(), | ||
| 633 | + async_context->resource_.Get( | ||
| 634 | + async_context->node_env()->isolate())), | ||
| 635 | + cs_(async_context->node_env(), | ||
| 636 | + &resource_storage_, | ||
| 637 | + async_context->async_context()) {} | ||
| 638 | + | ||
| 639 | + private: | ||
| 640 | + v8::Global<v8::Object> resource_storage_; | ||
| 641 | + node::CallbackScope cs_; | ||
| 631 | 642 | }; | |
| 632 | 643 | ||
| 633 | 644 | node_napi_env env_; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -250,10 +250,12 @@ class InternalCallbackScope { | |||
| 250 | 250 | // stack-allocated itself, OR that `object` is a pointer to a stack-allocated | |
| 251 | 251 | // `v8::Local<v8::Object>` which outlives this scope (e.g. for the | |
| 252 | 252 | // public `CallbackScope` which indirectly allocates an instance of | |
| 253 | - // this class for ABI stability purposes). | ||
| 253 | + // this class for ABI stability purposes), OR pass a `Global<>`. | ||
| 254 | 254 | InternalCallbackScope( | |
| 255 | 255 | Environment* env, | |
| 256 | - std::variant<v8::Local<v8::Object>, v8::Local<v8::Object>*> object, | ||
| 256 | + std::variant<v8::Local<v8::Object>, | ||
| 257 | + v8::Local<v8::Object>*, | ||
| 258 | + v8::Global<v8::Object>*> object, | ||
| 257 | 259 | const async_context& asyncContext, | |
| 258 | 260 | int flags = kNoFlags, | |
| 259 | 261 | v8::Local<v8::Value> context_frame = v8::Local<v8::Value>()); | |
@@ -270,7 +272,6 @@ class InternalCallbackScope { | |||
| 270 | 272 | Environment* env_; | |
| 271 | 273 | async_context async_context_; | |
| 272 | 274 | v8::Local<v8::Object> object_storage_; | |
| 273 | - v8::Local<v8::Object>* object_; | ||
| 274 | 275 | bool skip_hooks_; | |
| 275 | 276 | bool skip_task_queues_; | |
| 276 | 277 | bool failed_ = false; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments