mirror of
https://github.com/nodejs/node.git
synced 2025-08-15 13:48:44 +02:00
lib: rewrite AsyncLocalStorage without async_hooks
PR-URL: https://github.com/nodejs/node/pull/48528 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com> Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de>
This commit is contained in:
parent
0c1877a82a
commit
d1229eeca4
29 changed files with 658 additions and 173 deletions
|
@ -10,7 +10,6 @@ const {
|
|||
NumberIsSafeInteger,
|
||||
ObjectDefineProperties,
|
||||
ObjectFreeze,
|
||||
ObjectIs,
|
||||
ReflectApply,
|
||||
Symbol,
|
||||
} = primordials;
|
||||
|
@ -30,6 +29,8 @@ const {
|
|||
} = require('internal/validators');
|
||||
const internal_async_hooks = require('internal/async_hooks');
|
||||
|
||||
const AsyncContextFrame = require('internal/async_context_frame');
|
||||
|
||||
// Get functions
|
||||
// For userland AsyncResources, make sure to emit a destroy event when the
|
||||
// resource gets gced.
|
||||
|
@ -158,6 +159,7 @@ function createHook(fns) {
|
|||
// Embedder API //
|
||||
|
||||
const destroyedSymbol = Symbol('destroyed');
|
||||
const contextFrameSymbol = Symbol('context_frame');
|
||||
|
||||
class AsyncResource {
|
||||
constructor(type, opts = kEmptyObject) {
|
||||
|
@ -177,6 +179,8 @@ class AsyncResource {
|
|||
throw new ERR_INVALID_ASYNC_ID('triggerAsyncId', triggerAsyncId);
|
||||
}
|
||||
|
||||
this[contextFrameSymbol] = AsyncContextFrame.current();
|
||||
|
||||
const asyncId = newAsyncId();
|
||||
this[async_id_symbol] = asyncId;
|
||||
this[trigger_async_id_symbol] = triggerAsyncId;
|
||||
|
@ -201,12 +205,12 @@ class AsyncResource {
|
|||
const asyncId = this[async_id_symbol];
|
||||
emitBefore(asyncId, this[trigger_async_id_symbol], this);
|
||||
|
||||
const contextFrame = this[contextFrameSymbol];
|
||||
const prior = AsyncContextFrame.exchange(contextFrame);
|
||||
try {
|
||||
const ret =
|
||||
ReflectApply(fn, thisArg, args);
|
||||
|
||||
return ret;
|
||||
return ReflectApply(fn, thisArg, args);
|
||||
} finally {
|
||||
AsyncContextFrame.set(prior);
|
||||
if (hasAsyncIdStack())
|
||||
emitAfter(asyncId);
|
||||
}
|
||||
|
@ -270,110 +274,15 @@ class AsyncResource {
|
|||
}
|
||||
}
|
||||
|
||||
const storageList = [];
|
||||
const storageHook = createHook({
|
||||
init(asyncId, type, triggerAsyncId, resource) {
|
||||
const currentResource = executionAsyncResource();
|
||||
// Value of currentResource is always a non null object
|
||||
for (let i = 0; i < storageList.length; ++i) {
|
||||
storageList[i]._propagate(resource, currentResource, type);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
class AsyncLocalStorage {
|
||||
constructor() {
|
||||
this.kResourceStore = Symbol('kResourceStore');
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
static bind(fn) {
|
||||
return AsyncResource.bind(fn);
|
||||
}
|
||||
|
||||
static snapshot() {
|
||||
return AsyncLocalStorage.bind((cb, ...args) => cb(...args));
|
||||
}
|
||||
|
||||
disable() {
|
||||
if (this.enabled) {
|
||||
this.enabled = false;
|
||||
// If this.enabled, the instance must be in storageList
|
||||
ArrayPrototypeSplice(storageList,
|
||||
ArrayPrototypeIndexOf(storageList, this), 1);
|
||||
if (storageList.length === 0) {
|
||||
storageHook.disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_enable() {
|
||||
if (!this.enabled) {
|
||||
this.enabled = true;
|
||||
ArrayPrototypePush(storageList, this);
|
||||
storageHook.enable();
|
||||
}
|
||||
}
|
||||
|
||||
// Propagate the context from a parent resource to a child one
|
||||
_propagate(resource, triggerResource, type) {
|
||||
const store = triggerResource[this.kResourceStore];
|
||||
if (this.enabled) {
|
||||
resource[this.kResourceStore] = store;
|
||||
}
|
||||
}
|
||||
|
||||
enterWith(store) {
|
||||
this._enable();
|
||||
const resource = executionAsyncResource();
|
||||
resource[this.kResourceStore] = store;
|
||||
}
|
||||
|
||||
run(store, callback, ...args) {
|
||||
// Avoid creation of an AsyncResource if store is already active
|
||||
if (ObjectIs(store, this.getStore())) {
|
||||
return ReflectApply(callback, null, args);
|
||||
}
|
||||
|
||||
this._enable();
|
||||
|
||||
const resource = executionAsyncResource();
|
||||
const oldStore = resource[this.kResourceStore];
|
||||
|
||||
resource[this.kResourceStore] = store;
|
||||
|
||||
try {
|
||||
return ReflectApply(callback, null, args);
|
||||
} finally {
|
||||
resource[this.kResourceStore] = oldStore;
|
||||
}
|
||||
}
|
||||
|
||||
exit(callback, ...args) {
|
||||
if (!this.enabled) {
|
||||
return ReflectApply(callback, null, args);
|
||||
}
|
||||
this.disable();
|
||||
try {
|
||||
return ReflectApply(callback, null, args);
|
||||
} finally {
|
||||
this._enable();
|
||||
}
|
||||
}
|
||||
|
||||
getStore() {
|
||||
if (this.enabled) {
|
||||
const resource = executionAsyncResource();
|
||||
return resource[this.kResourceStore];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Placing all exports down here because the exported classes won't export
|
||||
// otherwise.
|
||||
module.exports = {
|
||||
// Public API
|
||||
AsyncLocalStorage,
|
||||
get AsyncLocalStorage() {
|
||||
return AsyncContextFrame.enabled ?
|
||||
require('internal/async_local_storage/native') :
|
||||
require('internal/async_local_storage/async_hooks');
|
||||
},
|
||||
createHook,
|
||||
executionAsyncId,
|
||||
triggerAsyncId,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue