Skip to content

Commit 255de69

Browse files
joyeecheungtargos
authored andcommitted
process: refactor global.queueMicrotask()
- Lazy load `async_hooks` in the implementation - Rename `process/next_tick.js` to `process/task_queues.js` and move the implementation of `global.queueMicrotask()` there since these methods are conceptually related to each other. - Move the bindings used by `global.queueMicrotask()` into `node_task_queue.cc` instead of the generic `node_util.cc` - Use `defineOperation` to define `global.queueMicrotask()` PR-URL: #26523 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 1481e5b commit 255de69

12 files changed

+89
-86
lines changed

lib/internal/bootstrap/node.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ process.emitWarning = emitWarning;
122122
const {
123123
nextTick,
124124
runNextTicks
125-
} = require('internal/process/next_tick').setup();
125+
} = require('internal/process/task_queues').setupTaskQueue();
126126

127127
process.nextTick = nextTick;
128128
// Used to emulate a tick manually in the JS land.
@@ -430,7 +430,7 @@ function setupQueueMicrotask() {
430430
get() {
431431
process.emitWarning('queueMicrotask() is experimental.',
432432
'ExperimentalWarning');
433-
const { queueMicrotask } = require('internal/queue_microtask');
433+
const { queueMicrotask } = require('internal/process/task_queues');
434434

435435
Object.defineProperty(global, 'queueMicrotask', {
436436
value: queueMicrotask,

lib/internal/modules/cjs/loader.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,7 @@ Module.runMain = function() {
862862
return loader.import(pathToFileURL(process.argv[1]).pathname);
863863
})
864864
.catch((e) => {
865-
internalBinding('util').triggerFatalException(e);
865+
internalBinding('task_queue').triggerFatalException(e);
866866
});
867867
} else {
868868
Module._load(process.argv[1], null, true);

lib/internal/process/next_tick.js lib/internal/process/task_queues.js

+55-13
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ const {
66
tickInfo,
77
// Used to run V8's micro task queue.
88
runMicrotasks,
9-
setTickCallback
9+
setTickCallback,
10+
enqueueMicrotask,
11+
triggerFatalException
1012
} = internalBinding('task_queue');
1113

1214
const {
@@ -27,7 +29,10 @@ const {
2729
emitDestroy,
2830
symbols: { async_id_symbol, trigger_async_id_symbol }
2931
} = require('internal/async_hooks');
30-
const { ERR_INVALID_CALLBACK } = require('internal/errors').codes;
32+
const {
33+
ERR_INVALID_CALLBACK,
34+
ERR_INVALID_ARG_TYPE
35+
} = require('internal/errors').codes;
3136
const FixedQueue = require('internal/fixed_queue');
3237

3338
// *Must* match Environment::TickInfo::Fields in src/env.h.
@@ -130,15 +135,52 @@ function nextTick(callback) {
130135
queue.push(new TickObject(callback, args, getDefaultTriggerAsyncId()));
131136
}
132137

133-
// TODO(joyeecheung): make this a factory class so that node.js can
134-
// control the side effects caused by the initializers.
135-
exports.setup = function() {
136-
// Sets the per-isolate promise rejection callback
137-
listenForRejections();
138-
// Sets the callback to be run in every tick.
139-
setTickCallback(processTicksAndRejections);
140-
return {
141-
nextTick,
142-
runNextTicks
143-
};
138+
let AsyncResource;
139+
function createMicrotaskResource() {
140+
// Lazy load the async_hooks module
141+
if (!AsyncResource) {
142+
AsyncResource = require('async_hooks').AsyncResource;
143+
}
144+
return new AsyncResource('Microtask', {
145+
triggerAsyncId: getDefaultTriggerAsyncId(),
146+
requireManualDestroy: true,
147+
});
148+
}
149+
150+
function queueMicrotask(callback) {
151+
if (typeof callback !== 'function') {
152+
throw new ERR_INVALID_ARG_TYPE('callback', 'function', callback);
153+
}
154+
155+
const asyncResource = createMicrotaskResource();
156+
157+
enqueueMicrotask(() => {
158+
asyncResource.runInAsyncScope(() => {
159+
try {
160+
callback();
161+
} catch (error) {
162+
// TODO(devsnek) remove this if
163+
// https://bugs.chromium.org/p/v8/issues/detail?id=8326
164+
// is resolved such that V8 triggers the fatal exception
165+
// handler for microtasks
166+
triggerFatalException(error);
167+
} finally {
168+
asyncResource.emitDestroy();
169+
}
170+
});
171+
});
172+
}
173+
174+
module.exports = {
175+
setupTaskQueue() {
176+
// Sets the per-isolate promise rejection callback
177+
listenForRejections();
178+
// Sets the callback to be run in every tick.
179+
setTickCallback(processTicksAndRejections);
180+
return {
181+
nextTick,
182+
runNextTicks
183+
};
184+
},
185+
queueMicrotask
144186
};

lib/internal/queue_microtask.js

-38
This file was deleted.

node.gyp

+1-2
Original file line numberDiff line numberDiff line change
@@ -162,16 +162,15 @@
162162
'lib/internal/process/esm_loader.js',
163163
'lib/internal/process/execution.js',
164164
'lib/internal/process/main_thread_only.js',
165-
'lib/internal/process/next_tick.js',
166165
'lib/internal/process/per_thread.js',
167166
'lib/internal/process/policy.js',
168167
'lib/internal/process/promises.js',
169168
'lib/internal/process/stdio.js',
170169
'lib/internal/process/warning.js',
171170
'lib/internal/process/worker_thread_only.js',
172171
'lib/internal/process/report.js',
172+
'lib/internal/process/task_queues.js',
173173
'lib/internal/querystring.js',
174-
'lib/internal/queue_microtask.js',
175174
'lib/internal/readline.js',
176175
'lib/internal/repl.js',
177176
'lib/internal/repl/await.js',

src/node_errors.cc

-12
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ using errors::TryCatchScope;
1313
using v8::Context;
1414
using v8::Exception;
1515
using v8::Function;
16-
using v8::FunctionCallbackInfo;
1716
using v8::HandleScope;
1817
using v8::Int32;
1918
using v8::Isolate;
@@ -752,15 +751,4 @@ void FatalException(Isolate* isolate,
752751
}
753752
}
754753

755-
void FatalException(const FunctionCallbackInfo<Value>& args) {
756-
Isolate* isolate = args.GetIsolate();
757-
Environment* env = Environment::GetCurrent(isolate);
758-
if (env != nullptr && env->abort_on_uncaught_exception()) {
759-
Abort();
760-
}
761-
Local<Value> exception = args[0];
762-
Local<Message> message = Exception::CreateMessage(isolate, exception);
763-
FatalException(isolate, exception, message);
764-
}
765-
766754
} // namespace node

src/node_errors.h

-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ void FatalException(v8::Isolate* isolate,
3131
v8::Local<v8::Value> error,
3232
v8::Local<v8::Message> message);
3333

34-
void FatalException(const v8::FunctionCallbackInfo<v8::Value>& args);
35-
3634
// Helpers to construct errors similar to the ones provided by
3735
// lib/internal/errors.js.
3836
// Example: with `V(ERR_INVALID_ARG_TYPE, TypeError)`, there will be

src/node_task_queue.cc

+25
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "env-inl.h"
22
#include "node.h"
3+
#include "node_errors.h"
34
#include "node_internals.h"
45
#include "v8.h"
56

@@ -9,6 +10,7 @@ namespace node {
910

1011
using v8::Array;
1112
using v8::Context;
13+
using v8::Exception;
1214
using v8::Function;
1315
using v8::FunctionCallbackInfo;
1416
using v8::Isolate;
@@ -17,6 +19,7 @@ using v8::kPromiseRejectAfterResolved;
1719
using v8::kPromiseRejectWithNoHandler;
1820
using v8::kPromiseResolveAfterResolved;
1921
using v8::Local;
22+
using v8::Message;
2023
using v8::Number;
2124
using v8::Object;
2225
using v8::Promise;
@@ -26,6 +29,15 @@ using v8::Value;
2629

2730
namespace task_queue {
2831

32+
static void EnqueueMicrotask(const FunctionCallbackInfo<Value>& args) {
33+
Environment* env = Environment::GetCurrent(args);
34+
Isolate* isolate = env->isolate();
35+
36+
CHECK(args[0]->IsFunction());
37+
38+
isolate->EnqueueMicrotask(args[0].As<Function>());
39+
}
40+
2941
static void RunMicrotasks(const FunctionCallbackInfo<Value>& args) {
3042
args.GetIsolate()->RunMicrotasks();
3143
}
@@ -95,13 +107,26 @@ static void SetPromiseRejectCallback(
95107
env->set_promise_reject_callback(args[0].As<Function>());
96108
}
97109

110+
static void TriggerFatalException(const FunctionCallbackInfo<Value>& args) {
111+
Isolate* isolate = args.GetIsolate();
112+
Environment* env = Environment::GetCurrent(isolate);
113+
if (env != nullptr && env->abort_on_uncaught_exception()) {
114+
Abort();
115+
}
116+
Local<Value> exception = args[0];
117+
Local<Message> message = Exception::CreateMessage(isolate, exception);
118+
FatalException(isolate, exception, message);
119+
}
120+
98121
static void Initialize(Local<Object> target,
99122
Local<Value> unused,
100123
Local<Context> context,
101124
void* priv) {
102125
Environment* env = Environment::GetCurrent(context);
103126
Isolate* isolate = env->isolate();
104127

128+
env->SetMethod(target, "triggerFatalException", TriggerFatalException);
129+
env->SetMethod(target, "enqueueMicrotask", EnqueueMicrotask);
105130
env->SetMethod(target, "setTickCallback", SetTickCallback);
106131
env->SetMethod(target, "runMicrotasks", RunMicrotasks);
107132
target->Set(env->context(),

src/node_util.cc

-11
Original file line numberDiff line numberDiff line change
@@ -180,15 +180,6 @@ void ArrayBufferViewHasBuffer(const FunctionCallbackInfo<Value>& args) {
180180
args.GetReturnValue().Set(args[0].As<ArrayBufferView>()->HasBuffer());
181181
}
182182

183-
void EnqueueMicrotask(const FunctionCallbackInfo<Value>& args) {
184-
Environment* env = Environment::GetCurrent(args);
185-
Isolate* isolate = env->isolate();
186-
187-
CHECK(args[0]->IsFunction());
188-
189-
isolate->EnqueueMicrotask(args[0].As<Function>());
190-
}
191-
192183
class WeakReference : public BaseObject {
193184
public:
194185
WeakReference(Environment* env, Local<Object> object, Local<Object> target)
@@ -261,8 +252,6 @@ void Initialize(Local<Object> target,
261252
WatchdogHasPendingSigint);
262253

263254
env->SetMethod(target, "arrayBufferViewHasBuffer", ArrayBufferViewHasBuffer);
264-
env->SetMethod(target, "enqueueMicrotask", EnqueueMicrotask);
265-
env->SetMethod(target, "triggerFatalException", FatalException);
266255
Local<Object> constants = Object::New(env->isolate());
267256
NODE_DEFINE_CONSTANT(constants, ALL_PROPERTIES);
268257
NODE_DEFINE_CONSTANT(constants, ONLY_WRITABLE);

test/message/events_unhandled_error_nexttick.out

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Error
1313
at internal/main/run_main_module.js:*:*
1414
Emitted 'error' event at:
1515
at process.nextTick (*events_unhandled_error_nexttick.js:*:*)
16-
at processTicksAndRejections (internal/process/next_tick.js:*:*)
17-
at process.runNextTicks [as _tickCallback] (internal/process/next_tick.js:*:*)
16+
at processTicksAndRejections (internal/process/task_queues.js:*:*)
17+
at process.runNextTicks [as _tickCallback] (internal/process/task_queues.js:*:*)
1818
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
1919
at internal/main/run_main_module.js:*:*

test/message/nexttick_throw.out

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
^
55
ReferenceError: undefined_reference_error_maker is not defined
66
at *test*message*nexttick_throw.js:*:*
7-
at processTicksAndRejections (internal/process/next_tick.js:*:*)
8-
at process.runNextTicks [as _tickCallback] (internal/process/next_tick.js:*:*)
7+
at processTicksAndRejections (internal/process/task_queues.js:*:*)
8+
at process.runNextTicks [as _tickCallback] (internal/process/task_queues.js:*:*)
99
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
1010
at internal/main/run_main_module.js:*:*

test/parallel/test-bootstrap-modules.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ const expectedModules = new Set([
4848
'NativeModule internal/options',
4949
'NativeModule internal/priority_queue',
5050
'NativeModule internal/process/execution',
51-
'NativeModule internal/process/next_tick',
5251
'NativeModule internal/process/per_thread',
5352
'NativeModule internal/process/promises',
53+
'NativeModule internal/process/task_queues',
5454
'NativeModule internal/process/warning',
5555
'NativeModule internal/querystring',
5656
'NativeModule internal/timers',

0 commit comments

Comments
 (0)