Skip to content
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

async_hooks: introduce mandatory store argument in AsyncLocalStorage run methods #31930

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions benchmark/async_hooks/async-resource-vs-destroy.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ function buildDestroy(getServe) {
function buildAsyncLocalStorage(getServe) {
const asyncLocalStorage = new AsyncLocalStorage();
const server = createServer((req, res) => {
asyncLocalStorage.runSyncAndReturn(() => {
asyncLocalStorage.runSyncAndReturn({}, () => {
getServe(getCLS, setCLS)(req, res);
});
});
Expand All @@ -118,12 +118,12 @@ function buildAsyncLocalStorage(getServe) {

function getCLS() {
const store = asyncLocalStorage.getStore();
return store.get('store');
return store.state;
}

function setCLS(state) {
const store = asyncLocalStorage.getStore();
store.set('store', state);
store.state = state;
}

function close() {
Expand Down
46 changes: 25 additions & 21 deletions doc/api/async_hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,7 @@ function log(...args) {
}

http.createServer((request, response) => {
asyncLocalStorage.run(() => {
asyncLocalStorage.run(new Map(), () => {
const store = asyncLocalStorage.getStore();
store.set(kReq, request);
someAsyncOperation((err, result) => {
Expand Down Expand Up @@ -943,27 +943,27 @@ in the current process.
added: REPLACEME
-->

* Returns: {Map}
* Returns: {any}

This method returns the current store.
If this method is called outside of an asynchronous context initialized by
calling `asyncLocalStorage.run` or `asyncLocalStorage.runAndReturn`, it will
return `undefined`.

### `asyncLocalStorage.run(callback[, ...args])`
### `asyncLocalStorage.run(store, callback[, ...args])`
<!-- YAML
added: REPLACEME
-->

* `store` {any}
* `callback` {Function}
* `...args` {any}

Calling `asyncLocalStorage.run(callback)` will create a new asynchronous
context.
Within the callback function and the asynchronous operations from the callback,
`asyncLocalStorage.getStore()` will return an instance of `Map` known as
"the store". This store will be persistent through the following
asynchronous calls.
context. Within the callback function and the asynchronous operations from
the callback, `asyncLocalStorage.getStore()` will return the object or
the primitive value passed into the `store` argument (known as "the store").
This store will be persistent through the following asynchronous calls.

The callback will be ran asynchronously. Optionally, arguments can be passed
to the function. They will be passed to the callback function.
Expand All @@ -975,10 +975,11 @@ Also, the stacktrace will be impacted by the asynchronous call.
Example:

```js
asyncLocalStorage.run(() => {
asyncLocalStorage.getStore(); // Returns a Map
const store = { id: 1 };
asyncLocalStorage.run(store, () => {
asyncLocalStorage.getStore(); // Returns the store object
someAsyncOperation(() => {
asyncLocalStorage.getStore(); // Returns the same Map
asyncLocalStorage.getStore(); // Returns the same object
});
});
asyncLocalStorage.getStore(); // Returns undefined
Expand Down Expand Up @@ -1007,20 +1008,21 @@ Also, the stacktrace will be impacted by the asynchronous call.
Example:

```js
asyncLocalStorage.run(() => {
asyncLocalStorage.getStore(); // Returns a Map
asyncLocalStorage.run('store value', () => {
asyncLocalStorage.getStore(); // Returns 'store value'
asyncLocalStorage.exit(() => {
asyncLocalStorage.getStore(); // Returns undefined
});
asyncLocalStorage.getStore(); // Returns the same Map
asyncLocalStorage.getStore(); // Returns 'store value'
});
```

### `asyncLocalStorage.runSyncAndReturn(callback[, ...args])`
### `asyncLocalStorage.runSyncAndReturn(store, callback[, ...args])`
<!-- YAML
added: REPLACEME
-->

* `store` {any}
* `callback` {Function}
* `...args` {any}

Expand All @@ -1038,9 +1040,10 @@ the context will be exited.
Example:

```js
const store = { id: 2 };
try {
asyncLocalStorage.runSyncAndReturn(() => {
asyncLocalStorage.getStore(); // Returns a Map
asyncLocalStorage.runSyncAndReturn(store, () => {
asyncLocalStorage.getStore(); // Returns the store object
throw new Error();
});
} catch (e) {
Expand Down Expand Up @@ -1073,13 +1076,13 @@ Example:
```js
// Within a call to run or runSyncAndReturn
try {
asyncLocalStorage.getStore(); // Returns a Map
asyncLocalStorage.getStore(); // Returns the store object or value
asyncLocalStorage.exitSyncAndReturn(() => {
asyncLocalStorage.getStore(); // Returns undefined
throw new Error();
});
} catch (e) {
asyncLocalStorage.getStore(); // Returns the same Map
asyncLocalStorage.getStore(); // Returns the same object or value
// The error will be caught here
}
```
Expand All @@ -1105,8 +1108,9 @@ It cannot be promisified using `util.promisify`. If needed, the `Promise`
constructor can be used:

```js
const store = new Map(); // initialize the store
new Promise((resolve, reject) => {
asyncLocalStorage.run(() => {
asyncLocalStorage.run(store, () => {
someFunction((err, result) => {
if (err) {
return reject(err);
Expand Down Expand Up @@ -1135,7 +1139,7 @@ the following pattern should be used:

```js
async function fn() {
await asyncLocalStorage.runSyncAndReturn(() => {
await asyncLocalStorage.runSyncAndReturn(new Map(), () => {
asyncLocalStorage.getStore().set('key', value);
return foo(); // The return value of foo will be awaited
});
Expand Down
14 changes: 6 additions & 8 deletions lib/async_hooks.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
'use strict';

const {
Map,
NumberIsSafeInteger,
ReflectApply,
Symbol,

} = primordials;

const {
Expand Down Expand Up @@ -247,14 +245,14 @@ class AsyncLocalStorage {
}
}

_enter() {
_enter(store) {
if (!this.enabled) {
this.enabled = true;
storageList.push(this);
storageHook.enable();
}
const resource = executionAsyncResource();
resource[this.kResourceStore] = new Map();
resource[this.kResourceStore] = store;
}

_exit() {
Expand All @@ -264,8 +262,8 @@ class AsyncLocalStorage {
}
}

runSyncAndReturn(callback, ...args) {
this._enter();
runSyncAndReturn(store, callback, ...args) {
Qard marked this conversation as resolved.
Show resolved Hide resolved
this._enter(store);
try {
return callback(...args);
} finally {
Expand All @@ -289,8 +287,8 @@ class AsyncLocalStorage {
}
}

run(callback, ...args) {
this._enter();
run(store, callback, ...args) {
this._enter(store);
process.nextTick(callback, ...args);
this._exit();
}
Expand Down
4 changes: 2 additions & 2 deletions test/async-hooks/test-async-local-storage-args.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ const { AsyncLocalStorage } = require('async_hooks');

const asyncLocalStorage = new AsyncLocalStorage();

asyncLocalStorage.run((runArg) => {
asyncLocalStorage.run({}, (runArg) => {
assert.strictEqual(runArg, 1);
asyncLocalStorage.exit((exitArg) => {
assert.strictEqual(exitArg, 2);
}, 2);
}, 1);

asyncLocalStorage.runSyncAndReturn((runArg) => {
asyncLocalStorage.runSyncAndReturn({}, (runArg) => {
assert.strictEqual(runArg, 'foo');
asyncLocalStorage.exitSyncAndReturn((exitArg) => {
assert.strictEqual(exitArg, 'bar');
Expand Down
2 changes: 1 addition & 1 deletion test/async-hooks/test-async-local-storage-async-await.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ async function test() {
}

async function main() {
await asyncLocalStorage.runSyncAndReturn(test);
await asyncLocalStorage.runSyncAndReturn(new Map(), test);
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ async function testAwait() {
await asyncLocalStorage.exitSyncAndReturn(testOut);
}

asyncLocalStorage.run(() => {
asyncLocalStorage.run(new Map(), () => {
const store = asyncLocalStorage.getStore();
store.set('key', 'value');
testAwait(); // should not reject
Expand Down
4 changes: 2 additions & 2 deletions test/async-hooks/test-async-local-storage-enable-disable.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ const { AsyncLocalStorage } = require('async_hooks');

const asyncLocalStorage = new AsyncLocalStorage();

asyncLocalStorage.runSyncAndReturn(() => {
asyncLocalStorage.runSyncAndReturn(new Map(), () => {
asyncLocalStorage.getStore().set('foo', 'bar');
process.nextTick(() => {
assert.strictEqual(asyncLocalStorage.getStore().get('foo'), 'bar');
asyncLocalStorage.disable();
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
process.nextTick(() => {
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
asyncLocalStorage.runSyncAndReturn(() => {
asyncLocalStorage.runSyncAndReturn(new Map(), () => {
assert.notStrictEqual(asyncLocalStorage.getStore(), undefined);
});
});
Expand Down
2 changes: 1 addition & 1 deletion test/async-hooks/test-async-local-storage-errors-async.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ process.setUncaughtExceptionCaptureCallback((err) => {
assert.strictEqual(asyncLocalStorage.getStore().get('hello'), 'node');
});

asyncLocalStorage.run(() => {
asyncLocalStorage.run(new Map(), () => {
const store = asyncLocalStorage.getStore();
store.set('hello', 'node');
setTimeout(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ process.setUncaughtExceptionCaptureCallback((err) => {
});

try {
asyncLocalStorage.runSyncAndReturn(() => {
asyncLocalStorage.runSyncAndReturn(new Map(), () => {
const store = asyncLocalStorage.getStore();
store.set('hello', 'node');
setTimeout(() => {
Expand Down
2 changes: 1 addition & 1 deletion test/async-hooks/test-async-local-storage-http.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const server = http.createServer((req, res) => {
});

server.listen(0, () => {
asyncLocalStorage.run(() => {
asyncLocalStorage.run(new Map(), () => {
const store = asyncLocalStorage.getStore();
store.set('hello', 'world');
http.get({ host: 'localhost', port: server.address().port }, () => {
Expand Down
24 changes: 24 additions & 0 deletions test/async-hooks/test-async-local-storage-misc-stores.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict';
require('../common');
const assert = require('assert');
const { AsyncLocalStorage } = require('async_hooks');

const asyncLocalStorage = new AsyncLocalStorage();

asyncLocalStorage.run(42, () => {
assert.strictEqual(asyncLocalStorage.getStore(), 42);
});

const runStore = { foo: 'bar' };
asyncLocalStorage.run(runStore, () => {
assert.strictEqual(asyncLocalStorage.getStore(), runStore);
});

asyncLocalStorage.runSyncAndReturn('hello node', () => {
assert.strictEqual(asyncLocalStorage.getStore(), 'hello node');
});

const runSyncStore = { hello: 'node' };
asyncLocalStorage.runSyncAndReturn(runSyncStore, () => {
assert.strictEqual(asyncLocalStorage.getStore(), runSyncStore);
});
4 changes: 2 additions & 2 deletions test/async-hooks/test-async-local-storage-nested.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ const { AsyncLocalStorage } = require('async_hooks');
const asyncLocalStorage = new AsyncLocalStorage();

setTimeout(() => {
asyncLocalStorage.run(() => {
asyncLocalStorage.run(new Map(), () => {
const asyncLocalStorage2 = new AsyncLocalStorage();
asyncLocalStorage2.run(() => {
asyncLocalStorage2.run(new Map(), () => {
const store = asyncLocalStorage.getStore();
const store2 = asyncLocalStorage2.getStore();
store.set('hello', 'world');
Expand Down
6 changes: 3 additions & 3 deletions test/async-hooks/test-async-local-storage-no-mix-contexts.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ const asyncLocalStorage = new AsyncLocalStorage();
const asyncLocalStorage2 = new AsyncLocalStorage();

setTimeout(() => {
asyncLocalStorage.run(() => {
asyncLocalStorage2.run(() => {
asyncLocalStorage.run(new Map(), () => {
asyncLocalStorage2.run(new Map(), () => {
const store = asyncLocalStorage.getStore();
const store2 = asyncLocalStorage2.getStore();
store.set('hello', 'world');
Expand All @@ -28,7 +28,7 @@ setTimeout(() => {
}, 100);

setTimeout(() => {
asyncLocalStorage.run(() => {
asyncLocalStorage.run(new Map(), () => {
const store = asyncLocalStorage.getStore();
store.set('hello', 'earth');
setTimeout(() => {
Expand Down
2 changes: 1 addition & 1 deletion test/async-hooks/test-async-local-storage-promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ async function main() {
throw err;
});
await new Promise((resolve, reject) => {
asyncLocalStorage.run(() => {
asyncLocalStorage.run(new Map(), () => {
const store = asyncLocalStorage.getStore();
store.set('a', 1);
next().then(resolve, reject);
Expand Down