Skip to content

Commit f2389eb

Browse files
HarshithaKPcodebytere
authored andcommittedFeb 27, 2020
worker: emit runtime error on loop creation failure
Instead of hard asserting throw a runtime error, that is more consumable. Fixes: #31614 PR-URL: #31621 Reviewed-By: Anna Henningsen <anna@addaleax.net>
1 parent a86cb0e commit f2389eb

File tree

6 files changed

+50
-17
lines changed

6 files changed

+50
-17
lines changed
 

‎doc/api/errors.md

+5
Original file line numberDiff line numberDiff line change
@@ -2066,6 +2066,11 @@ meaning of the error depends on the specific function.
20662066

20672067
The WASI instance has already started.
20682068

2069+
<a id="ERR_WORKER_INIT_FAILED"></a>
2070+
### `ERR_WORKER_INIT_FAILED`
2071+
2072+
The `Worker` initialization failed.
2073+
20692074
<a id="ERR_WORKER_INVALID_EXEC_ARGV"></a>
20702075
### `ERR_WORKER_INVALID_EXEC_ARGV`
20712076

‎lib/internal/errors.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -1381,12 +1381,13 @@ E('ERR_VM_MODULE_NOT_MODULE',
13811381
'Provided module is not an instance of Module', Error);
13821382
E('ERR_VM_MODULE_STATUS', 'Module status %s', Error);
13831383
E('ERR_WASI_ALREADY_STARTED', 'WASI instance has already started', Error);
1384+
E('ERR_WORKER_INIT_FAILED', 'Worker initialization failure: %s', Error);
13841385
E('ERR_WORKER_INVALID_EXEC_ARGV', (errors, msg = 'invalid execArgv flags') =>
13851386
`Initiated Worker with ${msg}: ${errors.join(', ')}`,
13861387
Error);
13871388
E('ERR_WORKER_NOT_RUNNING', 'Worker instance not running', Error);
1388-
E('ERR_WORKER_OUT_OF_MEMORY', 'Worker terminated due to reaching memory limit',
1389-
Error);
1389+
E('ERR_WORKER_OUT_OF_MEMORY',
1390+
'Worker terminated due to reaching memory limit: %s', Error);
13901391
E('ERR_WORKER_PATH',
13911392
'The worker script filename must be an absolute path or a relative ' +
13921393
'path starting with \'./\' or \'../\'. Received "%s"',

‎lib/internal/worker.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ const {
2525
ERR_WORKER_UNSUPPORTED_EXTENSION,
2626
ERR_WORKER_INVALID_EXEC_ARGV,
2727
ERR_INVALID_ARG_TYPE,
28+
// eslint-disable-next-line no-unused-vars
29+
ERR_WORKER_INIT_FAILED,
2830
} = errorCodes;
2931
const { validateString } = require('internal/validators');
3032
const { getOptionValue } = require('internal/options');
@@ -136,7 +138,9 @@ class Worker extends EventEmitter {
136138
throw new ERR_WORKER_INVALID_EXEC_ARGV(
137139
this[kHandle].invalidNodeOptions, 'invalid NODE_OPTIONS env variable');
138140
}
139-
this[kHandle].onexit = (code, customErr) => this[kOnExit](code, customErr);
141+
this[kHandle].onexit = (code, customErr, customErrReason) => {
142+
this[kOnExit](code, customErr, customErrReason);
143+
};
140144
this[kPort] = this[kHandle].messagePort;
141145
this[kPort].on('message', (data) => this[kOnMessage](data));
142146
this[kPort].start();
@@ -181,14 +185,15 @@ class Worker extends EventEmitter {
181185
this[kHandle].startThread();
182186
}
183187

184-
[kOnExit](code, customErr) {
188+
[kOnExit](code, customErr, customErrReason) {
185189
debug(`[${threadId}] hears end event for Worker ${this.threadId}`);
186190
drainMessagePort(this[kPublicPort]);
187191
drainMessagePort(this[kPort]);
188192
this[kDispose]();
189193
if (customErr) {
190-
debug(`[${threadId}] failing with custom error ${customErr}`);
191-
this.emit('error', new errorCodes[customErr]());
194+
debug(`[${threadId}] failing with custom error ${customErr} \
195+
and with reason {customErrReason}`);
196+
this.emit('error', new errorCodes[customErr](customErrReason));
192197
}
193198
this.emit('exit', code);
194199
this.removeAllListeners();

‎src/node_worker.cc

+29-10
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,16 @@ class WorkerThreadData {
133133
public:
134134
explicit WorkerThreadData(Worker* w)
135135
: w_(w) {
136-
CHECK_EQ(uv_loop_init(&loop_), 0);
136+
int ret = uv_loop_init(&loop_);
137+
if (ret != 0) {
138+
char err_buf[128];
139+
uv_err_name_r(ret, err_buf, sizeof(err_buf));
140+
w->custom_error_ = "ERR_WORKER_INIT_FAILED";
141+
w->custom_error_str_ = err_buf;
142+
w->loop_init_failed_ = true;
143+
w->stopped_ = true;
144+
return;
145+
}
137146

138147
std::shared_ptr<ArrayBufferAllocator> allocator =
139148
ArrayBufferAllocator::Create();
@@ -146,6 +155,8 @@ class WorkerThreadData {
146155
Isolate* isolate = Isolate::Allocate();
147156
if (isolate == nullptr) {
148157
w->custom_error_ = "ERR_WORKER_OUT_OF_MEMORY";
158+
w->custom_error_str_ = "Failed to create new Isolate";
159+
w->stopped_ = true;
149160
return;
150161
}
151162

@@ -204,11 +215,14 @@ class WorkerThreadData {
204215
isolate->Dispose();
205216

206217
// Wait until the platform has cleaned up all relevant resources.
207-
while (!platform_finished)
218+
while (!platform_finished) {
219+
CHECK(!w_->loop_init_failed_);
208220
uv_run(&loop_, UV_RUN_ONCE);
221+
}
222+
}
223+
if (!w_->loop_init_failed_) {
224+
CheckedUvLoopClose(&loop_);
209225
}
210-
211-
CheckedUvLoopClose(&loop_);
212226
}
213227

214228
private:
@@ -223,6 +237,7 @@ size_t Worker::NearHeapLimit(void* data, size_t current_heap_limit,
223237
size_t initial_heap_limit) {
224238
Worker* worker = static_cast<Worker*>(data);
225239
worker->custom_error_ = "ERR_WORKER_OUT_OF_MEMORY";
240+
worker->custom_error_str_ = "JS heap out of memory";
226241
worker->Exit(1);
227242
// Give the current GC some extra leeway to let it finish rather than
228243
// crash hard. We are not going to perform further allocations anyway.
@@ -242,6 +257,7 @@ void Worker::Run() {
242257

243258
WorkerThreadData data(this);
244259
if (isolate_ == nullptr) return;
260+
CHECK(!data.w_->loop_init_failed_);
245261

246262
Debug(this, "Starting worker with id %llu", thread_id_);
247263
{
@@ -287,9 +303,8 @@ void Worker::Run() {
287303
TryCatch try_catch(isolate_);
288304
context = NewContext(isolate_);
289305
if (context.IsEmpty()) {
290-
// TODO(addaleax): Inform the target about the actual underlying
291-
// failure.
292306
custom_error_ = "ERR_WORKER_OUT_OF_MEMORY";
307+
custom_error_str_ = "Failed to create new Context";
293308
return;
294309
}
295310
}
@@ -417,10 +432,14 @@ void Worker::JoinThread() {
417432
Undefined(env()->isolate())).Check();
418433

419434
Local<Value> args[] = {
420-
Integer::New(env()->isolate(), exit_code_),
421-
custom_error_ != nullptr ?
422-
OneByteString(env()->isolate(), custom_error_).As<Value>() :
423-
Null(env()->isolate()).As<Value>(),
435+
Integer::New(env()->isolate(), exit_code_),
436+
custom_error_ != nullptr
437+
? OneByteString(env()->isolate(), custom_error_).As<Value>()
438+
: Null(env()->isolate()).As<Value>(),
439+
!custom_error_str_.empty()
440+
? OneByteString(env()->isolate(), custom_error_str_.c_str())
441+
.As<Value>()
442+
: Null(env()->isolate()).As<Value>(),
424443
};
425444

426445
MakeCallback(env()->onexit_string(), arraysize(args), args);

‎src/node_worker.h

+2
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ class Worker : public AsyncWrap {
8585

8686
bool thread_joined_ = true;
8787
const char* custom_error_ = nullptr;
88+
std::string custom_error_str_;
89+
bool loop_init_failed_ = false;
8890
int exit_code_ = 0;
8991
uint64_t thread_id_ = -1;
9092
uintptr_t stack_base_ = 0;

‎test/parallel/test-worker-resource-limits.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ if (!process.env.HAS_STARTED_WORKER) {
2525
}));
2626
w.on('error', common.expectsError({
2727
code: 'ERR_WORKER_OUT_OF_MEMORY',
28-
message: 'Worker terminated due to reaching memory limit'
28+
message: 'Worker terminated due to reaching memory limit: ' +
29+
'JS heap out of memory'
2930
}));
3031
return;
3132
}

0 commit comments

Comments
 (0)
Please sign in to comment.