Skip to content

Commit 2e33ad1

Browse files
joyeecheungBridgeAR
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 fa74cd3 commit 2e33ad1

13 files changed

Lines changed: 199 additions & 192 deletions

‎lib/internal/bootstrap/node.js‎

Lines 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
constexceptionHandlerState={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
constcredentials=internalBinding('credentials');
111117
if(credentials.implementsPosixCredentials){
112118
process.getuid=credentials.getuid;

‎lib/internal/process/next_tick.js‎

Lines 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-
functionsetupNextTick(_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-
constemitPromiseRejectionWarnings=
18-
require('internal/process/promises').setup(_setupPromises);
19-
const{ERR_INVALID_CALLBACK}=require('internal/errors').codes;
20-
constFixedQueue=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-
constkHasScheduled=0;
33-
constkHasPromiseRejections=1;
34-
35-
constqueue=newFixedQueue();
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-
functioninternalTickCallback(){
51-
lettock;
52-
do{
53-
while(tock=queue.shift()){
54-
constasyncId=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-
constcallback=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+
constFixedQueue=require('internal/fixed_queue');
31+
32+
// *Must* match Environment::TickInfo::Fields in src/env.h.
33+
constkHasScheduled=0;
34+
constkHasPromiseRejections=1;
35+
36+
constqueue=newFixedQueue();
37+
38+
functionrunNextTicks(){
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-
classTickObject{
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-
constasyncId=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+
functioninternalTickCallback(){
48+
lettock;
49+
do{
50+
while(tock=queue.shift()){
51+
constasyncId=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+
constcallback=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-
functionnextTick(callback){
106-
if(typeofcallback!=='function')
107-
thrownewERR_INVALID_CALLBACK();
108-
109-
if(process._exiting)
110-
return;
111-
112-
varargs;
113-
switch(arguments.length){
114-
case1: break;
115-
case2: args=[arguments[1]];break;
116-
case3: args=[arguments[1],arguments[2]];break;
117-
case4: args=[arguments[1],arguments[2],arguments[3]];break;
118-
default:
119-
args=newArray(arguments.length-1);
120-
for(vari=1;i<arguments.length;i++)
121-
args[i-1]=arguments[i];
79+
classTickObject{
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+
constasyncId=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(newTickObject(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+
functionnextTick(callback){
103+
if(typeofcallback!=='function')
104+
thrownewERR_INVALID_CALLBACK();
105+
106+
if(process._exiting)
107+
return;
108+
109+
varargs;
110+
switch(arguments.length){
111+
case1: break;
112+
case2: args=[arguments[1]];break;
113+
case3: args=[arguments[1],arguments[2]];break;
114+
case4: args=[arguments[1],arguments[2],arguments[3]];break;
115+
default:
116+
args=newArray(arguments.length-1);
117+
for(vari=1;i<arguments.length;i++)
118+
args[i-1]=arguments[i];
127119
}
120+
121+
if(queue.isEmpty())
122+
tickInfo[kHasScheduled]=1;
123+
queue.push(newTickObject(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+
};

‎lib/internal/process/promises.js‎

Lines 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
constmaybeUnhandledPromises=newWeakMap();
69
constpendingUnhandledRejections=[];
710
constasyncHandledRejections=[];
8-
constpromiseRejectEvents={};
911
letlastPromiseId=0;
1012

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

‎node.gyp‎

Lines 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',

‎src/env.h‎

Lines 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
classEnvironment;
383383

‎src/node.cc‎

Lines 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)

‎src/node_binding.cc‎

Lines 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)