Skip to content

Commit e01dbd4

Browse files
HarshithaKPcodebytere
authored andcommitted
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 <[email protected]>
1 parent eee0777 commit e01dbd4

File tree

6 files changed

+50
-17
lines changed

6 files changed

+50
-17
lines changed

doc/api/errors.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2007,6 +2007,11 @@ meaning of the error depends on the specific function.
20072007

20082008
The WASI instance has already started.
20092009

2010+
<a id="ERR_WORKER_INIT_FAILED"></a>
2011+
### `ERR_WORKER_INIT_FAILED`
2012+
2013+
The `Worker` initialization failed.
2014+
20102015
<a id="ERR_WORKER_INVALID_EXEC_ARGV"></a>
20112016
### `ERR_WORKER_INVALID_EXEC_ARGV`
20122017

lib/internal/errors.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,11 +1344,12 @@ E('ERR_VM_MODULE_NOT_MODULE',
13441344
'Provided module is not an instance of Module', Error);
13451345
E('ERR_VM_MODULE_STATUS', 'Module status %s', Error);
13461346
E('ERR_WASI_ALREADY_STARTED', 'WASI instance has already started', Error);
1347+
E('ERR_WORKER_INIT_FAILED', 'Worker initialization failure: %s', Error);
13471348
E('ERR_WORKER_INVALID_EXEC_ARGV', (errors, msg = 'invalid execArgv flags') =>
13481349
`Initiated Worker with ${msg}: ${errors.join(', ')}`,
13491350
Error);
1350-
E('ERR_WORKER_OUT_OF_MEMORY', 'Worker terminated due to reaching memory limit',
1351-
Error);
1351+
E('ERR_WORKER_OUT_OF_MEMORY',
1352+
'Worker terminated due to reaching memory limit: %s', Error);
13521353
E('ERR_WORKER_PATH',
13531354
'The worker script filename must be an absolute path or a relative ' +
13541355
'path starting with \'./\' or \'../\'. Received "%s"',

lib/internal/worker.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ const {
2424
ERR_WORKER_UNSUPPORTED_EXTENSION,
2525
ERR_WORKER_INVALID_EXEC_ARGV,
2626
ERR_INVALID_ARG_TYPE,
27+
// eslint-disable-next-line no-unused-vars
28+
ERR_WORKER_INIT_FAILED,
2729
} = errorCodes;
2830
const { validateString } = require('internal/validators');
2931
const { getOptionValue } = require('internal/options');
@@ -135,7 +137,9 @@ class Worker extends EventEmitter {
135137
throw new ERR_WORKER_INVALID_EXEC_ARGV(
136138
this[kHandle].invalidNodeOptions, 'invalid NODE_OPTIONS env variable');
137139
}
138-
this[kHandle].onexit = (code, customErr) => this[kOnExit](code, customErr);
140+
this[kHandle].onexit = (code, customErr, customErrReason) => {
141+
this[kOnExit](code, customErr, customErrReason);
142+
};
139143
this[kPort] = this[kHandle].messagePort;
140144
this[kPort].on('message', (data) => this[kOnMessage](data));
141145
this[kPort].start();
@@ -180,14 +184,15 @@ class Worker extends EventEmitter {
180184
this[kHandle].startThread();
181185
}
182186

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

src/node_worker.cc

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,16 @@ class WorkerThreadData {
138138
public:
139139
explicit WorkerThreadData(Worker* w)
140140
: w_(w) {
141-
CHECK_EQ(uv_loop_init(&loop_), 0);
141+
int ret = uv_loop_init(&loop_);
142+
if (ret != 0) {
143+
char err_buf[128];
144+
uv_err_name_r(ret, err_buf, sizeof(err_buf));
145+
w->custom_error_ = "ERR_WORKER_INIT_FAILED";
146+
w->custom_error_str_ = err_buf;
147+
w->loop_init_failed_ = true;
148+
w->stopped_ = true;
149+
return;
150+
}
142151

143152
Isolate::CreateParams params;
144153
SetIsolateCreateParamsForNode(&params);
@@ -149,6 +158,8 @@ class WorkerThreadData {
149158
Isolate* isolate = Isolate::Allocate();
150159
if (isolate == nullptr) {
151160
w->custom_error_ = "ERR_WORKER_OUT_OF_MEMORY";
161+
w->custom_error_str_ = "Failed to create new Isolate";
162+
w->stopped_ = true;
152163
return;
153164
}
154165

@@ -206,11 +217,14 @@ class WorkerThreadData {
206217
isolate->Dispose();
207218

208219
// Wait until the platform has cleaned up all relevant resources.
209-
while (!platform_finished)
220+
while (!platform_finished) {
221+
CHECK(!w_->loop_init_failed_);
210222
uv_run(&loop_, UV_RUN_ONCE);
223+
}
224+
}
225+
if (!w_->loop_init_failed_) {
226+
CheckedUvLoopClose(&loop_);
211227
}
212-
213-
CheckedUvLoopClose(&loop_);
214228
}
215229

216230
private:
@@ -225,6 +239,7 @@ size_t Worker::NearHeapLimit(void* data, size_t current_heap_limit,
225239
size_t initial_heap_limit) {
226240
Worker* worker = static_cast<Worker*>(data);
227241
worker->custom_error_ = "ERR_WORKER_OUT_OF_MEMORY";
242+
worker->custom_error_str_ = "JS heap out of memory";
228243
worker->Exit(1);
229244
// Give the current GC some extra leeway to let it finish rather than
230245
// crash hard. We are not going to perform further allocations anyway.
@@ -244,6 +259,7 @@ void Worker::Run() {
244259

245260
WorkerThreadData data(this);
246261
if (isolate_ == nullptr) return;
262+
CHECK(!data.w_->loop_init_failed_);
247263

248264
Debug(this, "Starting worker with id %llu", thread_id_);
249265
{
@@ -289,9 +305,8 @@ void Worker::Run() {
289305
TryCatch try_catch(isolate_);
290306
context = NewContext(isolate_);
291307
if (context.IsEmpty()) {
292-
// TODO(addaleax): Inform the target about the actual underlying
293-
// failure.
294308
custom_error_ = "ERR_WORKER_OUT_OF_MEMORY";
309+
custom_error_str_ = "Failed to create new Context";
295310
return;
296311
}
297312
}
@@ -421,10 +436,14 @@ void Worker::JoinThread() {
421436
Undefined(env()->isolate())).Check();
422437

423438
Local<Value> args[] = {
424-
Integer::New(env()->isolate(), exit_code_),
425-
custom_error_ != nullptr ?
426-
OneByteString(env()->isolate(), custom_error_).As<Value>() :
427-
Null(env()->isolate()).As<Value>(),
439+
Integer::New(env()->isolate(), exit_code_),
440+
custom_error_ != nullptr
441+
? OneByteString(env()->isolate(), custom_error_).As<Value>()
442+
: Null(env()->isolate()).As<Value>(),
443+
!custom_error_str_.empty()
444+
? OneByteString(env()->isolate(), custom_error_str_.c_str())
445+
.As<Value>()
446+
: Null(env()->isolate()).As<Value>(),
428447
};
429448

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

src/node_worker.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ class Worker : public AsyncWrap {
8787

8888
bool thread_joined_ = true;
8989
const char* custom_error_ = nullptr;
90+
std::string custom_error_str_;
91+
bool loop_init_failed_ = false;
9092
int exit_code_ = 0;
9193
uint64_t thread_id_ = -1;
9294
uintptr_t stack_base_ = 0;

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

Lines changed: 2 additions & 1 deletion
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)