Skip to content
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
74 changes: 20 additions & 54 deletions lib/async_hooks.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
'use strict';

const internalUtil = require('internal/util');
const async_wrap = process.binding('async_wrap');
/* Both these arrays are used to communicate between JS and C++ with as little
* overhead as possible.
*
* async_hook_fields is a Uint32Array() that communicates the number of each
* type of active hooks of each type and wraps the uin32_t array of
* node::Environment::AsyncHooks::fields_.
*
* async_uid_fields is a Float64Array() that contains the async/trigger ids for
* several operations. These fields are as follows:
* kCurrentAsyncId: The async id of the current execution stack.
* kCurrentTriggerId: The trigger id of the current execution stack.
* kAsyncUidCntr: Counter that tracks the unique ids given to new resources.
* kInitTriggerId: Written to just before creating a new resource, so the
* constructor knows what other resource is responsible for its init().
* Used this way so the trigger id doesn't need to be passed to every
* resource's constructor.
*/
const { async_hook_fields, async_uid_fields } = async_wrap;
const {
async_wrap,
async_hook_fields,
async_uid_fields,
internalJSConstants,
initTriggerId,
resetTriggerId,
executionAsyncId,
triggerAsyncId
} = require('internal/async_hooks');

// Used to change the state of the async id stack.
const { pushAsyncIds, popAsyncIds } = async_wrap;
// Array of all AsyncHooks that will be iterated whenever an async event fires.
Expand All @@ -39,8 +31,14 @@ var tmp_async_hook_fields = null;
// Each constant tracks how many callbacks there are for any given step of
// async execution. These are tracked so if the user didn't include callbacks
// for a given step, that step can bail out early.
const { kInit, kBefore, kAfter, kDestroy, kTotals, kCurrentAsyncId,
kCurrentTriggerId, kAsyncUidCntr, kInitTriggerId } = async_wrap.constants;
const {
kInit,
kBefore,
kAfter,
kDestroy,
kTotals,
kAsyncUidCntr,
} = internalJSConstants;

const { async_id_symbol, trigger_id_symbol } = async_wrap;

Expand Down Expand Up @@ -194,16 +192,6 @@ function createHook(fns) {
}


function executionAsyncId() {
return async_uid_fields[kCurrentAsyncId];
}


function triggerAsyncId() {
return async_uid_fields[kCurrentTriggerId];
}


// Embedder API //

class AsyncResource {
Expand Down Expand Up @@ -278,27 +266,6 @@ function newUid() {
}


// Return the triggerAsyncId meant for the constructor calling it. It's up to
// the user to safeguard this call and make sure it's zero'd out when the
// constructor is complete.
function initTriggerId() {
var tId = async_uid_fields[kInitTriggerId];
// Reset value after it's been called so the next constructor doesn't
// inherit it by accident.
async_uid_fields[kInitTriggerId] = 0;
if (tId <= 0)
tId = async_uid_fields[kCurrentAsyncId];
return tId;
}


function setInitTriggerId(triggerAsyncId) {
// CHECK(Number.isSafeInteger(triggerAsyncId))
// CHECK(triggerAsyncId > 0)
async_uid_fields[kInitTriggerId] = triggerAsyncId;
}


function emitInitScript(asyncId, type, triggerAsyncId, resource) {
// Short circuit all checks for the common case. Which is that no hooks have
// been set. Do this to remove performance impact for embedders (and core).
Expand All @@ -313,7 +280,7 @@ function emitInitScript(asyncId, type, triggerAsyncId, resource) {
triggerAsyncId = initTriggerId();
} else {
// If a triggerAsyncId was passed, any kInitTriggerId still must be null'd.
async_uid_fields[kInitTriggerId] = 0;
resetTriggerId();
}

// TODO(trevnorris): I'd prefer allowing these checks to not exist, or only
Expand Down Expand Up @@ -455,7 +422,6 @@ module.exports = {
// Sensitive Embedder API
newUid,
initTriggerId,
setInitTriggerId,
emitInit: emitInitScript,
emitBefore: emitBeforeScript,
emitAfter: emitAfterScript,
Expand Down
20 changes: 12 additions & 8 deletions lib/dgram.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const errors = require('internal/errors');
const Buffer = require('buffer').Buffer;
const util = require('util');
const EventEmitter = require('events');
const setInitTriggerId = require('async_hooks').setInitTriggerId;
const { triggerIdScopeSync } = require('internal/async_hooks');
const UV_UDP_REUSEADDR = process.binding('constants').os.UV_UDP_REUSEADDR;
const async_id_symbol = process.binding('async_wrap').async_id_symbol;
const nextTick = require('internal/process/next_tick').nextTick;
Expand Down Expand Up @@ -455,13 +455,17 @@ function doSend(ex, self, ip, list, address, port, callback) {
// node::SendWrap isn't instantiated and attached to the JS instance of
// SendWrap above until send() is called. So don't set the init trigger id
// until now.
setInitTriggerId(self[async_id_symbol]);
var err = self._handle.send(req,
list,
list.length,
port,
ip,
!!callback);
const err = triggerIdScopeSync(
self[async_id_symbol],
() => self._handle.send(
req,
list,
list.length,
port,
ip,
!!callback
)
);
if (err && callback) {
// don't emit as error, dgram_legacy.js compatibility
const ex = exceptionWithHostPort(err, 'send', address, port);
Expand Down
99 changes: 99 additions & 0 deletions lib/internal/async_hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
'use strict';

const async_wrap = process.binding('async_wrap');

/* Both these arrays are used to communicate between JS and C++ with as little
* overhead as possible.
*
* async_hook_fields is a Uint32Array() that communicates the number of each
* type of active hooks of each type and wraps the uin32_t array of
* node::Environment::AsyncHooks::fields_.
*
* async_uid_fields is a Float64Array() that contains the async/trigger ids for
* several operations. These fields are as follows:
* kCurrentAsyncId: The async id of the current execution stack.
* kCurrentTriggerId: The trigger id of the current execution stack.
* kAsyncUidCntr: Counter that tracks the unique ids given to new resources.
* kInitTriggerId: Written to just before creating a new resource, so the
* constructor knows what other resource is responsible for its init().
* Used this way so the trigger id doesn't need to be passed to every
* resource's constructor.
*/
const { async_hook_fields, async_uid_fields } = async_wrap;
const {
kCurrentTriggerId,
kCurrentAsyncId,
kInitTriggerId
} = async_wrap.constants;

const {
kInit,
kBefore,
kAfter,
kDestroy,
kTotals,
kAsyncUidCntr,
} = async_wrap.constants;

const internalJSConstants = {
kInit,
kBefore,
kAfter,
kDestroy,
kTotals,
kAsyncUidCntr,
};

// Return the triggerAsyncId meant for the constructor calling it. It's up to
// the user to safeguard this call and make sure it's zero'd out when the
// constructor is complete.
function initTriggerId() {
var tId = async_uid_fields[kInitTriggerId];
// Reset value after it's been called so the next constructor doesn't
// inherit it by accident.
async_uid_fields[kInitTriggerId] = 0;
if (tId <= 0)
tId = executionAsyncId();
return tId;
}


function resetTriggerId() {
async_uid_fields[kInitTriggerId] = 0;
}


function executionAsyncId() {
return async_uid_fields[kCurrentAsyncId];
}


function triggerAsyncId() {
return async_uid_fields[kCurrentTriggerId];
}


function triggerIdScopeSync(id, block) {
let old = async_uid_fields[kInitTriggerId];
async_uid_fields[kInitTriggerId] = id;
try {
const ret = block();
if (async_uid_fields[kInitTriggerId] !== id) old = null;
return ret;
} finally {
old && (async_uid_fields[kInitTriggerId] = old);
}
}


module.exports = {
async_wrap,
async_hook_fields,
async_uid_fields,
internalJSConstants,
initTriggerId,
resetTriggerId,
executionAsyncId,
triggerAsyncId,
triggerIdScopeSync,
};
45 changes: 26 additions & 19 deletions lib/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ const PipeConnectWrap = process.binding('pipe_wrap').PipeConnectWrap;
const ShutdownWrap = process.binding('stream_wrap').ShutdownWrap;
const WriteWrap = process.binding('stream_wrap').WriteWrap;
const async_id_symbol = process.binding('async_wrap').async_id_symbol;
const { newUid, setInitTriggerId } = require('async_hooks');
const { triggerIdScopeSync } = require('internal/async_hooks');
const { newUid } = require('async_hooks');
const nextTick = require('internal/process/next_tick').nextTick;
const errors = require('internal/errors');

Expand Down Expand Up @@ -290,10 +291,12 @@ function onSocketFinish() {
req.oncomplete = afterShutdown;
req.handle = this._handle;
// node::ShutdownWrap isn't instantiated and attached to the JS instance of
// ShutdownWrap above until shutdown() is called. So don't set the init
// ShutdownWrap above until shutdown() is called. So we don't set the init
// trigger id until now.
setInitTriggerId(this[async_id_symbol]);
var err = this._handle.shutdown(req);
const err = triggerIdScopeSync(
this[async_id_symbol],
() => this._handle.shutdown(req)
);

if (err)
return this.destroy(errnoException(err, 'shutdown'));
Expand Down Expand Up @@ -904,23 +907,24 @@ function internalConnect(
req.localPort = localPort;

// node::TCPConnectWrap isn't instantiated and attached to the JS instance
// of TCPConnectWrap above until connect() is called. So don't set the init
// trigger id until now.
setInitTriggerId(self[async_id_symbol]);
if (addressType === 4)
err = self._handle.connect(req, address, port);
else
err = self._handle.connect6(req, address, port);

// of TCPConnectWrap above until connect() is called. So we don't set the
// init triggerID until now.
const methodName = addressType === 4 ? 'connect' : 'connect6';
err = triggerIdScopeSync(
self[async_id_symbol],
() => self._handle[methodName](req, address, port)
);
} else {
const req = new PipeConnectWrap();
req.address = address;
req.oncomplete = afterConnect;
// node::PipeConnectWrap isn't instantiated and attached to the JS instance
// of PipeConnectWrap above until connect() is called. So don't set the
// init trigger id until now.
setInitTriggerId(self[async_id_symbol]);
err = self._handle.connect(req, address, afterConnect);
// of PipeConnectWrap above until connect() is called. So we don't set the
// init triggerID until now.
err = triggerIdScopeSync(
self[async_id_symbol],
() => self._handle.connect(req, address, afterConnect)
);
}

if (err) {
Expand Down Expand Up @@ -1051,8 +1055,7 @@ function lookupAndConnect(self, options) {
debug('connect: dns options', dnsopts);
self._host = host;
var lookup = options.lookup || dns.lookup;
setInitTriggerId(self[async_id_symbol]);
lookup(host, dnsopts, function emitLookup(err, ip, addressType) {
function emitLookup(err, ip, addressType) {
self.emit('lookup', err, ip, addressType, host);

// It's possible we were destroyed while looking this up.
Expand All @@ -1078,7 +1081,11 @@ function lookupAndConnect(self, options) {
localAddress,
localPort);
}
});
}
triggerIdScopeSync(
self[async_id_symbol],
() => lookup(host, dnsopts, emitLookup)
);
}


Expand Down
1 change: 1 addition & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
'lib/v8.js',
'lib/vm.js',
'lib/zlib.js',
'lib/internal/async_hooks.js',
'lib/internal/buffer.js',
'lib/internal/child_process.js',
'lib/internal/cluster/child.js',
Expand Down