lib: document nextTick queue internals

Make this code (a bit more) comprehensible by adding some
internals docs.

With diagrams and everything! 🎉

PR-URL: https://github.com/nodejs/node/pull/19469
Reviewed-By: Anatoli Papirovski <apapirovski@mac.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Weijia Wang <starkwang@126.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Reviewed-By: Gus Caplan <me@gus.host>
This commit is contained in:
Anna Henningsen
2018-03-20 02:51:01 +01:00
parent 2d94f77fd3
commit 34a948fc0b

View File

@@ -31,10 +31,60 @@ function setupNextTick() {
const kHasScheduled = 0;
const kHasPromiseRejections = 1;
// Queue size for each tick array. Must be a factor of two.
// Queue size for each tick array. Must be a power of two.
const kQueueSize = 2048;
const kQueueMask = kQueueSize - 1;
// The next tick queue is implemented as a singly-linked list of fixed-size
// circular buffers. It looks something like this:
//
// head tail
// | |
// v v
// +-----------+ <-----\ +-----------+ <------\ +-----------+
// | [null] | \----- | next | \------- | next |
// +-----------+ +-----------+ +-----------+
// | tick | <-- bottom | tick | <-- bottom | [empty] |
// | tick | | tick | | [empty] |
// | tick | | tick | | [empty] |
// | tick | | tick | | [empty] |
// | tick | | tick | bottom --> | tick |
// | tick | | tick | | tick |
// | ... | | ... | | ... |
// | tick | | tick | | tick |
// | tick | | tick | | tick |
// | [empty] | <-- top | tick | | tick |
// | [empty] | | tick | | tick |
// | [empty] | | tick | | tick |
// +-----------+ +-----------+ <-- top top --> +-----------+
//
// Or, if there is only one fixed-size queue, it looks something
// like either of these:
//
// head tail head tail
// | | | |
// v v v v
// +-----------+ +-----------+
// | [null] | | [null] |
// +-----------+ +-----------+
// | [empty] | | tick |
// | [empty] | | tick |
// | tick | <-- bottom top --> | [empty] |
// | tick | | [empty] |
// | [empty] | <-- top bottom --> | tick |
// | [empty] | | tick |
// +-----------+ +-----------+
//
// Adding a value means moving `top` forward by one, removing means
// moving `bottom` forward by one.
//
// We let `bottom` and `top` wrap around, so when `top` is conceptually
// pointing to the end of the list, that means that the actual value is `0`.
//
// In particular, when `top === bottom`, this can mean *either* that the
// current queue is empty or that it is full. We can differentiate by
// checking whether an entry in the queue is empty (a.k.a. `=== undefined`).
class FixedQueue {
constructor() {
this.bottom = 0;
@@ -49,11 +99,12 @@ function setupNextTick() {
}
shift() {
const next = this.list[this.bottom];
if (next === undefined) return null;
const nextItem = this.list[this.bottom];
if (nextItem === undefined)
return null;
this.list[this.bottom] = undefined;
this.bottom = (this.bottom + 1) & kQueueMask;
return next;
return nextItem;
}
}
@@ -62,21 +113,34 @@ function setupNextTick() {
function push(data) {
if (head.bottom === head.top) {
if (head.list[head.top] !== undefined)
// Either empty or full:
if (head.list[head.top] !== undefined) {
// It's full: Creates a new queue, sets the old queue's `.next` to it,
// and sets it as the new main queue.
head = head.next = new FixedQueue();
else
} else {
// If the head is empty, that means that it was the only fixed-sized
// queue in existence.
DCHECK_EQ(head.next, null);
// This is the first tick object in existence, so we need to inform
// the C++ side that we do want to run `_tickCallback()`.
tickInfo[kHasScheduled] = 1;
}
}
head.push(data);
}
function shift() {
const next = tail.shift();
if (tail.top === tail.bottom) {
if (tail.next)
if (tail.top === tail.bottom) { // -> .shift() emptied the current queue.
if (tail.next !== null) {
// If there is another queue, it forms the new tail.
tail = tail.next;
else
} else {
// We've just run out of items. Let the native side know that it
// doesn't need to bother calling into JS to run the queue.
tickInfo[kHasScheduled] = 0;
}
}
return next;
}