Skip to content

Commit 0af4c9e

Browse files
src: fix domains + --abort-on-uncaught-exception
If run with --abort-on-uncaught-exception, V8 will abort the process whenever it does not see a JS-installed CatchClause in the stack. C++ TryCatch clauses are ignored. Domains work by setting a FatalException handler which is ignored when running in abort mode. This patch modifies MakeCallback to call its target function through a JS function that installs a CatchClause and manually calls _fatalException on error, if the process is both using domains and is in abort mode. Semver: patch PR-URL: #922 Fixes: #836 Reviewed-By: Ben Noordhuis <[email protected]>
1 parent 2ca22aa commit 0af4c9e

File tree

6 files changed

+154
-2
lines changed

6 files changed

+154
-2
lines changed

src/async-wrap.cc

+18-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "v8.h"
99

10+
using v8::Array;
1011
using v8::Context;
1112
using v8::Function;
1213
using v8::FunctionCallbackInfo;
@@ -81,6 +82,7 @@ Handle<Value> AsyncWrap::MakeCallback(const Handle<Function> cb,
8182
Local<Object> process = env()->process_object();
8283
Local<Object> domain;
8384
bool has_domain = false;
85+
bool has_abort_on_uncaught_and_domains = false;
8486

8587
if (env()->using_domains()) {
8688
Local<Value> domain_v = context->Get(env()->domain_string());
@@ -89,6 +91,7 @@ Handle<Value> AsyncWrap::MakeCallback(const Handle<Function> cb,
8991
domain = domain_v.As<Object>();
9092
if (domain->Get(env()->disposed_string())->IsTrue())
9193
return Undefined(env()->isolate());
94+
has_abort_on_uncaught_and_domains = env()->using_abort_on_uncaught_exc();
9295
}
9396
}
9497

@@ -112,7 +115,21 @@ Handle<Value> AsyncWrap::MakeCallback(const Handle<Function> cb,
112115
try_catch.SetVerbose(true);
113116
}
114117

115-
Local<Value> ret = cb->Call(context, argc, argv);
118+
Local<Value> ret;
119+
120+
if (has_abort_on_uncaught_and_domains) {
121+
Local<Value> fn = process->Get(env()->domain_abort_uncaught_exc_string());
122+
if (fn->IsFunction()) {
123+
Local<Array> special_context = Array::New(env()->isolate(), 2);
124+
special_context->Set(0, context);
125+
special_context->Set(1, cb);
126+
ret = fn.As<Function>()->Call(special_context, argc, argv);
127+
} else {
128+
ret = cb->Call(context, argc, argv);
129+
}
130+
} else {
131+
ret = cb->Call(context, argc, argv);
132+
}
116133

117134
if (try_catch.HasCaught()) {
118135
return Undefined(env()->isolate());

src/env-inl.h

+9
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ inline Environment::Environment(v8::Local<v8::Context> context,
165165
isolate_data_(IsolateData::GetOrCreate(context->GetIsolate(), loop)),
166166
using_smalloc_alloc_cb_(false),
167167
using_domains_(false),
168+
using_abort_on_uncaught_exc_(false),
168169
using_asyncwrap_(false),
169170
printed_error_(false),
170171
debugger_agent_(this),
@@ -283,6 +284,14 @@ inline void Environment::set_using_smalloc_alloc_cb(bool value) {
283284
using_smalloc_alloc_cb_ = value;
284285
}
285286

287+
inline bool Environment::using_abort_on_uncaught_exc() const {
288+
return using_abort_on_uncaught_exc_;
289+
}
290+
291+
inline void Environment::set_using_abort_on_uncaught_exc(bool value) {
292+
using_abort_on_uncaught_exc_ = value;
293+
}
294+
286295
inline bool Environment::using_domains() const {
287296
return using_domains_;
288297
}

src/env.h

+5
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ namespace node {
6767
V(dev_string, "dev") \
6868
V(disposed_string, "_disposed") \
6969
V(domain_string, "domain") \
70+
V(domain_abort_uncaught_exc_string, "_makeCallbackAbortOnUncaught") \
7071
V(exchange_string, "exchange") \
7172
V(idle_string, "idle") \
7273
V(irq_string, "irq") \
@@ -402,6 +403,9 @@ class Environment {
402403
inline bool using_smalloc_alloc_cb() const;
403404
inline void set_using_smalloc_alloc_cb(bool value);
404405

406+
inline bool using_abort_on_uncaught_exc() const;
407+
inline void set_using_abort_on_uncaught_exc(bool value);
408+
405409
inline bool using_domains() const;
406410
inline void set_using_domains(bool value);
407411

@@ -496,6 +500,7 @@ class Environment {
496500
ares_task_list cares_task_list_;
497501
bool using_smalloc_alloc_cb_;
498502
bool using_domains_;
503+
bool using_abort_on_uncaught_exc_;
499504
bool using_asyncwrap_;
500505
bool printed_error_;
501506
debugger::Agent debugger_agent_;

src/node.cc

+5-1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ static bool print_eval = false;
112112
static bool force_repl = false;
113113
static bool trace_deprecation = false;
114114
static bool throw_deprecation = false;
115+
static bool abort_on_uncaught_exception = false;
115116
static const char* eval_string = nullptr;
116117
static bool use_debug_agent = false;
117118
static bool debug_wait_connect = false;
@@ -3109,6 +3110,9 @@ static void ParseArgs(int* argc,
31093110
trace_deprecation = true;
31103111
} else if (strcmp(arg, "--throw-deprecation") == 0) {
31113112
throw_deprecation = true;
3113+
} else if (strcmp(arg, "--abort-on-uncaught-exception") == 0 ||
3114+
strcmp(arg, "--abort_on_uncaught_exception") == 0) {
3115+
abort_on_uncaught_exception = true;
31123116
} else if (strcmp(arg, "--v8-options") == 0) {
31133117
new_v8_argv[new_v8_argc] = "--help";
31143118
new_v8_argc += 1;
@@ -3789,7 +3793,7 @@ int Start(int argc, char** argv) {
37893793
exec_argc,
37903794
exec_argv);
37913795
Context::Scope context_scope(context);
3792-
3796+
env->set_using_abort_on_uncaught_exc(abort_on_uncaught_exception);
37933797
// Start debug agent when argv has --debug
37943798
if (use_debug_agent)
37953799
StartDebug(env, debug_wait_connect);

src/node.js

+8
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,14 @@
209209
};
210210

211211
startup.processFatal = function() {
212+
process._makeCallbackAbortOnUncaught = function() {
213+
try {
214+
return this[1].apply(this[0], arguments);
215+
} catch (err) {
216+
process._fatalException(err);
217+
}
218+
};
219+
212220
process._fatalException = function(er) {
213221
var caught;
214222

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
var common = require('../common');
2+
var assert = require('assert');
3+
var spawn = require('child_process').spawn;
4+
5+
var tests = [
6+
nextTick,
7+
timer,
8+
timerPlusNextTick,
9+
firstRun,
10+
netServer
11+
]
12+
13+
tests.forEach(function(test) {
14+
console.log(test.name);
15+
var child = spawn(process.execPath, [
16+
'--abort-on-uncaught-exception',
17+
'-e',
18+
'(' + test + ')()',
19+
common.PORT
20+
]);
21+
child.stderr.pipe(process.stderr);
22+
child.stdout.pipe(process.stdout);
23+
child.on('exit', function(code) {
24+
assert.strictEqual(code, 0);
25+
});
26+
});
27+
28+
function nextTick() {
29+
var domain = require('domain');
30+
var d = domain.create();
31+
32+
d.on('error', function(err) {
33+
console.log('ok');
34+
process.exit(0);
35+
});
36+
d.run(function() {
37+
process.nextTick(function() {
38+
throw new Error('exceptional!');
39+
});
40+
});
41+
}
42+
43+
function timer() {
44+
var domain = require('domain');
45+
var d = domain.create();
46+
47+
d.on('error', function(err) {
48+
console.log('ok');
49+
process.exit(0);
50+
});
51+
d.run(function() {
52+
setTimeout(function() {
53+
throw new Error('exceptional!');
54+
}, 33);
55+
});
56+
}
57+
58+
function timerPlusNextTick() {
59+
var domain = require('domain');
60+
var d = domain.create();
61+
62+
d.on('error', function(err) {
63+
console.log('ok');
64+
process.exit(0);
65+
});
66+
d.run(function() {
67+
setTimeout(function() {
68+
process.nextTick(function() {
69+
throw new Error('exceptional!');
70+
});
71+
}, 33);
72+
});
73+
}
74+
75+
function firstRun() {
76+
var domain = require('domain');
77+
var d = domain.create();
78+
79+
d.on('error', function(err) {
80+
console.log('ok');
81+
process.exit(0);
82+
});
83+
d.run(function() {
84+
throw new Error('exceptional!');
85+
});
86+
}
87+
88+
function netServer() {
89+
var domain = require('domain');
90+
var net = require('net');
91+
var d = domain.create();
92+
93+
d.on('error', function(err) {
94+
console.log('ok');
95+
process.exit(0);
96+
});
97+
d.run(function() {
98+
var server = net.createServer(function(conn) {
99+
conn.pipe(conn);
100+
});
101+
server.listen(Number(process.argv[1]), '0.0.0.0', function() {
102+
var conn = net.connect(Number(process.argv[1]), '0.0.0.0')
103+
conn.once('data', function() {
104+
throw new Error('ok');
105+
})
106+
conn.end('ok');
107+
});
108+
});
109+
}

0 commit comments

Comments
 (0)