Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

worker: refactor worker.terminate() #28021

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions doc/api/deprecations.md
Original file line number Diff line number Diff line change
Expand Up @@ -2484,6 +2484,20 @@ The legacy HTTP parser, used by default in versions of Node.js prior to 12.0.0,
is deprecated. This deprecation applies to users of the
[`--http-parser=legacy`][] command-line flag.

<a id="DEP0XXX"></a>
### DEP0XXX: worker.terminate() with callback
<!-- YAML
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/28021
description: Runtime deprecation.
-->

Type: Runtime

Passing a callback to [`worker.terminate()`][] is deprecated. Use the returned
`Promise` instead, or a listener to the worker’s `'exit'` event.

[`--http-parser=legacy`]: cli.html#cli_http_parser_library
[`--pending-deprecation`]: cli.html#cli_pending_deprecation
[`Buffer.allocUnsafeSlow(size)`]: buffer.html#buffer_class_method_buffer_allocunsafeslow_size
Expand Down Expand Up @@ -2576,6 +2590,7 @@ is deprecated. This deprecation applies to users of the
[`util.types`]: util.html#util_util_types
[`util`]: util.html
[`worker.exitedAfterDisconnect`]: cluster.html#cluster_worker_exitedafterdisconnect
[`worker.terminate()`]: worker_threads.html#worker_threads_worker_terminate
[`zlib.bytesWritten`]: zlib.html#zlib_zlib_byteswritten
[Legacy URL API]: url.html#url_legacy_url_api
[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
Expand Down
26 changes: 13 additions & 13 deletions doc/api/worker_threads.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,24 +617,23 @@ inside the worker thread. If `stdout: true` was not passed to the
[`Worker`][] constructor, then data will be piped to the parent thread's
[`process.stdout`][] stream.

### worker.terminate([callback])
### worker.terminate()
<!-- YAML
added: v10.5.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/28021
description: This function now returns a Promise.
Passing a callback is deprecated, and was useless up to this
version, as the Worker was actually terminated synchronously.
Terminating is now a fully asynchronous operation.
-->

* `callback` {Function}
* `err` {Error}
* `exitCode` {integer}
* Returns: {Promise}

Stop all JavaScript execution in the worker thread as soon as possible.
`callback` is an optional function that is invoked once this operation is known
to have completed.

**Warning**: Currently, not all code in the internals of Node.js is prepared to
expect termination at arbitrary points in time and may crash if it encounters
that condition. Consequently, only call `.terminate()` if it is known that the
Worker thread is not accessing Node.js core modules other than what is exposed
in the `worker` module.
Returns a Promise for the exit code that is fulfilled when the
[`'exit'` event][] is emitted.

### worker.threadId
<!-- YAML
Expand All @@ -657,6 +656,7 @@ active handle in the event system. If the worker is already `unref()`ed calling
`unref()` again will have no effect.

[`'close'` event]: #worker_threads_event_close
[`'exit'` event]: #worker_threads_event_exit
[`AsyncResource`]: async_hooks.html#async_hooks_class_asyncresource
[`Buffer`]: buffer.html
[`EventEmitter`]: events.html
Expand Down Expand Up @@ -690,7 +690,7 @@ active handle in the event system. If the worker is already `unref()`ed calling
[`worker.on('message')`]: #worker_threads_event_message_1
[`worker.postMessage()`]: #worker_threads_worker_postmessage_value_transferlist
[`worker.SHARE_ENV`]: #worker_threads_worker_share_env
[`worker.terminate()`]: #worker_threads_worker_terminate_callback
[`worker.terminate()`]: #worker_threads_worker_terminate
[`worker.threadId`]: #worker_threads_worker_threadid_1
[Addons worker support]: addons.html#addons_worker_support
[HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
Expand Down
14 changes: 13 additions & 1 deletion lib/internal/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,22 @@ class Worker extends EventEmitter {

debug(`[${threadId}] terminates Worker with ID ${this.threadId}`);

if (typeof callback !== 'undefined')
if (typeof callback === 'function') {
process.emitWarning(
'Passing a callback to worker.terminate() is deprecated. ' +
'It returns a Promise instead.',
'DeprecationWarning', 'DEP0XXX');
this.once('exit', (exitCode) => callback(null, exitCode));
}

this[kHandle].stopThread();

// Do not use events.once() here, because the 'exit' event will always be
// emitted regardless of any errors, and the point is to only resolve
// once the thread has actually stopped.
return new Promise((resolve) => {
this.once('exit', resolve);
});
}

ref() {
Expand Down
7 changes: 1 addition & 6 deletions src/node_worker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -350,11 +350,8 @@ void Worker::JoinThread() {
thread_joined_ = true;

env()->remove_sub_worker_context(this);
OnThreadStopped();
on_thread_finished_.Uninstall();
}

void Worker::OnThreadStopped() {
{
HandleScope handle_scope(env()->isolate());
Context::Scope context_scope(env()->context());
Expand All @@ -368,7 +365,7 @@ void Worker::OnThreadStopped() {
MakeCallback(env()->onexit_string(), 1, &code);
}

// JoinThread() cleared all libuv handles bound to this Worker,
// We cleared all libuv handles bound to this Worker above,
// the C++ object is no longer needed for anything now.
MakeWeak();
}
Expand Down Expand Up @@ -532,8 +529,6 @@ void Worker::StopThread(const FunctionCallbackInfo<Value>& args) {

Debug(w, "Worker %llu is getting stopped by parent", w->thread_id_);
w->Exit(1);
w->JoinThread();
delete w;
}

void Worker::Ref(const FunctionCallbackInfo<Value>& args) {
Expand Down
1 change: 0 additions & 1 deletion src/node_worker.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ class Worker : public AsyncWrap {
static void Unref(const v8::FunctionCallbackInfo<v8::Value>& args);

private:
void OnThreadStopped();
void CreateEnvMessagePort(Environment* env);

std::shared_ptr<PerIsolateOptions> per_isolate_opts_;
Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-worker-dns-terminate.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ require('worker_threads').parentPort.postMessage('0');

w.on('message', common.mustCall(() => {
// This should not crash the worker during a DNS request.
w.terminate(common.mustCall());
w.terminate().then(common.mustCall());
}));
8 changes: 7 additions & 1 deletion test/parallel/test-worker-nexttick-terminate.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ process.nextTick(() => {
});
`, { eval: true });

// Test deprecation of .terminate() with callback.
common.expectWarning(
'DeprecationWarning',
'Passing a callback to worker.terminate() is deprecated. ' +
'It returns a Promise instead.', 'DEP0XXX');

w.on('message', common.mustCall(() => {
setTimeout(() => {
w.terminate(common.mustCall());
w.terminate(common.mustCall()).then(common.mustCall());
}, 1);
}));