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 8abcc76

Browse filesBrowse files
joyeecheungaddaleax
authored andcommitted
src: move process.nextTick and promise setup into node_task_queue.cc
This patch: - Moves the process.nextTick and promise setup C++ code into node_task_queue.cc which is exposed as `internalBinding('task_queue')` - Makes `lib/internal/process/promises.js` and `lib/internal/process/next_tick.js` as side-effect-free as possible - Removes the bootstrapper object being passed into `bootstrap/node.js`, let `next_tick.js` and `promises.js` load whatever they need from `internalBinding('task_queue')` instead. - Rename `process._tickCallback` to `runNextTicks` internally for clarity but still expose it as `process._tickCallback`. PR-URL: #25163 Refs: #24961 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Anatoli Papirovski <apapirovski@mac.com>
1 parent 733af61 commit 8abcc76
Copy full SHA for 8abcc76

File tree

Expand file treeCollapse file tree

13 files changed

+199
-192
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

13 files changed

+199
-192
lines changed
Open diff view settings
Collapse file

‎lib/internal/bootstrap/node.js‎

Copy file name to clipboardExpand all lines: lib/internal/bootstrap/node.js
+13-7Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,9 @@
1414

1515
// This file is compiled as if it's wrapped in a function with arguments
1616
// passed by node::LoadEnvironment()
17-
/* global process, bootstrappers, loaderExports, triggerFatalException */
17+
/* global process, loaderExports, triggerFatalException */
1818
/* global isMainThread */
1919

20-
const {
21-
_setupNextTick,
22-
_setupPromises
23-
} = bootstrappers;
2420
const { internalBinding, NativeModule } = loaderExports;
2521

2622
const exceptionHandlerState = { captureFn: null };
@@ -105,8 +101,18 @@ function startup() {
105101
}
106102

107103
NativeModule.require('internal/process/warning').setup();
108-
NativeModule.require('internal/process/next_tick').setup(_setupNextTick,
109-
_setupPromises);
104+
const {
105+
nextTick,
106+
runNextTicks
107+
} = NativeModule.require('internal/process/next_tick').setup();
108+
109+
process.nextTick = nextTick;
110+
// Used to emulate a tick manually in the JS land.
111+
// A better name for this function would be `runNextTicks` but
112+
// it has been exposed to the process object so we keep this legacy name
113+
// TODO(joyeecheung): either remove it or make it public
114+
process._tickCallback = runNextTicks;
115+
110116
const credentials = internalBinding('credentials');
111117
if (credentials.implementsPosixCredentials) {
112118
process.getuid = credentials.getuid;
Collapse file

‎lib/internal/process/next_tick.js‎

Copy file name to clipboard
+129-119Lines changed: 129 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,138 @@
11
'use strict';
22

3-
exports.setup = setupNextTick;
4-
5-
function setupNextTick(_setupNextTick, _setupPromises) {
6-
const {
7-
getDefaultTriggerAsyncId,
8-
newAsyncId,
9-
initHooksExist,
10-
destroyHooksExist,
11-
emitInit,
12-
emitBefore,
13-
emitAfter,
14-
emitDestroy,
15-
symbols: { async_id_symbol, trigger_async_id_symbol }
16-
} = require('internal/async_hooks');
17-
const emitPromiseRejectionWarnings =
18-
require('internal/process/promises').setup(_setupPromises);
19-
const { ERR_INVALID_CALLBACK } = require('internal/errors').codes;
20-
const FixedQueue = require('internal/fixed_queue');
21-
22-
// tickInfo is used so that the C++ code in src/node.cc can
23-
// have easy access to our nextTick state, and avoid unnecessary
24-
// calls into JS land.
25-
// runMicrotasks is used to run V8's micro task queue.
26-
const [
27-
tickInfo,
28-
runMicrotasks
29-
] = _setupNextTick(internalTickCallback);
30-
31-
// *Must* match Environment::TickInfo::Fields in src/env.h.
32-
const kHasScheduled = 0;
33-
const kHasPromiseRejections = 1;
34-
35-
const queue = new FixedQueue();
36-
37-
process.nextTick = nextTick;
38-
// Needs to be accessible from beyond this scope.
39-
process._tickCallback = _tickCallback;
40-
41-
function _tickCallback() {
42-
if (tickInfo[kHasScheduled] === 0 && tickInfo[kHasPromiseRejections] === 0)
43-
runMicrotasks();
44-
if (tickInfo[kHasScheduled] === 0 && tickInfo[kHasPromiseRejections] === 0)
45-
return;
46-
47-
internalTickCallback();
48-
}
49-
50-
function internalTickCallback() {
51-
let tock;
52-
do {
53-
while (tock = queue.shift()) {
54-
const asyncId = tock[async_id_symbol];
55-
emitBefore(asyncId, tock[trigger_async_id_symbol]);
56-
// emitDestroy() places the async_id_symbol into an asynchronous queue
57-
// that calls the destroy callback in the future. It's called before
58-
// calling tock.callback so destroy will be called even if the callback
59-
// throws an exception that is handled by 'uncaughtException' or a
60-
// domain.
61-
// TODO(trevnorris): This is a bit of a hack. It relies on the fact
62-
// that nextTick() doesn't allow the event loop to proceed, but if
63-
// any async hooks are enabled during the callback's execution then
64-
// this tock's after hook will be called, but not its destroy hook.
65-
if (destroyHooksExist())
66-
emitDestroy(asyncId);
67-
68-
const callback = tock.callback;
69-
if (tock.args === undefined)
70-
callback();
71-
else
72-
Reflect.apply(callback, undefined, tock.args);
73-
74-
emitAfter(asyncId);
75-
}
76-
tickInfo[kHasScheduled] = 0;
77-
runMicrotasks();
78-
} while (!queue.isEmpty() || emitPromiseRejectionWarnings());
79-
tickInfo[kHasPromiseRejections] = 0;
80-
}
3+
const {
4+
// For easy access to the nextTick state in the C++ land,
5+
// and to avoid unnecessary calls into JS land.
6+
tickInfo,
7+
// Used to run V8's micro task queue.
8+
runMicrotasks,
9+
setTickCallback,
10+
initializePromiseRejectCallback
11+
} = internalBinding('task_queue');
12+
13+
const {
14+
promiseRejectHandler,
15+
emitPromiseRejectionWarnings
16+
} = require('internal/process/promises');
17+
18+
const {
19+
getDefaultTriggerAsyncId,
20+
newAsyncId,
21+
initHooksExist,
22+
destroyHooksExist,
23+
emitInit,
24+
emitBefore,
25+
emitAfter,
26+
emitDestroy,
27+
symbols: { async_id_symbol, trigger_async_id_symbol }
28+
} = require('internal/async_hooks');
29+
const { ERR_INVALID_CALLBACK } = require('internal/errors').codes;
30+
const FixedQueue = require('internal/fixed_queue');
31+
32+
// *Must* match Environment::TickInfo::Fields in src/env.h.
33+
const kHasScheduled = 0;
34+
const kHasPromiseRejections = 1;
35+
36+
const queue = new FixedQueue();
37+
38+
function runNextTicks() {
39+
if (tickInfo[kHasScheduled] === 0 && tickInfo[kHasPromiseRejections] === 0)
40+
runMicrotasks();
41+
if (tickInfo[kHasScheduled] === 0 && tickInfo[kHasPromiseRejections] === 0)
42+
return;
43+
44+
internalTickCallback();
45+
}
8146

82-
class TickObject {
83-
constructor(callback, args, triggerAsyncId) {
84-
// This must be set to null first to avoid function tracking
85-
// on the hidden class, revisit in V8 versions after 6.2
86-
this.callback = null;
87-
this.callback = callback;
88-
this.args = args;
89-
90-
const asyncId = newAsyncId();
91-
this[async_id_symbol] = asyncId;
92-
this[trigger_async_id_symbol] = triggerAsyncId;
93-
94-
if (initHooksExist()) {
95-
emitInit(asyncId,
96-
'TickObject',
97-
triggerAsyncId,
98-
this);
99-
}
47+
function internalTickCallback() {
48+
let tock;
49+
do {
50+
while (tock = queue.shift()) {
51+
const asyncId = tock[async_id_symbol];
52+
emitBefore(asyncId, tock[trigger_async_id_symbol]);
53+
// emitDestroy() places the async_id_symbol into an asynchronous queue
54+
// that calls the destroy callback in the future. It's called before
55+
// calling tock.callback so destroy will be called even if the callback
56+
// throws an exception that is handled by 'uncaughtException' or a
57+
// domain.
58+
// TODO(trevnorris): This is a bit of a hack. It relies on the fact
59+
// that nextTick() doesn't allow the event loop to proceed, but if
60+
// any async hooks are enabled during the callback's execution then
61+
// this tock's after hook will be called, but not its destroy hook.
62+
if (destroyHooksExist())
63+
emitDestroy(asyncId);
64+
65+
const callback = tock.callback;
66+
if (tock.args === undefined)
67+
callback();
68+
else
69+
Reflect.apply(callback, undefined, tock.args);
70+
71+
emitAfter(asyncId);
10072
}
101-
}
73+
tickInfo[kHasScheduled] = 0;
74+
runMicrotasks();
75+
} while (!queue.isEmpty() || emitPromiseRejectionWarnings());
76+
tickInfo[kHasPromiseRejections] = 0;
77+
}
10278

103-
// `nextTick()` will not enqueue any callback when the process is about to
104-
// exit since the callback would not have a chance to be executed.
105-
function nextTick(callback) {
106-
if (typeof callback !== 'function')
107-
throw new ERR_INVALID_CALLBACK();
108-
109-
if (process._exiting)
110-
return;
111-
112-
var args;
113-
switch (arguments.length) {
114-
case 1: break;
115-
case 2: args = [arguments[1]]; break;
116-
case 3: args = [arguments[1], arguments[2]]; break;
117-
case 4: args = [arguments[1], arguments[2], arguments[3]]; break;
118-
default:
119-
args = new Array(arguments.length - 1);
120-
for (var i = 1; i < arguments.length; i++)
121-
args[i - 1] = arguments[i];
79+
class TickObject {
80+
constructor(callback, args, triggerAsyncId) {
81+
// This must be set to null first to avoid function tracking
82+
// on the hidden class, revisit in V8 versions after 6.2
83+
this.callback = null;
84+
this.callback = callback;
85+
this.args = args;
86+
87+
const asyncId = newAsyncId();
88+
this[async_id_symbol] = asyncId;
89+
this[trigger_async_id_symbol] = triggerAsyncId;
90+
91+
if (initHooksExist()) {
92+
emitInit(asyncId,
93+
'TickObject',
94+
triggerAsyncId,
95+
this);
12296
}
97+
}
98+
}
12399

124-
if (queue.isEmpty())
125-
tickInfo[kHasScheduled] = 1;
126-
queue.push(new TickObject(callback, args, getDefaultTriggerAsyncId()));
100+
// `nextTick()` will not enqueue any callback when the process is about to
101+
// exit since the callback would not have a chance to be executed.
102+
function nextTick(callback) {
103+
if (typeof callback !== 'function')
104+
throw new ERR_INVALID_CALLBACK();
105+
106+
if (process._exiting)
107+
return;
108+
109+
var args;
110+
switch (arguments.length) {
111+
case 1: break;
112+
case 2: args = [arguments[1]]; break;
113+
case 3: args = [arguments[1], arguments[2]]; break;
114+
case 4: args = [arguments[1], arguments[2], arguments[3]]; break;
115+
default:
116+
args = new Array(arguments.length - 1);
117+
for (var i = 1; i < arguments.length; i++)
118+
args[i - 1] = arguments[i];
127119
}
120+
121+
if (queue.isEmpty())
122+
tickInfo[kHasScheduled] = 1;
123+
queue.push(new TickObject(callback, args, getDefaultTriggerAsyncId()));
128124
}
125+
126+
// TODO(joyeecheung): make this a factory class so that node.js can
127+
// control the side effects caused by the initializers.
128+
exports.setup = function() {
129+
// Initializes the per-isolate promise rejection callback which
130+
// will call the handler being passed into this function.
131+
initializePromiseRejectCallback(promiseRejectHandler);
132+
// Sets the callback to be run in every tick.
133+
setTickCallback(internalTickCallback);
134+
return {
135+
nextTick,
136+
runNextTicks
137+
};
138+
};
Collapse file

‎lib/internal/process/promises.js‎

Copy file name to clipboardExpand all lines: lib/internal/process/promises.js
+9-9Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
'use strict';
22

33
const { safeToString } = internalBinding('util');
4+
const {
5+
promiseRejectEvents
6+
} = internalBinding('task_queue');
47

58
const maybeUnhandledPromises = new WeakMap();
69
const pendingUnhandledRejections = [];
710
const asyncHandledRejections = [];
8-
const promiseRejectEvents = {};
911
let lastPromiseId = 0;
1012

11-
exports.setup = setupPromises;
12-
13-
function setupPromises(_setupPromises) {
14-
_setupPromises(handler, promiseRejectEvents);
15-
return emitPromiseRejectionWarnings;
16-
}
17-
18-
function handler(type, promise, reason) {
13+
function promiseRejectHandler(type, promise, reason) {
1914
switch (type) {
2015
case promiseRejectEvents.kPromiseRejectWithNoHandler:
2116
return unhandledRejection(promise, reason);
@@ -124,3 +119,8 @@ function emitPromiseRejectionWarnings() {
124119
}
125120
return maybeScheduledTicks || pendingUnhandledRejections.length !== 0;
126121
}
122+
123+
module.exports = {
124+
promiseRejectHandler,
125+
emitPromiseRejectionWarnings
126+
};
Collapse file

‎node.gyp‎

Copy file name to clipboardExpand all lines: node.gyp
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,6 @@
327327

328328
'sources': [
329329
'src/async_wrap.cc',
330-
'src/bootstrapper.cc',
331330
'src/callback_scope.cc',
332331
'src/cares_wrap.cc',
333332
'src/connect_wrap.cc',
@@ -374,6 +373,7 @@
374373
'src/node_serdes.cc',
375374
'src/node_stat_watcher.cc',
376375
'src/node_symbols.cc',
376+
'src/node_task_queue.cc',
377377
'src/node_trace_events.cc',
378378
'src/node_types.cc',
379379
'src/node_url.cc',
Collapse file

‎src/env.h‎

Copy file name to clipboardExpand all lines: src/env.h
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
362362
V(performance_entry_template, v8::Function) \
363363
V(pipe_constructor_template, v8::FunctionTemplate) \
364364
V(process_object, v8::Object) \
365-
V(promise_handler_function, v8::Function) \
365+
V(promise_reject_callback, v8::Function) \
366366
V(promise_wrap_template, v8::ObjectTemplate) \
367367
V(sab_lifetimepartner_constructor_template, v8::FunctionTemplate) \
368368
V(script_context_constructor_template, v8::FunctionTemplate) \
@@ -377,7 +377,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
377377
V(tty_constructor_template, v8::FunctionTemplate) \
378378
V(udp_constructor_function, v8::Function) \
379379
V(url_constructor_function, v8::Function) \
380-
V(write_wrap_template, v8::ObjectTemplate) \
380+
V(write_wrap_template, v8::ObjectTemplate)
381381

382382
class Environment;
383383

Collapse file

‎src/node.cc‎

Copy file name to clipboardExpand all lines: src/node.cc
-6Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,20 +1163,14 @@ void LoadEnvironment(Environment* env) {
11631163
return;
11641164
}
11651165

1166-
// Bootstrap Node.js
1167-
Local<Object> bootstrapper = Object::New(env->isolate());
1168-
SetupBootstrapObject(env, bootstrapper);
1169-
11701166
// process, bootstrappers, loaderExports, triggerFatalException
11711167
std::vector<Local<String>> node_params = {
11721168
env->process_string(),
1173-
FIXED_ONE_BYTE_STRING(isolate, "bootstrappers"),
11741169
FIXED_ONE_BYTE_STRING(isolate, "loaderExports"),
11751170
FIXED_ONE_BYTE_STRING(isolate, "triggerFatalException"),
11761171
FIXED_ONE_BYTE_STRING(isolate, "isMainThread")};
11771172
std::vector<Local<Value>> node_args = {
11781173
process,
1179-
bootstrapper,
11801174
loader_exports.ToLocalChecked(),
11811175
env->NewFunctionTemplate(FatalException)
11821176
->GetFunction(context)
Collapse file

‎src/node_binding.cc‎

Copy file name to clipboardExpand all lines: src/node_binding.cc
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
V(stream_wrap) \
5353
V(string_decoder) \
5454
V(symbols) \
55+
V(task_queue) \
5556
V(tcp_wrap) \
5657
V(timers) \
5758
V(trace_events) \

0 commit comments

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