mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user