summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJoyee Cheung <joyeec9h3@gmail.com>2018-12-19 18:44:14 +0800
committerJoyee Cheung <joyeec9h3@gmail.com>2018-12-24 07:57:15 +0800
commit457603e96194b4858ad715f9faacb3ad7fec7f35 (patch)
treec9d496b06366721b7453b38c176b1cdbaa39fbe9 /lib
parente830e2742cedceb7fc89de4910b2c1a9536a1638 (diff)
downloadandroid-node-v8-457603e96194b4858ad715f9faacb3ad7fec7f35.tar.gz
android-node-v8-457603e96194b4858ad715f9faacb3ad7fec7f35.tar.bz2
android-node-v8-457603e96194b4858ad715f9faacb3ad7fec7f35.zip
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: https://github.com/nodejs/node/pull/25163 Refs: https://github.com/nodejs/node/issues/24961 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Anatoli Papirovski <apapirovski@mac.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/internal/bootstrap/node.js20
-rw-r--r--lib/internal/process/next_tick.js248
-rw-r--r--lib/internal/process/promises.js18
3 files changed, 151 insertions, 135 deletions
diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js
index 0f207ab660..870a54746b 100644
--- a/lib/internal/bootstrap/node.js
+++ b/lib/internal/bootstrap/node.js
@@ -14,13 +14,9 @@
// This file is compiled as if it's wrapped in a function with arguments
// passed by node::LoadEnvironment()
-/* global process, bootstrappers, loaderExports, triggerFatalException */
+/* global process, loaderExports, triggerFatalException */
/* global isMainThread */
-const {
- _setupNextTick,
- _setupPromises
-} = bootstrappers;
const { internalBinding, NativeModule } = loaderExports;
const exceptionHandlerState = { captureFn: null };
@@ -105,8 +101,18 @@ function startup() {
}
NativeModule.require('internal/process/warning').setup();
- NativeModule.require('internal/process/next_tick').setup(_setupNextTick,
- _setupPromises);
+ const {
+ nextTick,
+ runNextTicks
+ } = NativeModule.require('internal/process/next_tick').setup();
+
+ process.nextTick = nextTick;
+ // Used to emulate a tick manually in the JS land.
+ // A better name for this function would be `runNextTicks` but
+ // it has been exposed to the process object so we keep this legacy name
+ // TODO(joyeecheung): either remove it or make it public
+ process._tickCallback = runNextTicks;
+
const credentials = internalBinding('credentials');
if (credentials.implementsPosixCredentials) {
process.getuid = credentials.getuid;
diff --git a/lib/internal/process/next_tick.js b/lib/internal/process/next_tick.js
index cb0274367a..00bba25471 100644
--- a/lib/internal/process/next_tick.js
+++ b/lib/internal/process/next_tick.js
@@ -1,128 +1,138 @@
'use strict';
-exports.setup = setupNextTick;
-
-function setupNextTick(_setupNextTick, _setupPromises) {
- const {
- getDefaultTriggerAsyncId,
- newAsyncId,
- initHooksExist,
- destroyHooksExist,
- emitInit,
- emitBefore,
- emitAfter,
- emitDestroy,
- symbols: { async_id_symbol, trigger_async_id_symbol }
- } = require('internal/async_hooks');
- const emitPromiseRejectionWarnings =
- require('internal/process/promises').setup(_setupPromises);
- const { ERR_INVALID_CALLBACK } = require('internal/errors').codes;
- const FixedQueue = require('internal/fixed_queue');
-
- // tickInfo is used so that the C++ code in src/node.cc can
- // have easy access to our nextTick state, and avoid unnecessary
- // calls into JS land.
- // runMicrotasks is used to run V8's micro task queue.
- const [
- tickInfo,
- runMicrotasks
- ] = _setupNextTick(internalTickCallback);
-
- // *Must* match Environment::TickInfo::Fields in src/env.h.
- const kHasScheduled = 0;
- const kHasPromiseRejections = 1;
-
- const queue = new FixedQueue();
-
- process.nextTick = nextTick;
- // Needs to be accessible from beyond this scope.
- process._tickCallback = _tickCallback;
-
- function _tickCallback() {
- if (tickInfo[kHasScheduled] === 0 && tickInfo[kHasPromiseRejections] === 0)
- runMicrotasks();
- if (tickInfo[kHasScheduled] === 0 && tickInfo[kHasPromiseRejections] === 0)
- return;
-
- internalTickCallback();
- }
-
- function internalTickCallback() {
- let tock;
- do {
- while (tock = queue.shift()) {
- const asyncId = tock[async_id_symbol];
- emitBefore(asyncId, tock[trigger_async_id_symbol]);
- // emitDestroy() places the async_id_symbol into an asynchronous queue
- // that calls the destroy callback in the future. It's called before
- // calling tock.callback so destroy will be called even if the callback
- // throws an exception that is handled by 'uncaughtException' or a
- // domain.
- // TODO(trevnorris): This is a bit of a hack. It relies on the fact
- // that nextTick() doesn't allow the event loop to proceed, but if
- // any async hooks are enabled during the callback's execution then
- // this tock's after hook will be called, but not its destroy hook.
- if (destroyHooksExist())
- emitDestroy(asyncId);
-
- const callback = tock.callback;
- if (tock.args === undefined)
- callback();
- else
- Reflect.apply(callback, undefined, tock.args);
-
- emitAfter(asyncId);
- }
- tickInfo[kHasScheduled] = 0;
- runMicrotasks();
- } while (!queue.isEmpty() || emitPromiseRejectionWarnings());
- tickInfo[kHasPromiseRejections] = 0;
- }
+const {
+ // For easy access to the nextTick state in the C++ land,
+ // and to avoid unnecessary calls into JS land.
+ tickInfo,
+ // Used to run V8's micro task queue.
+ runMicrotasks,
+ setTickCallback,
+ initializePromiseRejectCallback
+} = internalBinding('task_queue');
+
+const {
+ promiseRejectHandler,
+ emitPromiseRejectionWarnings
+} = require('internal/process/promises');
+
+const {
+ getDefaultTriggerAsyncId,
+ newAsyncId,
+ initHooksExist,
+ destroyHooksExist,
+ emitInit,
+ emitBefore,
+ emitAfter,
+ emitDestroy,
+ symbols: { async_id_symbol, trigger_async_id_symbol }
+} = require('internal/async_hooks');
+const { ERR_INVALID_CALLBACK } = require('internal/errors').codes;
+const FixedQueue = require('internal/fixed_queue');
+
+// *Must* match Environment::TickInfo::Fields in src/env.h.
+const kHasScheduled = 0;
+const kHasPromiseRejections = 1;
+
+const queue = new FixedQueue();
+
+function runNextTicks() {
+ if (tickInfo[kHasScheduled] === 0 && tickInfo[kHasPromiseRejections] === 0)
+ runMicrotasks();
+ if (tickInfo[kHasScheduled] === 0 && tickInfo[kHasPromiseRejections] === 0)
+ return;
+
+ internalTickCallback();
+}
- class TickObject {
- constructor(callback, args, triggerAsyncId) {
- // This must be set to null first to avoid function tracking
- // on the hidden class, revisit in V8 versions after 6.2
- this.callback = null;
- this.callback = callback;
- this.args = args;
-
- const asyncId = newAsyncId();
- this[async_id_symbol] = asyncId;
- this[trigger_async_id_symbol] = triggerAsyncId;
-
- if (initHooksExist()) {
- emitInit(asyncId,
- 'TickObject',
- triggerAsyncId,
- this);
- }
+function internalTickCallback() {
+ let tock;
+ do {
+ while (tock = queue.shift()) {
+ const asyncId = tock[async_id_symbol];
+ emitBefore(asyncId, tock[trigger_async_id_symbol]);
+ // emitDestroy() places the async_id_symbol into an asynchronous queue
+ // that calls the destroy callback in the future. It's called before
+ // calling tock.callback so destroy will be called even if the callback
+ // throws an exception that is handled by 'uncaughtException' or a
+ // domain.
+ // TODO(trevnorris): This is a bit of a hack. It relies on the fact
+ // that nextTick() doesn't allow the event loop to proceed, but if
+ // any async hooks are enabled during the callback's execution then
+ // this tock's after hook will be called, but not its destroy hook.
+ if (destroyHooksExist())
+ emitDestroy(asyncId);
+
+ const callback = tock.callback;
+ if (tock.args === undefined)
+ callback();
+ else
+ Reflect.apply(callback, undefined, tock.args);
+
+ emitAfter(asyncId);
}
- }
+ tickInfo[kHasScheduled] = 0;
+ runMicrotasks();
+ } while (!queue.isEmpty() || emitPromiseRejectionWarnings());
+ tickInfo[kHasPromiseRejections] = 0;
+}
- // `nextTick()` will not enqueue any callback when the process is about to
- // exit since the callback would not have a chance to be executed.
- function nextTick(callback) {
- if (typeof callback !== 'function')
- throw new ERR_INVALID_CALLBACK();
-
- if (process._exiting)
- return;
-
- var args;
- switch (arguments.length) {
- case 1: break;
- case 2: args = [arguments[1]]; break;
- case 3: args = [arguments[1], arguments[2]]; break;
- case 4: args = [arguments[1], arguments[2], arguments[3]]; break;
- default:
- args = new Array(arguments.length - 1);
- for (var i = 1; i < arguments.length; i++)
- args[i - 1] = arguments[i];
+class TickObject {
+ constructor(callback, args, triggerAsyncId) {
+ // This must be set to null first to avoid function tracking
+ // on the hidden class, revisit in V8 versions after 6.2
+ this.callback = null;
+ this.callback = callback;
+ this.args = args;
+
+ const asyncId = newAsyncId();
+ this[async_id_symbol] = asyncId;
+ this[trigger_async_id_symbol] = triggerAsyncId;
+
+ if (initHooksExist()) {
+ emitInit(asyncId,
+ 'TickObject',
+ triggerAsyncId,
+ this);
}
+ }
+}
- if (queue.isEmpty())
- tickInfo[kHasScheduled] = 1;
- queue.push(new TickObject(callback, args, getDefaultTriggerAsyncId()));
+// `nextTick()` will not enqueue any callback when the process is about to
+// exit since the callback would not have a chance to be executed.
+function nextTick(callback) {
+ if (typeof callback !== 'function')
+ throw new ERR_INVALID_CALLBACK();
+
+ if (process._exiting)
+ return;
+
+ var args;
+ switch (arguments.length) {
+ case 1: break;
+ case 2: args = [arguments[1]]; break;
+ case 3: args = [arguments[1], arguments[2]]; break;
+ case 4: args = [arguments[1], arguments[2], arguments[3]]; break;
+ default:
+ args = new Array(arguments.length - 1);
+ for (var i = 1; i < arguments.length; i++)
+ args[i - 1] = arguments[i];
}
+
+ if (queue.isEmpty())
+ tickInfo[kHasScheduled] = 1;
+ queue.push(new TickObject(callback, args, getDefaultTriggerAsyncId()));
}
+
+// TODO(joyeecheung): make this a factory class so that node.js can
+// control the side effects caused by the initializers.
+exports.setup = function() {
+ // Initializes the per-isolate promise rejection callback which
+ // will call the handler being passed into this function.
+ initializePromiseRejectCallback(promiseRejectHandler);
+ // Sets the callback to be run in every tick.
+ setTickCallback(internalTickCallback);
+ return {
+ nextTick,
+ runNextTicks
+ };
+};
diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js
index 31c96293b3..df4e8188fe 100644
--- a/lib/internal/process/promises.js
+++ b/lib/internal/process/promises.js
@@ -1,21 +1,16 @@
'use strict';
const { safeToString } = internalBinding('util');
+const {
+ promiseRejectEvents
+} = internalBinding('task_queue');
const maybeUnhandledPromises = new WeakMap();
const pendingUnhandledRejections = [];
const asyncHandledRejections = [];
-const promiseRejectEvents = {};
let lastPromiseId = 0;
-exports.setup = setupPromises;
-
-function setupPromises(_setupPromises) {
- _setupPromises(handler, promiseRejectEvents);
- return emitPromiseRejectionWarnings;
-}
-
-function handler(type, promise, reason) {
+function promiseRejectHandler(type, promise, reason) {
switch (type) {
case promiseRejectEvents.kPromiseRejectWithNoHandler:
return unhandledRejection(promise, reason);
@@ -124,3 +119,8 @@ function emitPromiseRejectionWarnings() {
}
return maybeScheduledTicks || pendingUnhandledRejections.length !== 0;
}
+
+module.exports = {
+ promiseRejectHandler,
+ emitPromiseRejectionWarnings
+};