Skip to content

Commit 20a6656

Browse files
author
Julien Gilli
committed
domains: fix handling of uncaught exceptions
Fix node exiting due to an exception being thrown rather than emitting an `'uncaughtException'` event on the process object when: 1. no error handler is set on the domain within which an error is thrown 2. an `'uncaughtException'` event listener is set on the process Also fix an issue where the process would not abort in the proper function call if an error is thrown within a domain with no error handler and `--abort-on-uncaught-exception` is used. Fixes #3607 and #3653.
1 parent 88c17f8 commit 20a6656

File tree

6 files changed

+680
-94
lines changed

6 files changed

+680
-94
lines changed

lib/domain.js

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,20 @@ Object.defineProperty(process, 'domain', {
2727
}
2828
});
2929

30+
// It's possible to enter one domain while already inside
31+
// another one. the stack is each entered domain.
32+
const stack = [];
33+
exports._stack = stack;
34+
3035
// let the process know we're using domains
31-
const _domain_flag = process._setupDomainUse(_domain);
36+
const _domain_flag = process._setupDomainUse(_domain, stack);
3237

3338
exports.Domain = Domain;
3439

3540
exports.create = exports.createDomain = function() {
3641
return new Domain();
3742
};
3843

39-
// it's possible to enter one domain while already inside
40-
// another one. the stack is each entered domain.
41-
var stack = [];
42-
exports._stack = stack;
4344
// the active domain is always the one that we're currently in.
4445
exports.active = null;
4546

@@ -96,14 +97,20 @@ Domain.prototype._errorHandler = function errorHandler(er) {
9697
// that these exceptions are caught, and thus would prevent it from
9798
// aborting in these cases.
9899
if (stack.length === 1) {
99-
try {
100-
// Set the _emittingTopLevelDomainError so that we know that, even
101-
// if technically the top-level domain is still active, it would
102-
// be ok to abort on an uncaught exception at this point
103-
process._emittingTopLevelDomainError = true;
104-
caught = emitError();
105-
} finally {
106-
process._emittingTopLevelDomainError = false;
100+
// If there's no error handler, do not emit an 'error' event
101+
// as this would throw an error, make the process exit, and thus
102+
// prevent the process 'uncaughtException' event from being emitted
103+
// if a listener is set.
104+
if (EventEmitter.listenerCount(self, 'error') > 0) {
105+
try {
106+
// Set the _emittingTopLevelDomainError so that we know that, even
107+
// if technically the top-level domain is still active, it would
108+
// be ok to abort on an uncaught exception at this point
109+
process._emittingTopLevelDomainError = true;
110+
caught = emitError();
111+
} finally {
112+
process._emittingTopLevelDomainError = false;
113+
}
107114
}
108115
} else {
109116
// wrap this in a try/catch so we don't get infinite throwing

src/env.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ namespace node {
236236
V(buffer_prototype_object, v8::Object) \
237237
V(context, v8::Context) \
238238
V(domain_array, v8::Array) \
239+
V(domains_stack_array, v8::Array) \
239240
V(fs_stats_constructor_function, v8::Function) \
240241
V(generic_internal_field_template, v8::ObjectTemplate) \
241242
V(jsstream_constructor_template, v8::FunctionTemplate) \

src/node.cc

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -947,17 +947,53 @@ void* ArrayBufferAllocator::Allocate(size_t size) {
947947
return malloc(size);
948948
}
949949

950+
static bool DomainHasErrorHandler(const Environment* env,
951+
const Local<Object>& domain) {
952+
HandleScope scope(env->isolate());
953+
954+
Local<Value> domain_event_listeners_v = domain->Get(env->events_string());
955+
if (!domain_event_listeners_v->IsObject())
956+
return false;
957+
958+
Local<Object> domain_event_listeners_o =
959+
domain_event_listeners_v.As<Object>();
960+
961+
Local<Value> domain_error_listeners_v =
962+
domain_event_listeners_o->Get(env->error_string());
963+
964+
if (domain_error_listeners_v->IsFunction() ||
965+
(domain_error_listeners_v->IsArray() &&
966+
domain_error_listeners_v.As<Array>()->Length() > 0))
967+
return true;
968+
969+
return false;
970+
}
971+
972+
static bool TopDomainHasErrorHandler(const Environment* env) {
973+
HandleScope scope(env->isolate());
950974

951-
static bool IsDomainActive(const Environment* env) {
952975
if (!env->using_domains())
953976
return false;
954977

955-
Local<Array> domain_array = env->domain_array().As<Array>();
956-
if (domain_array->Length() == 0)
978+
Local<Array> domains_stack_array = env->domains_stack_array().As<Array>();
979+
if (domains_stack_array->Length() == 0)
980+
return false;
981+
982+
uint32_t domains_stack_length = domains_stack_array->Length();
983+
if (domains_stack_length == 0)
984+
return false;
985+
986+
Local<Value> top_domain_v =
987+
domains_stack_array->Get(domains_stack_length - 1);
988+
989+
if (!top_domain_v->IsObject())
957990
return false;
958991

959-
Local<Value> domain_v = domain_array->Get(0);
960-
return !domain_v->IsNull();
992+
Local<Object> top_domain = top_domain_v.As<Object>();
993+
if (DomainHasErrorHandler(env, top_domain))
994+
return true;
995+
996+
return false;
961997
}
962998

963999

@@ -971,7 +1007,7 @@ static bool ShouldAbortOnUncaughtException(Isolate* isolate) {
9711007
bool isEmittingTopLevelDomainError =
9721008
process_object->Get(emitting_top_level_domain_error_key)->BooleanValue();
9731009

974-
return !IsDomainActive(env) || isEmittingTopLevelDomainError;
1010+
return isEmittingTopLevelDomainError || !TopDomainHasErrorHandler(env);
9751011
}
9761012

9771013

@@ -1000,6 +1036,9 @@ void SetupDomainUse(const FunctionCallbackInfo<Value>& args) {
10001036
CHECK(args[0]->IsArray());
10011037
env->set_domain_array(args[0].As<Array>());
10021038

1039+
CHECK(args[1]->IsArray());
1040+
env->set_domains_stack_array(args[1].As<Array>());
1041+
10031042
// Do a little housekeeping.
10041043
env->process_object()->Delete(
10051044
FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupDomainUse"));

0 commit comments

Comments
 (0)