Skip to content

Commit cf6df06

Browse files
committed
src: keep ALS store in AsyncResource::MakeCallback
AsyncResource saves the async context frame when it is created, and MakeCallback() enters it with async_context_frame::Scope. Then node::MakeCallback() opens the callback scope with an undefined frame, so the callback never runs in the saved one. Since AsyncContextFrame is the default, AsyncLocalStorage loses its store in these callbacks. Pass the saved frame to InternalMakeCallback(), as the Node-API AsyncContext already does. This also removes the Scope from every call, with its two Environment lookups and its global handle. Refs: #66316 Refs: #43038 Refs: nodejs/performance#24 Signed-off-by: Nigro Simone <nigro.simone@gmail.com>
1 parent 66f26d3 commit cf6df06

2 files changed

Lines changed: 47 additions & 16 deletions

File tree

‎src/api/async_resource.cc‎

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "async_context_frame.h"
22
#include "env-inl.h"
33
#include "node.h"
4+
#include "node_internals.h"
45

56
namespace node {
67

@@ -10,6 +11,7 @@ using v8::Local;
1011
using v8::MaybeLocal;
1112
using v8::Object;
1213
using v8::String;
14+
using v8::Undefined;
1315
using v8::Value;
1416

1517
AsyncResource::AsyncResource(Isolate* isolate,
@@ -39,33 +41,40 @@ MaybeLocal<Value> AsyncResource::MakeCallback(Local<Function> callback,
3941
int argc,
4042
Local<Value>* argv) {
4143
auto isolate = env_->isolate();
42-
async_context_frame::Scope async_context_frame_scope(
43-
isolate, context_frame_.Get(isolate));
44-
45-
return node::MakeCallback(
46-
isolate, get_resource(), callback, argc, argv, async_context_);
44+
// As in Node-API: node::MakeCallback() would run it with no frame.
45+
return InternalMakeCallback(isolate,
46+
get_resource(),
47+
callback,
48+
argc,
49+
argv,
50+
async_context_,
51+
context_frame_.Get(isolate));
4752
}
4853

4954
MaybeLocal<Value> AsyncResource::MakeCallback(const char* method,
5055
int argc,
5156
Local<Value>* argv) {
52-
auto isolate = env_->isolate();
53-
async_context_frame::Scope async_context_frame_scope(
54-
isolate, context_frame_.Get(isolate));
55-
56-
return node::MakeCallback(
57-
isolate, get_resource(), method, argc, argv, async_context_);
57+
Local<String> method_string;
58+
if (!String::NewFromUtf8(env_->isolate(), method).ToLocal(&method_string)) {
59+
return {};
60+
}
61+
return MakeCallback(method_string, argc, argv);
5862
}
5963

6064
MaybeLocal<Value> AsyncResource::MakeCallback(Local<String> symbol,
6165
int argc,
6266
Local<Value>* argv) {
6367
auto isolate = env_->isolate();
64-
async_context_frame::Scope async_context_frame_scope(
65-
isolate, context_frame_.Get(isolate));
66-
67-
return node::MakeCallback(
68-
isolate, get_resource(), symbol, argc, argv, async_context_);
68+
// Check can_call_into_js() first because calling Get() might do so.
69+
if (!env_->can_call_into_js()) return {};
70+
Local<Value> callback;
71+
if (!get_resource()
72+
->Get(isolate->GetCurrentContext(), symbol)
73+
.ToLocal(&callback)) {
74+
return {};
75+
}
76+
if (!callback->IsFunction()) return Undefined(isolate);
77+
return MakeCallback(callback.As<Function>(), argc, argv);
6978
}
7079

7180
Local<Object> AsyncResource::get_resource() {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
3+
const common = require('../../common');
4+
const assert = require('assert');
5+
const { AsyncLocalStorage } = require('async_hooks');
6+
const binding = require(`./build/${common.buildType}/binding`);
7+
8+
// AsyncResource::MakeCallback() must run the callback in the async context
9+
// frame that was active when the resource was created.
10+
11+
const als = new AsyncLocalStorage();
12+
const object = {
13+
'methöd': common.mustCall(() => {
14+
assert.strictEqual(als.getStore(), 'store');
15+
}, 3),
16+
};
17+
const resource = als.run('store', () => binding.createAsyncResource(object));
18+
19+
binding.callViaFunction(resource);
20+
binding.callViaString(resource);
21+
binding.callViaUtf8Name(resource);
22+
binding.destroyAsyncResource(resource);

0 commit comments

Comments
 (0)