Skip to content

Commit efa62fd

Browse files
committed
node: add AsyncListener support
AsyncListener is a JS API that works in tandem with the AsyncWrap class to allow the user to be alerted to key events in the life cycle of an asynchronous event. The AsyncWrap class has its own MakeCallback implementation that core will be migrated to use, and uses state sharing techniques to allow quicker communication between JS and C++ whether the async event callbacks need to be called.
1 parent 21fbbd5 commit efa62fd

26 files changed

Lines changed: 1594 additions & 200 deletions

lib/timers.js

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ var TIMEOUT_MAX = 2147483647; // 2^31-1
3030

3131
var debug = require('util').debuglog('timer');
3232

33+
var asyncFlags = process._asyncFlags;
34+
var runAsyncQueue = process._runAsyncQueue;
35+
var loadAsyncQueue = process._loadAsyncQueue;
36+
var unloadAsyncQueue = process._unloadAsyncQueue;
37+
38+
// Do a little housekeeping.
39+
delete process._asyncFlags;
40+
delete process._runAsyncQueue;
41+
delete process._loadAsyncQueue;
42+
delete process._unloadAsyncQueue;
43+
3344

3445
// IDLE TIMEOUTS
3546
//
@@ -44,6 +55,9 @@ var debug = require('util').debuglog('timer');
4455
// value = list
4556
var lists = {};
4657

58+
// Make Timer as monomorphic as possible.
59+
Timer.prototype._asyncQueue = undefined;
60+
4761
// the main function - creates lists on demand and the watchers associated
4862
// with them.
4963
function insert(item, msecs) {
@@ -80,9 +94,9 @@ function listOnTimeout() {
8094
var now = Timer.now();
8195
debug('now: %s', now);
8296

83-
var first;
97+
var diff, first, hasQueue, threw;
8498
while (first = L.peek(list)) {
85-
var diff = now - first._idleStart;
99+
diff = now - first._idleStart;
86100
if (diff < msecs) {
87101
list.start(msecs - diff, 0);
88102
debug('%d list wait because diff is %d', msecs, diff);
@@ -99,12 +113,20 @@ function listOnTimeout() {
99113
//
100114
// https://github.com/joyent/node/issues/2631
101115
var domain = first.domain;
102-
if (domain && domain._disposed) continue;
116+
if (domain && domain._disposed)
117+
continue;
118+
119+
hasQueue = !!first._asyncQueue;
120+
103121
try {
122+
if (hasQueue)
123+
loadAsyncQueue(first);
104124
if (domain)
105125
domain.enter();
106-
var threw = true;
126+
threw = true;
107127
first._onTimeout();
128+
if (hasQueue)
129+
unloadAsyncQueue(first);
108130
if (domain)
109131
domain.exit();
110132
threw = false;
@@ -162,7 +184,6 @@ exports.enroll = function(item, msecs) {
162184
exports.active = function(item) {
163185
var msecs = item._idleTimeout;
164186
if (msecs >= 0) {
165-
166187
var list = lists[msecs];
167188
if (!list || L.isEmpty(list)) {
168189
insert(item, msecs);
@@ -171,6 +192,11 @@ exports.active = function(item) {
171192
L.append(list, item);
172193
}
173194
}
195+
// Whether or not a new TimerWrap needed to be created, this should run
196+
// for each item. This way each "item" (i.e. timer) can properly have
197+
// their own domain assigned.
198+
if (asyncFlags[0] > 0)
199+
runAsyncQueue(item);
174200
};
175201

176202

@@ -316,16 +342,43 @@ L.init(immediateQueue);
316342

317343
function processImmediate() {
318344
var queue = immediateQueue;
345+
var domain, hasQueue, immediate;
319346

320347
immediateQueue = {};
321348
L.init(immediateQueue);
322349

323350
while (L.isEmpty(queue) === false) {
324-
var immediate = L.shift(queue);
325-
var domain = immediate.domain;
326-
if (domain) domain.enter();
327-
immediate._onImmediate();
328-
if (domain) domain.exit();
351+
immediate = L.shift(queue);
352+
hasQueue = !!immediate._asyncQueue;
353+
domain = immediate.domain;
354+
355+
if (hasQueue)
356+
loadAsyncQueue(immediate);
357+
if (domain)
358+
domain.enter();
359+
360+
var threw = true;
361+
try {
362+
immediate._onImmediate();
363+
threw = false;
364+
} finally {
365+
if (threw) {
366+
if (!L.isEmpty(queue)) {
367+
// Handle any remaining on next tick, assuming we're still
368+
// alive to do so.
369+
while (!L.isEmpty(immediateQueue)) {
370+
L.append(queue, L.shift(immediateQueue));
371+
}
372+
immediateQueue = queue;
373+
process.nextTick(processImmediate);
374+
}
375+
}
376+
}
377+
378+
if (hasQueue)
379+
unloadAsyncQueue(immediate);
380+
if (domain)
381+
domain.exit();
329382
}
330383

331384
// Only round-trip to C++ land if we have to. Calling clearImmediate() on an
@@ -357,7 +410,11 @@ exports.setImmediate = function(callback) {
357410
process._immediateCallback = processImmediate;
358411
}
359412

360-
if (process.domain) immediate.domain = process.domain;
413+
// setImmediates are handled more like nextTicks.
414+
if (asyncFlags[0] > 0)
415+
runAsyncQueue(immediate);
416+
if (process.domain)
417+
immediate.domain = process.domain;
361418

362419
L.append(immediateQueue, immediate);
363420

@@ -389,9 +446,10 @@ function unrefTimeout() {
389446

390447
debug('unrefTimer fired');
391448

392-
var first;
449+
var diff, domain, first, hasQueue, threw;
393450
while (first = L.peek(unrefList)) {
394-
var diff = now - first._idleStart;
451+
diff = now - first._idleStart;
452+
hasQueue = !!first._asyncQueue;
395453

396454
if (diff < first._idleTimeout) {
397455
diff = first._idleTimeout - diff;
@@ -403,17 +461,21 @@ function unrefTimeout() {
403461

404462
L.remove(first);
405463

406-
var domain = first.domain;
464+
domain = first.domain;
407465

408466
if (!first._onTimeout) continue;
409467
if (domain && domain._disposed) continue;
410468

411469
try {
470+
if (hasQueue)
471+
loadAsyncQueue(first);
412472
if (domain) domain.enter();
413-
var threw = true;
473+
threw = true;
414474
debug('unreftimer firing timeout');
415475
first._onTimeout();
416476
threw = false;
477+
if (hasQueue)
478+
unloadAsyncQueue(first);
417479
if (domain) domain.exit();
418480
} finally {
419481
if (threw) process.nextTick(unrefTimeout);

node.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@
115115
'src/udp_wrap.cc',
116116
'src/uv.cc',
117117
# headers to make for a more pleasant IDE experience
118+
'src/async-wrap.h',
119+
'src/async-wrap-inl.h',
118120
'src/env.h',
119121
'src/env-inl.h',
120122
'src/handle_wrap.h',

0 commit comments

Comments
 (0)