-
-
Notifications
You must be signed in to change notification settings - Fork 38.2k
src: avoid env lookups and a global handle in InternalCallbackScope #66316
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| build/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| #include <node.h> | ||
| #include <uv.h> | ||
| #include <v8.h> | ||
|
|
||
| using v8::Context; | ||
| using v8::Function; | ||
| using v8::FunctionCallbackInfo; | ||
| using v8::Global; | ||
| using v8::HandleScope; | ||
| using v8::Isolate; | ||
| using v8::Local; | ||
| using v8::Object; | ||
| using v8::Value; | ||
|
|
||
| struct State { | ||
| uv_timer_t timer; | ||
| Isolate* isolate; | ||
| int64_t n; | ||
| Global<Function> fn; | ||
| Global<Function> done; | ||
| }; | ||
|
|
||
| static void OnTimer(uv_timer_t* handle) { | ||
| State* state = static_cast<State*>(handle->data); | ||
| Isolate* isolate = state->isolate; | ||
| HandleScope handle_scope(isolate); | ||
| Local<Function> fn = state->fn.Get(isolate); | ||
| Local<Context> context = fn->GetCreationContextChecked(isolate); | ||
| Context::Scope context_scope(context); | ||
| Local<Object> recv = context->Global(); | ||
| for (int64_t i = 0; i < state->n; i++) { | ||
| HandleScope inner_scope(isolate); | ||
| (void)node::MakeCallback(isolate, recv, fn, 0, nullptr, {0, 0}); | ||
| } | ||
| Local<Function> done = state->done.Get(isolate); | ||
| (void)node::MakeCallback(isolate, recv, done, 0, nullptr, {0, 0}); | ||
| uv_close(reinterpret_cast<uv_handle_t*>(&state->timer), | ||
| [](uv_handle_t* h) { delete static_cast<State*>(h->data); }); | ||
| } | ||
|
|
||
| // run(n, fn, done): calls fn n times from a timer, then calls done. | ||
| static void Run(const FunctionCallbackInfo<Value>& args) { | ||
| Isolate* isolate = args.GetIsolate(); | ||
| State* state = new State; | ||
| state->isolate = isolate; | ||
| state->n = args[0]->IntegerValue(isolate->GetCurrentContext()).FromJust(); | ||
| state->fn.Reset(isolate, args[1].As<Function>()); | ||
| state->done.Reset(isolate, args[2].As<Function>()); | ||
| state->timer.data = state; | ||
| uv_timer_init(node::GetCurrentEventLoop(isolate), &state->timer); | ||
| uv_timer_start(&state->timer, OnTimer, 0, 0); | ||
| } | ||
|
|
||
| static void Initialize(Local<Object> target, Local<Value> module, void* data) { | ||
| NODE_SET_METHOD(target, "run", Run); | ||
| } | ||
|
|
||
| NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| 'targets': [ | ||
| { | ||
| 'target_name': 'binding', | ||
| 'sources': [ 'binding.cc' ] | ||
| } | ||
| ] | ||
| } | ||
|
nigrosimone marked this conversation as resolved.
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| 'use strict'; | ||
|
|
||
| const common = require('../../common.js'); | ||
|
|
||
| // The addon calls into JS with node::MakeCallback from a libuv timer, so | ||
| // every call opens a top-level callback scope, like an I/O callback does. | ||
|
|
||
| let binding; | ||
| try { | ||
| binding = require(`./build/${common.buildType}/binding`); | ||
| } catch { | ||
| console.error('napi/make_callback/index.js Binding failed to load'); | ||
| process.exit(0); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably should be non-zero
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I followed the other benchmarks in benchmark/napi: 6 of the 10 do the same when the binding does not load. function_call explains why: the binding often fails to load because the benchmark runs with a different node version, so it aborts quietly. If you prefer a non-zero code, I can change it. |
||
| } | ||
|
|
||
| const bench = common.createBenchmark(main, { | ||
| n: [1e6, 1e7], | ||
| }); | ||
|
|
||
| function main({ n }) { | ||
| bench.start(); | ||
| binding.run(n, () => {}, () => bench.end(n)); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| 'use strict'; | ||
|
|
||
| const common = require('../../common'); | ||
| const assert = require('assert'); | ||
| const { AsyncLocalStorage } = require('async_hooks'); | ||
| const { runInCallbackScope } = require(`./build/${common.buildType}/binding`); | ||
|
|
||
| // A CallbackScope must restore the async context frame that was active | ||
| // before it, when there was none and when there was one. | ||
|
|
||
| const als = new AsyncLocalStorage(); | ||
|
|
||
| runInCallbackScope({}, 0, 0, common.mustCall(() => { | ||
| als.enterWith('inner'); | ||
| })); | ||
| assert.strictEqual(als.getStore(), undefined); | ||
|
|
||
| als.run('outer', common.mustCall(() => { | ||
| runInCallbackScope({}, 0, 0, common.mustCall(() => { | ||
| als.enterWith('inner'); | ||
| })); | ||
| assert.strictEqual(als.getStore(), 'outer'); | ||
| })); |
Uh oh!
There was an error while loading. Please reload this page.