Skip to content

Commit eee4378

Browse files
committed
src,worker: runtime error on loop creation failure
Instead of hard asserting throw a runtime error, that is more consumable. Fixes: nodejs#31614
1 parent 4c746a6 commit eee4378

File tree

5 files changed

+48
-15
lines changed

5 files changed

+48
-15
lines changed

doc/api/errors.md

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

20482048
The WASI instance has already started.
20492049

2050+
<a id="ERR_WORKER_INIT_FAILED"></a>
2051+
### `ERR_WORKER_INIT_FAILED`
2052+
2053+
The `Worker` initialization failed.
2054+
20502055
<a id="ERR_WORKER_INVALID_EXEC_ARGV"></a>
20512056
### `ERR_WORKER_INVALID_EXEC_ARGV`
20522057

lib/internal/errors.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,6 +1358,7 @@ E('ERR_VM_MODULE_NOT_MODULE',
13581358
'Provided module is not an instance of Module', Error);
13591359
E('ERR_VM_MODULE_STATUS', 'Module status %s', Error);
13601360
E('ERR_WASI_ALREADY_STARTED', 'WASI instance has already started', Error);
1361+
E('ERR_WORKER_INIT_FAILED', 'Worker initialization failure: %s', Error);
13611362
E('ERR_WORKER_INVALID_EXEC_ARGV', (errors, msg = 'invalid execArgv flags') =>
13621363
`Initiated Worker with ${msg}: ${errors.join(', ')}`,
13631364
Error);

lib/internal/worker.js

Lines changed: 9 additions & 4 deletions
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

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,17 @@ class WorkerThreadData {
134134
public:
135135
explicit WorkerThreadData(Worker* w)
136136
: w_(w) {
137-
CHECK_EQ(uv_loop_init(&loop_), 0);
137+
int ret = uv_loop_init(&loop_);
138+
if (ret != 0) {
139+
char err_buf[128];
140+
uv_err_name_r(ret, err_buf, sizeof(err_buf));
141+
std::string error_str = SPrintF("ERR_WORKER_INIT_FAILED: %s", err_buf);
142+
w->custom_error_ = "ERR_WORKER_INIT_FAILED";
143+
w->custom_error_str_ = err_buf;
144+
w->loop_init_failed_ = true;
145+
w->stopped_ = true;
146+
return;
147+
}
138148

139149
std::shared_ptr<ArrayBufferAllocator> allocator =
140150
ArrayBufferAllocator::Create();
@@ -147,6 +157,8 @@ class WorkerThreadData {
147157
Isolate* isolate = Isolate::Allocate();
148158
if (isolate == nullptr) {
149159
w->custom_error_ = "ERR_WORKER_OUT_OF_MEMORY";
160+
w->custom_error_str_ = "Failed to create new Isolate";
161+
w->stopped_ = true;
150162
return;
151163
}
152164

@@ -204,11 +216,14 @@ class WorkerThreadData {
204216
isolate->Dispose();
205217

206218
// Wait until the platform has cleaned up all relevant resources.
207-
while (!platform_finished)
219+
while (!platform_finished) {
220+
CHECK(!w_->loop_init_failed_);
208221
uv_run(&loop_, UV_RUN_ONCE);
222+
}
223+
}
224+
if (!w_->loop_init_failed_) {
225+
CheckedUvLoopClose(&loop_);
209226
}
210-
211-
CheckedUvLoopClose(&loop_);
212227
}
213228

214229
private:
@@ -223,6 +238,7 @@ size_t Worker::NearHeapLimit(void* data, size_t current_heap_limit,
223238
size_t initial_heap_limit) {
224239
Worker* worker = static_cast<Worker*>(data);
225240
worker->custom_error_ = "ERR_WORKER_OUT_OF_MEMORY";
241+
worker->custom_error_str_ = "JS heap Out of Memory";
226242
worker->Exit(1);
227243
// Give the current GC some extra leeway to let it finish rather than
228244
// crash hard. We are not going to perform further allocations anyway.
@@ -242,6 +258,7 @@ void Worker::Run() {
242258

243259
WorkerThreadData data(this);
244260
if (isolate_ == nullptr) return;
261+
CHECK(!data.w_->loop_init_failed_);
245262

246263
Debug(this, "Starting worker with id %llu", thread_id_);
247264
{
@@ -287,9 +304,8 @@ void Worker::Run() {
287304
TryCatch try_catch(isolate_);
288305
context = NewContext(isolate_);
289306
if (context.IsEmpty()) {
290-
// TODO(addaleax): Inform the target about the actual underlying
291-
// failure.
292307
custom_error_ = "ERR_WORKER_OUT_OF_MEMORY";
308+
custom_error_str_ = "Failed to create new Context";
293309
return;
294310
}
295311
}
@@ -417,10 +433,14 @@ void Worker::JoinThread() {
417433
Undefined(env()->isolate())).Check();
418434

419435
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>(),
436+
Integer::New(env()->isolate(), exit_code_),
437+
!custom_error_.empty()
438+
? OneByteString(env()->isolate(), custom_error_.c_str()).As<Value>()
439+
: Null(env()->isolate()).As<Value>(),
440+
!custom_error_str_.empty()
441+
? OneByteString(env()->isolate(), custom_error_str_.c_str())
442+
.As<Value>()
443+
: Null(env()->isolate()).As<Value>(),
424444
};
425445

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

src/node_worker.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ class Worker : public AsyncWrap {
8484
mutable Mutex mutex_;
8585

8686
bool thread_joined_ = true;
87-
const char* custom_error_ = nullptr;
87+
std::string custom_error_;
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;

0 commit comments

Comments
 (0)