Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4a7233c

Browse filesBrowse files
trevnorrisaddaleax
authored andcommitted
lib: implement async_hooks API in core
Implement async_hooks support in the following: * fatalException handler * process.nextTick * Timers * net/dgram/http PR-URL: #12892 Ref: #11883 Ref: #8531 Reviewed-By: Andreas Madsen <amwebdk@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Sam Roberts <vieuxtech@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
1 parent 7e3a3c9 commit 4a7233c
Copy full SHA for 4a7233c

File tree

Expand file treeCollapse file tree

13 files changed

+399
-40
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

13 files changed

+399
-40
lines changed
Open diff view settings
Collapse file

‎lib/_http_agent.js‎

Copy file name to clipboardExpand all lines: lib/_http_agent.js
+7-2Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ const net = require('net');
2525
const util = require('util');
2626
const EventEmitter = require('events');
2727
const debug = util.debuglog('http');
28+
const async_id_symbol = process.binding('async_wrap').async_id_symbol;
29+
const nextTick = require('internal/process/next_tick').nextTick;
2830

2931
// New Agent code.
3032

@@ -93,6 +95,7 @@ function Agent(options) {
9395
self.freeSockets[name] = freeSockets;
9496
socket.setKeepAlive(true, self.keepAliveMsecs);
9597
socket.unref();
98+
socket[async_id_symbol] = -1;
9699
socket._httpMessage = null;
97100
self.removeSocket(socket, options);
98101
freeSockets.push(socket);
@@ -163,6 +166,8 @@ Agent.prototype.addRequest = function addRequest(req, options, port/*legacy*/,
163166
if (freeLen) {
164167
// we have a free socket, so use that.
165168
var socket = this.freeSockets[name].shift();
169+
// Assign the handle a new asyncId and run any init() hooks.
170+
socket._handle.asyncReset();
166171
debug('have free socket');
167172

168173
// don't leak
@@ -177,7 +182,7 @@ Agent.prototype.addRequest = function addRequest(req, options, port/*legacy*/,
177182
// If we are under maxSockets create a new one.
178183
this.createSocket(req, options, function(err, newSocket) {
179184
if (err) {
180-
process.nextTick(function() {
185+
nextTick(newSocket._handle.getAsyncId(), function() {
181186
req.emit('error', err);
182187
});
183188
return;
@@ -290,7 +295,7 @@ Agent.prototype.removeSocket = function removeSocket(s, options) {
290295
// If we have pending requests and a socket gets closed make a new one
291296
this.createSocket(req, options, function(err, newSocket) {
292297
if (err) {
293-
process.nextTick(function() {
298+
nextTick(newSocket._handle.getAsyncId(), function() {
294299
req.emit('error', err);
295300
});
296301
return;
Collapse file

‎lib/_http_client.js‎

Copy file name to clipboardExpand all lines: lib/_http_client.js
+5-1Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const Agent = require('_http_agent');
3636
const Buffer = require('buffer').Buffer;
3737
const urlToOptions = require('internal/url').urlToOptions;
3838
const outHeadersKey = require('internal/http').outHeadersKey;
39+
const nextTick = require('internal/process/next_tick').nextTick;
3940

4041
// The actual list of disallowed characters in regexp form is more like:
4142
// /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
@@ -587,9 +588,12 @@ function responseKeepAlive(res, req) {
587588
socket.removeListener('close', socketCloseListener);
588589
socket.removeListener('error', socketErrorListener);
589590
socket.once('error', freeSocketErrorListener);
591+
// There are cases where _handle === null. Avoid those. Passing null to
592+
// nextTick() will call initTriggerId() to retrieve the id.
593+
const asyncId = socket._handle ? socket._handle.getAsyncId() : null;
590594
// Mark this socket as available, AFTER user-added end
591595
// handlers have a chance to run.
592-
process.nextTick(emitFreeNT, socket);
596+
nextTick(asyncId, emitFreeNT, socket);
593597
}
594598
}
595599

Collapse file

‎lib/_http_common.js‎

Copy file name to clipboardExpand all lines: lib/_http_common.js
+7-1Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const HTTPParser = binding.HTTPParser;
2828
const FreeList = require('internal/freelist');
2929
const ondrain = require('internal/http').ondrain;
3030
const incoming = require('_http_incoming');
31+
const emitDestroy = require('async_hooks').emitDestroy;
3132
const IncomingMessage = incoming.IncomingMessage;
3233
const readStart = incoming.readStart;
3334
const readStop = incoming.readStop;
@@ -211,8 +212,13 @@ function freeParser(parser, req, socket) {
211212
parser.incoming = null;
212213
parser.outgoing = null;
213214
parser[kOnExecute] = null;
214-
if (parsers.free(parser) === false)
215+
if (parsers.free(parser) === false) {
215216
parser.close();
217+
} else {
218+
// Since the Parser destructor isn't going to run the destroy() callbacks
219+
// it needs to be triggered manually.
220+
emitDestroy(parser.getAsyncId());
221+
}
216222
}
217223
if (req) {
218224
req.parser = null;
Collapse file

‎lib/_http_outgoing.js‎

Copy file name to clipboardExpand all lines: lib/_http_outgoing.js
+9-3Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ const common = require('_http_common');
3131
const checkIsHttpToken = common._checkIsHttpToken;
3232
const checkInvalidHeaderChar = common._checkInvalidHeaderChar;
3333
const outHeadersKey = require('internal/http').outHeadersKey;
34+
const async_id_symbol = process.binding('async_wrap').async_id_symbol;
35+
const nextTick = require('internal/process/next_tick').nextTick;
3436

3537
const CRLF = common.CRLF;
3638
const debug = common.debug;
@@ -264,8 +266,9 @@ function _writeRaw(data, encoding, callback) {
264266
if (this.output.length) {
265267
this._flushOutput(conn);
266268
} else if (!data.length) {
267-
if (typeof callback === 'function')
268-
process.nextTick(callback);
269+
if (typeof callback === 'function') {
270+
nextTick(this.socket[async_id_symbol], callback);
271+
}
269272
return true;
270273
}
271274
// Directly write to socket.
@@ -623,7 +626,10 @@ const crlf_buf = Buffer.from('\r\n');
623626
OutgoingMessage.prototype.write = function write(chunk, encoding, callback) {
624627
if (this.finished) {
625628
var err = new Error('write after end');
626-
process.nextTick(writeAfterEndNT.bind(this), err, callback);
629+
nextTick(this.socket[async_id_symbol],
630+
writeAfterEndNT.bind(this),
631+
err,
632+
callback);
627633

628634
return true;
629635
}
Collapse file

‎lib/async_hooks.js‎

Copy file name to clipboardExpand all lines: lib/async_hooks.js
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ var processing_hook = false;
3232
// Use to temporarily store and updated active_hooks_array if the user enables
3333
// or disables a hook while hooks are being processed.
3434
var tmp_active_hooks_array = null;
35-
// Keep track of the field counds held in tmp_active_hooks_array.
35+
// Keep track of the field counts held in tmp_active_hooks_array.
3636
var tmp_async_hook_fields = null;
3737

3838
// Each constant tracks how many callbacks there are for any given step of
@@ -41,9 +41,9 @@ var tmp_async_hook_fields = null;
4141
const { kInit, kBefore, kAfter, kDestroy, kCurrentAsyncId, kCurrentTriggerId,
4242
kAsyncUidCntr, kInitTriggerId } = async_wrap.constants;
4343

44+
const { async_id_symbol, trigger_id_symbol } = async_wrap;
45+
4446
// Used in AsyncHook and AsyncEvent.
45-
const async_id_symbol = Symbol('_asyncId');
46-
const trigger_id_symbol = Symbol('_triggerId');
4747
const init_symbol = Symbol('init');
4848
const before_symbol = Symbol('before');
4949
const after_symbol = Symbol('after');
Collapse file

‎lib/dgram.js‎

Copy file name to clipboardExpand all lines: lib/dgram.js
+10-2Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ const assert = require('assert');
2525
const Buffer = require('buffer').Buffer;
2626
const util = require('util');
2727
const EventEmitter = require('events');
28+
const setInitTriggerId = require('async_hooks').setInitTriggerId;
2829
const UV_UDP_REUSEADDR = process.binding('constants').os.UV_UDP_REUSEADDR;
30+
const async_id_symbol = process.binding('async_wrap').async_id_symbol;
31+
const nextTick = require('internal/process/next_tick').nextTick;
2932

3033
const UDP = process.binding('udp_wrap').UDP;
3134
const SendWrap = process.binding('udp_wrap').SendWrap;
@@ -111,6 +114,7 @@ function Socket(type, listener) {
111114
this._handle = handle;
112115
this._receiving = false;
113116
this._bindState = BIND_STATE_UNBOUND;
117+
this[async_id_symbol] = this._handle.getAsyncId();
114118
this.type = type;
115119
this.fd = null; // compatibility hack
116120

@@ -432,6 +436,10 @@ function doSend(ex, self, ip, list, address, port, callback) {
432436
req.callback = callback;
433437
req.oncomplete = afterSend;
434438
}
439+
// node::SendWrap isn't instantiated and attached to the JS instance of
440+
// SendWrap above until send() is called. So don't set the init trigger id
441+
// until now.
442+
setInitTriggerId(self[async_id_symbol]);
435443
var err = self._handle.send(req,
436444
list,
437445
list.length,
@@ -441,7 +449,7 @@ function doSend(ex, self, ip, list, address, port, callback) {
441449
if (err && callback) {
442450
// don't emit as error, dgram_legacy.js compatibility
443451
const ex = exceptionWithHostPort(err, 'send', address, port);
444-
process.nextTick(callback, ex);
452+
nextTick(self[async_id_symbol], callback, ex);
445453
}
446454
}
447455

@@ -468,7 +476,7 @@ Socket.prototype.close = function(callback) {
468476
this._stopReceiving();
469477
this._handle.close();
470478
this._handle = null;
471-
process.nextTick(socketCloseNT, this);
479+
nextTick(this[async_id_symbol], socketCloseNT, this);
472480

473481
return this;
474482
};
Collapse file

‎lib/internal/bootstrap_node.js‎

Copy file name to clipboardExpand all lines: lib/internal/bootstrap_node.js
+23-1Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,10 +292,20 @@
292292
}
293293

294294
function setupProcessFatal() {
295+
const async_wrap = process.binding('async_wrap');
296+
// Arrays containing hook flags and ids for async_hook calls.
297+
const { async_hook_fields, async_uid_fields } = async_wrap;
298+
// Internal functions needed to manipulate the stack.
299+
const { clearIdStack, popAsyncIds } = async_wrap;
300+
const { kAfter, kCurrentAsyncId, kInitTriggerId } = async_wrap.constants;
295301

296302
process._fatalException = function(er) {
297303
var caught;
298304

305+
// It's possible that kInitTriggerId was set for a constructor call that
306+
// threw and was never cleared. So clear it now.
307+
async_uid_fields[kInitTriggerId] = 0;
308+
299309
if (process.domain && process.domain._errorHandler)
300310
caught = process.domain._errorHandler(er);
301311

@@ -314,9 +324,21 @@
314324
// nothing to be done about it at this point.
315325
}
316326

317-
// if we handled an error, then make sure any ticks get processed
318327
} else {
328+
// If we handled an error, then make sure any ticks get processed
319329
NativeModule.require('timers').setImmediate(process._tickCallback);
330+
331+
// Emit the after() hooks now that the exception has been handled.
332+
if (async_hook_fields[kAfter] > 0) {
333+
do {
334+
NativeModule.require('async_hooks').emitAfter(
335+
async_uid_fields[kCurrentAsyncId]);
336+
// popAsyncIds() returns true if there are more ids on the stack.
337+
} while (popAsyncIds(async_uid_fields[kCurrentAsyncId]));
338+
// Or completely empty the id stack.
339+
} else {
340+
clearIdStack();
341+
}
320342
}
321343

322344
return caught;

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.