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
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
@@ -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
@@ -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
26 changes: 13 additions & 13 deletions doc/api/worker_threads.md
Original file line number Diff line number Diff line change
@@ -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
@@ -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
@@ -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
14 changes: 13 additions & 1 deletion lib/internal/worker.js
Original file line number Diff line number Diff line change
@@ -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() {
7 changes: 1 addition & 6 deletions src/node_worker.cc
Original file line number Diff line number Diff line change
@@ -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());
@@ -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();
}
@@ -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) {
1 change: 0 additions & 1 deletion src/node_worker.h
Original file line number Diff line number Diff line change
@@ -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_;
2 changes: 1 addition & 1 deletion test/parallel/test-worker-dns-terminate.js
Original file line number Diff line number Diff line change
@@ -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
@@ -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);
}));