async_hooks: ensure AsyncLocalStore instances work isolated
Avoid that one AsyncLocalStore instance changes the state of another AsyncLocalStore instance by restoring only the owned store instead the complete AsyncContextFrame. PR-URL: https://github.com/nodejs/node/pull/58149 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
This commit is contained in:
parent
b0f2aa9973
commit
a0d458e448
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
ObjectIs,
|
||||
ReflectApply,
|
||||
} = primordials;
|
||||
|
||||
@ -53,12 +54,15 @@ class AsyncLocalStorage {
|
||||
}
|
||||
|
||||
run(data, fn, ...args) {
|
||||
const prior = AsyncContextFrame.current();
|
||||
const prior = this.getStore();
|
||||
if (ObjectIs(prior, data)) {
|
||||
return ReflectApply(fn, null, args);
|
||||
}
|
||||
this.enterWith(data);
|
||||
try {
|
||||
return ReflectApply(fn, null, args);
|
||||
} finally {
|
||||
AsyncContextFrame.set(prior);
|
||||
this.enterWith(prior);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
'use strict';
|
||||
// Flags: --expose_gc
|
||||
// Flags: --expose_gc --expose-internals
|
||||
|
||||
// This test ensures that AsyncLocalStorage gets gced once it was disabled
|
||||
// and no strong references remain in userland.
|
||||
|
||||
const common = require('../common');
|
||||
const { AsyncLocalStorage } = require('async_hooks');
|
||||
const AsyncContextFrame = require('internal/async_context_frame');
|
||||
const { onGC } = require('../common/gc');
|
||||
|
||||
let asyncLocalStorage = new AsyncLocalStorage();
|
||||
@ -16,5 +17,11 @@ asyncLocalStorage.run({}, () => {
|
||||
onGC(asyncLocalStorage, { ongc: common.mustCall() });
|
||||
});
|
||||
|
||||
if (AsyncContextFrame.enabled) {
|
||||
// This disable() is needed to remove reference form AsyncContextFrame
|
||||
// created during exit of run() to the AsyncLocalStore instance.
|
||||
asyncLocalStorage.disable();
|
||||
}
|
||||
|
||||
asyncLocalStorage = null;
|
||||
global.gc();
|
||||
|
67
test/parallel/test-async-local-storage-isolation.js
Normal file
67
test/parallel/test-async-local-storage-isolation.js
Normal file
@ -0,0 +1,67 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const { AsyncLocalStorage } = require('node:async_hooks');
|
||||
const assert = require('node:assert');
|
||||
|
||||
// Verify that ALS instances are independent of each other.
|
||||
|
||||
{
|
||||
// Verify als2.enterWith() and als2.run inside als1.run()
|
||||
const als1 = new AsyncLocalStorage();
|
||||
const als2 = new AsyncLocalStorage();
|
||||
|
||||
assert.strictEqual(als1.getStore(), undefined);
|
||||
assert.strictEqual(als2.getStore(), undefined);
|
||||
|
||||
als1.run('store1', common.mustCall(() => {
|
||||
assert.strictEqual(als1.getStore(), 'store1');
|
||||
assert.strictEqual(als2.getStore(), undefined);
|
||||
|
||||
als2.run('store2', common.mustCall(() => {
|
||||
assert.strictEqual(als1.getStore(), 'store1');
|
||||
assert.strictEqual(als2.getStore(), 'store2');
|
||||
}));
|
||||
assert.strictEqual(als1.getStore(), 'store1');
|
||||
assert.strictEqual(als2.getStore(), undefined);
|
||||
|
||||
als2.enterWith('store3');
|
||||
assert.strictEqual(als1.getStore(), 'store1');
|
||||
assert.strictEqual(als2.getStore(), 'store3');
|
||||
}));
|
||||
|
||||
assert.strictEqual(als1.getStore(), undefined);
|
||||
assert.strictEqual(als2.getStore(), 'store3');
|
||||
}
|
||||
|
||||
{
|
||||
// Verify als1.disable() has no side effects to als2 and als3
|
||||
const als1 = new AsyncLocalStorage();
|
||||
const als2 = new AsyncLocalStorage();
|
||||
const als3 = new AsyncLocalStorage();
|
||||
|
||||
als3.enterWith('store3');
|
||||
|
||||
als1.run('store1', common.mustCall(() => {
|
||||
assert.strictEqual(als1.getStore(), 'store1');
|
||||
assert.strictEqual(als2.getStore(), undefined);
|
||||
assert.strictEqual(als3.getStore(), 'store3');
|
||||
|
||||
als2.run('store2', common.mustCall(() => {
|
||||
assert.strictEqual(als1.getStore(), 'store1');
|
||||
assert.strictEqual(als2.getStore(), 'store2');
|
||||
assert.strictEqual(als3.getStore(), 'store3');
|
||||
|
||||
als1.disable();
|
||||
assert.strictEqual(als1.getStore(), undefined);
|
||||
assert.strictEqual(als2.getStore(), 'store2');
|
||||
assert.strictEqual(als3.getStore(), 'store3');
|
||||
}));
|
||||
assert.strictEqual(als1.getStore(), undefined);
|
||||
assert.strictEqual(als2.getStore(), undefined);
|
||||
assert.strictEqual(als3.getStore(), 'store3');
|
||||
}));
|
||||
|
||||
assert.strictEqual(als1.getStore(), undefined);
|
||||
assert.strictEqual(als2.getStore(), undefined);
|
||||
assert.strictEqual(als3.getStore(), 'store3');
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user