Skip to content

Commit 06d607d

Browse files
puzpuzpuztargos
authored andcommitted
async_hooks: fix ctx loss after nested ALS calls
PR-URL: #32085 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Vladimir de Turckheim <vlad2t@hotmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
1 parent 52a1154 commit 06d607d

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
@@ -256,23 +256,21 @@ class AsyncLocalStorage {
256256
resource[this.kResourceStore] = store;
257257
}
258258

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

275270
exitSyncAndReturn(callback, ...args) {
271+
if (!this.enabled) {
272+
return callback(...args);
273+
}
276274
this.enabled = false;
277275
try {
278276
return callback(...args);
@@ -289,12 +287,17 @@ class AsyncLocalStorage {
289287
}
290288

291289
run(store, callback, ...args) {
290+
const resource = executionAsyncResource();
291+
const outerStore = resource[this.kResourceStore];
292292
this._enter(store);
293293
process.nextTick(callback, ...args);
294-
this._exit();
294+
resource[this.kResourceStore] = outerStore;
295295
}
296296

297297
exit(callback, ...args) {
298+
if (!this.enabled) {
299+
return process.nextTick(callback, ...args);
300+
}
298301
this.enabled = false;
299302
process.nextTick(callback, ...args);
300303
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)