Skip to content

Commit

Permalink
process: add isExiting
Browse files Browse the repository at this point in the history
This replaces the previously undocumented `_exiting` property with a
read-only property giving the same information. Setting the property is
restricted to internals, to prevent tampering. Tampering could affect
nextTick behavior, for example.
  • Loading branch information
bengl committed Dec 10, 2017
1 parent 9f55eac commit 1b7a9bf
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 13 deletions.
11 changes: 11 additions & 0 deletions doc/api/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -1208,6 +1208,17 @@ console.log(process.getgroups()); // [ 27, 30, 46, 1000 ]
*Note*: This function is only available on POSIX platforms (i.e. not Windows
or Android).

## process.isExiting
<!-- YAML
added: REPLACEME
-->

* {boolean}

This read-only boolean indicates if the process is currently exiting. That is,
when the `exit` event has been fired and no further scheduled asynchronous code
will run.

## process.kill(pid[, signal])
<!-- YAML
added: v0.0.6
Expand Down
9 changes: 5 additions & 4 deletions lib/internal/bootstrap_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
(function(process) {
let internalBinding;
const exceptionHandlerState = { captureFn: null };
let _process;

function startup() {
const EventEmitter = NativeModule.require('events');
Expand All @@ -33,7 +34,8 @@

setupGlobalVariables();

const _process = NativeModule.require('internal/process');
_process = NativeModule.require('internal/process');
_process.setIsExiting(false);
_process.setupConfig(NativeModule._source);
_process.setupSignalHandlers();
_process.setupUncaughtExceptionCapture(exceptionHandlerState);
Expand Down Expand Up @@ -302,7 +304,6 @@

global.Buffer = NativeModule.require('buffer').Buffer;
process.domain = null;
process._exiting = false;
}

function setupGlobalTimeouts() {
Expand Down Expand Up @@ -391,8 +392,8 @@
// since that means that we'll exit the process, emit the 'exit' event
if (!caught) {
try {
if (!process._exiting) {
process._exiting = true;
if (!process.isExiting) {
_process.setIsExiting(true);
process.emit('exit', 1);
}
} catch (er) {
Expand Down
34 changes: 30 additions & 4 deletions lib/internal/process.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
const errors = require('internal/errors');
const util = require('util');
const constants = process.binding('constants').os.signals;
const {
getHiddenValue,
setHiddenValue,
is_exiting_private_symbol: kIsExitingSymbol
} = process.binding('util');

const assert = process.assert = function(x, msg) {
if (!x) throw new errors.Error('ERR_ASSERTION', msg || 'assertion error');
Expand Down Expand Up @@ -138,12 +143,24 @@ function setupConfig(_source) {

function setupKillAndExit() {

Object.defineProperty(process, 'isExiting', {
enumerable: true,
configurable: false,
get: getIsExiting
});

Object.defineProperty(process, '_exiting', {
enumerable: true,
configurable: false,
get: getIsExiting
});

process.exit = function(code) {
if (code || code === 0)
process.exitCode = code;

if (!process._exiting) {
process._exiting = true;
if (!process.isExiting) {
setIsExiting(true);
process.emit('exit', process.exitCode || 0);
}
process.reallyExit(process.exitCode || 0);
Expand Down Expand Up @@ -247,7 +264,6 @@ function setupRawDebug() {
};
}


function setupUncaughtExceptionCapture(exceptionHandlerState) {
// This is a typed array for faster communication with JS.
const shouldAbortOnUncaughtToggle = process._shouldAbortOnUncaughtToggle;
Expand Down Expand Up @@ -275,6 +291,14 @@ function setupUncaughtExceptionCapture(exceptionHandlerState) {
};
}

function setIsExiting(isExiting) {
setHiddenValue(process, kIsExitingSymbol, isExiting);
}

function getIsExiting() {
return getHiddenValue(process, kIsExitingSymbol);
}

module.exports = {
setup_performance,
setup_cpuUsage,
Expand All @@ -285,5 +309,7 @@ module.exports = {
setupSignalHandlers,
setupChannel,
setupRawDebug,
setupUncaughtExceptionCapture
setupUncaughtExceptionCapture,
setIsExiting,
getIsExiting
};
4 changes: 2 additions & 2 deletions lib/internal/process/next_tick.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ function setupNextTick() {
if (typeof callback !== 'function')
throw new errors.TypeError('ERR_INVALID_CALLBACK');

if (process._exiting)
if (process.isExiting)
return;

var args;
Expand Down Expand Up @@ -221,7 +221,7 @@ function setupNextTick() {
// CHECK(Number.isSafeInteger(triggerAsyncId) || triggerAsyncId === null)
// CHECK(triggerAsyncId > 0 || triggerAsyncId === null)

if (process._exiting)
if (process.isExiting)
return;

var args;
Expand Down
2 changes: 1 addition & 1 deletion src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class ModuleWrap;
V(contextify_context_private_symbol, "node:contextify:context") \
V(contextify_global_private_symbol, "node:contextify:global") \
V(decorated_private_symbol, "node:decorated") \
V(is_exiting_private_symbol, "node:isExiting") \
V(npn_buffer_private_symbol, "node:npnBuffer") \
V(selected_npn_buffer_private_symbol, "node:selectedNpnBuffer") \
V(domain_private_symbol, "node:domain") \
Expand Down Expand Up @@ -145,7 +146,6 @@ class ModuleWrap;
V(errno_string, "errno") \
V(error_string, "error") \
V(events_string, "_events") \
V(exiting_string, "_exiting") \
V(exit_code_string, "exitCode") \
V(exit_string, "exit") \
V(expire_string, "expire") \
Expand Down
4 changes: 3 additions & 1 deletion src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4294,7 +4294,9 @@ int EmitExit(Environment* env) {
HandleScope handle_scope(env->isolate());
Context::Scope context_scope(env->context());
Local<Object> process_object = env->process_object();
process_object->Set(env->exiting_string(), True(env->isolate()));
process_object->SetPrivate(env->context(),
env->is_exiting_private_symbol(),
True(env->isolate()));

Local<String> exitCode = env->exit_code_string();
int code = process_object->Get(exitCode)->Int32Value();
Expand Down
8 changes: 7 additions & 1 deletion test/parallel/test-next-tick-when-exiting.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@
require('../common');
const assert = require('assert');

const isExitingDesc = Object.getOwnPropertyDescriptor(process, 'isExiting');
assert.ok(!('set' in isExitingDesc));
assert.ok('get' in isExitingDesc);
assert.strictEqual(isExitingDesc.configurable, false);
assert.strictEqual(isExitingDesc.enumerable, true);

process.on('exit', () => {
assert.strictEqual(process._exiting, true, 'process._exiting was not set!');
assert.strictEqual(process.isExiting, true, 'process.isExiting was not set!');

process.nextTick(() => {
assert.fail('process is exiting, should not be called.');
Expand Down

0 comments on commit 1b7a9bf

Please sign in to comment.