Skip to content

Commit 3befe80

Browse files
puzpuzpuzMylesBorins
authored andcommitted
async_hooks: fix ctx loss after nested ALS calls
PR-URL: #32085 Reviewed-By: Stephen Belanger <[email protected]> Reviewed-By: Vladimir de Turckheim <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
1 parent a037770 commit 3befe80

File tree

3 files changed

+50
-23
lines changed

3 files changed

+50
-23
lines changed

lib/async_hooks.js

+12-9
Original file line numberDiff line numberDiff line change
@@ -255,23 +255,21 @@ class AsyncLocalStorage {
255255
resource[this.kResourceStore] = store;
256256
}
257257

258-
_exit() {
259-
const resource = executionAsyncResource();
260-
if (resource) {
261-
resource[this.kResourceStore] = undefined;
262-
}
263-
}
264-
265258
runSyncAndReturn(store, callback, ...args) {
259+
const resource = executionAsyncResource();
260+
const outerStore = resource[this.kResourceStore];
266261
this._enter(store);
267262
try {
268263
return callback(...args);
269264
} finally {
270-
this._exit();
265+
resource[this.kResourceStore] = outerStore;
271266
}
272267
}
273268

274269
exitSyncAndReturn(callback, ...args) {
270+
if (!this.enabled) {
271+
return callback(...args);
272+
}
275273
this.enabled = false;
276274
try {
277275
return callback(...args);
@@ -288,12 +286,17 @@ class AsyncLocalStorage {
288286
}
289287

290288
run(store, callback, ...args) {
289+
const resource = executionAsyncResource();
290+
const outerStore = resource[this.kResourceStore];
291291
this._enter(store);
292292
process.nextTick(callback, ...args);
293-
this._exit();
293+
resource[this.kResourceStore] = outerStore;
294294
}
295295

296296
exit(callback, ...args) {
297+
if (!this.enabled) {
298+
return process.nextTick(callback, ...args);
299+
}
297300
this.enabled = false;
298301
process.nextTick(callback, ...args);
299302
this.enabled = true;

test/async-hooks/test-async-local-storage-enable-disable.js

+8
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,16 @@ asyncLocalStorage.runSyncAndReturn(new Map(), () => {
1212
process.nextTick(() => {
1313
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
1414
});
15+
1516
asyncLocalStorage.disable();
1617
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
18+
19+
// Calls to exit() should not mess with enabled status
20+
asyncLocalStorage.exit(() => {
21+
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
22+
});
23+
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
24+
1725
process.nextTick(() => {
1826
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
1927
asyncLocalStorage.runSyncAndReturn(new Map(), () => {

test/async-hooks/test-async-local-storage-nested.js

+30-14
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,35 @@ const assert = require('assert');
44
const { AsyncLocalStorage } = require('async_hooks');
55

66
const asyncLocalStorage = new AsyncLocalStorage();
7+
const outer = {};
8+
const inner = {};
79

8-
setTimeout(() => {
9-
asyncLocalStorage.run(new Map(), () => {
10-
const asyncLocalStorage2 = new AsyncLocalStorage();
11-
asyncLocalStorage2.run(new Map(), () => {
12-
const store = asyncLocalStorage.getStore();
13-
const store2 = asyncLocalStorage2.getStore();
14-
store.set('hello', 'world');
15-
store2.set('hello', 'foo');
16-
setTimeout(() => {
17-
assert.strictEqual(asyncLocalStorage.getStore().get('hello'), 'world');
18-
assert.strictEqual(asyncLocalStorage2.getStore().get('hello'), 'foo');
19-
}, 200);
20-
});
10+
function testInner() {
11+
assert.strictEqual(asyncLocalStorage.getStore(), outer);
12+
13+
asyncLocalStorage.run(inner, () => {
14+
assert.strictEqual(asyncLocalStorage.getStore(), inner);
15+
});
16+
assert.strictEqual(asyncLocalStorage.getStore(), outer);
17+
18+
asyncLocalStorage.exit(() => {
19+
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
20+
});
21+
assert.strictEqual(asyncLocalStorage.getStore(), outer);
22+
23+
asyncLocalStorage.runSyncAndReturn(inner, () => {
24+
assert.strictEqual(asyncLocalStorage.getStore(), inner);
2125
});
22-
}, 100);
26+
assert.strictEqual(asyncLocalStorage.getStore(), outer);
27+
28+
asyncLocalStorage.exitSyncAndReturn(() => {
29+
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
30+
});
31+
assert.strictEqual(asyncLocalStorage.getStore(), outer);
32+
}
33+
34+
asyncLocalStorage.run(outer, testInner);
35+
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
36+
37+
asyncLocalStorage.runSyncAndReturn(outer, testInner);
38+
assert.strictEqual(asyncLocalStorage.getStore(), undefined);

0 commit comments

Comments
 (0)