worker: add name for worker

PR-URL: https://github.com/nodejs/node/pull/59213
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
theanarkh 2025-08-05 21:45:41 +08:00 committed by GitHub
parent a4ce69e05f
commit 3090def635
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 118 additions and 5 deletions

View File

@ -721,6 +721,17 @@ An integer identifier for the current thread. On the corresponding worker object
(if there is any), it is available as [`worker.threadId`][]. (if there is any), it is available as [`worker.threadId`][].
This value is unique for each [`Worker`][] instance inside a single process. This value is unique for each [`Worker`][] instance inside a single process.
## `worker.threadName`
<!-- YAML
added: REPLACEME
-->
* {string|null}
A string identifier for the current thread or null if the thread is not running.
On the corresponding worker object (if there is any), it is available as [`worker.threadName`][].
## `worker.workerData` ## `worker.workerData`
<!-- YAML <!-- YAML
@ -2015,6 +2026,17 @@ An integer identifier for the referenced thread. Inside the worker thread,
it is available as [`require('node:worker_threads').threadId`][]. it is available as [`require('node:worker_threads').threadId`][].
This value is unique for each `Worker` instance inside a single process. This value is unique for each `Worker` instance inside a single process.
### `worker.threadName`
<!-- YAML
added: REPLACEME
-->
* {string|null}
A string identifier for the referenced thread or null if the thread is not running.
Inside the worker thread, it is available as [`require('node:worker_threads').threadName`][].
### `worker.unref()` ### `worker.unref()`
<!-- YAML <!-- YAML
@ -2145,6 +2167,7 @@ thread spawned will spawn another until the application crashes.
[`require('node:worker_threads').parentPort.postMessage()`]: #workerpostmessagevalue-transferlist [`require('node:worker_threads').parentPort.postMessage()`]: #workerpostmessagevalue-transferlist
[`require('node:worker_threads').parentPort`]: #workerparentport [`require('node:worker_threads').parentPort`]: #workerparentport
[`require('node:worker_threads').threadId`]: #workerthreadid [`require('node:worker_threads').threadId`]: #workerthreadid
[`require('node:worker_threads').threadName`]: #workerthreadname
[`require('node:worker_threads').workerData`]: #workerworkerdata [`require('node:worker_threads').workerData`]: #workerworkerdata
[`trace_events`]: tracing.md [`trace_events`]: tracing.md
[`v8.getHeapSnapshot()`]: v8.md#v8getheapsnapshotoptions [`v8.getHeapSnapshot()`]: v8.md#v8getheapsnapshotoptions
@ -2155,6 +2178,7 @@ thread spawned will spawn another until the application crashes.
[`worker.postMessage()`]: #workerpostmessagevalue-transferlist [`worker.postMessage()`]: #workerpostmessagevalue-transferlist
[`worker.terminate()`]: #workerterminate [`worker.terminate()`]: #workerterminate
[`worker.threadId`]: #workerthreadid_1 [`worker.threadId`]: #workerthreadid_1
[`worker.threadName`]: #workerthreadname_1
[async-resource-worker-pool]: async_context.md#using-asyncresource-for-a-worker-thread-pool [async-resource-worker-pool]: async_context.md#using-asyncresource-for-a-worker-thread-pool
[browser `LockManager`]: https://developer.mozilla.org/en-US/docs/Web/API/LockManager [browser `LockManager`]: https://developer.mozilla.org/en-US/docs/Web/API/LockManager
[browser `MessagePort`]: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort [browser `MessagePort`]: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort

View File

@ -73,6 +73,7 @@ const {
isInternalThread, isInternalThread,
resourceLimits: resourceLimitsRaw, resourceLimits: resourceLimitsRaw,
threadId, threadId,
threadName,
Worker: WorkerImpl, Worker: WorkerImpl,
kMaxYoungGenerationSizeMb, kMaxYoungGenerationSizeMb,
kMaxOldGenerationSizeMb, kMaxOldGenerationSizeMb,
@ -425,6 +426,12 @@ class Worker extends EventEmitter {
return this[kHandle].threadId; return this[kHandle].threadId;
} }
get threadName() {
if (this[kHandle] === null) return null;
return this[kHandle].threadName;
}
get stdin() { get stdin() {
return this[kParentSideStdio].stdin; return this[kParentSideStdio].stdin;
} }
@ -589,6 +596,7 @@ module.exports = {
getEnvironmentData, getEnvironmentData,
assignEnvironmentData, assignEnvironmentData,
threadId, threadId,
threadName,
InternalWorker, InternalWorker,
Worker, Worker,
}; };

View File

@ -8,6 +8,7 @@ const {
setEnvironmentData, setEnvironmentData,
getEnvironmentData, getEnvironmentData,
threadId, threadId,
threadName,
Worker, Worker,
} = require('internal/worker'); } = require('internal/worker');
@ -44,6 +45,7 @@ module.exports = {
resourceLimits, resourceLimits,
postMessageToThread, postMessageToThread,
threadId, threadId,
threadName,
SHARE_ENV, SHARE_ENV,
Worker, Worker,
parentPort: null, parentPort: null,

View File

@ -414,6 +414,25 @@ Environment* CreateEnvironment(
EnvironmentFlags::Flags flags, EnvironmentFlags::Flags flags,
ThreadId thread_id, ThreadId thread_id,
std::unique_ptr<InspectorParentHandle> inspector_parent_handle) { std::unique_ptr<InspectorParentHandle> inspector_parent_handle) {
return CreateEnvironment(isolate_data,
context,
args,
exec_args,
flags,
thread_id,
std::move(inspector_parent_handle),
{});
}
Environment* CreateEnvironment(
IsolateData* isolate_data,
Local<Context> context,
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args,
EnvironmentFlags::Flags flags,
ThreadId thread_id,
std::unique_ptr<InspectorParentHandle> inspector_parent_handle,
std::string_view thread_name) {
Isolate* isolate = isolate_data->isolate(); Isolate* isolate = isolate_data->isolate();
Isolate::Scope isolate_scope(isolate); Isolate::Scope isolate_scope(isolate);
@ -434,7 +453,8 @@ Environment* CreateEnvironment(
exec_args, exec_args,
env_snapshot_info, env_snapshot_info,
flags, flags,
thread_id); thread_id,
thread_name);
CHECK_NOT_NULL(env); CHECK_NOT_NULL(env);
if (use_snapshot) { if (use_snapshot) {

View File

@ -695,6 +695,10 @@ inline uint64_t Environment::thread_id() const {
return thread_id_; return thread_id_;
} }
inline std::string_view Environment::thread_name() const {
return thread_name_;
}
inline worker::Worker* Environment::worker_context() const { inline worker::Worker* Environment::worker_context() const {
return isolate_data()->worker_context(); return isolate_data()->worker_context();
} }

View File

@ -784,7 +784,8 @@ Environment::Environment(IsolateData* isolate_data,
const std::vector<std::string>& exec_args, const std::vector<std::string>& exec_args,
const EnvSerializeInfo* env_info, const EnvSerializeInfo* env_info,
EnvironmentFlags::Flags flags, EnvironmentFlags::Flags flags,
ThreadId thread_id) ThreadId thread_id,
std::string_view thread_name)
: isolate_(isolate), : isolate_(isolate),
external_memory_accounter_(new ExternalMemoryAccounter()), external_memory_accounter_(new ExternalMemoryAccounter()),
isolate_data_(isolate_data), isolate_data_(isolate_data),
@ -811,7 +812,8 @@ Environment::Environment(IsolateData* isolate_data,
flags_(flags), flags_(flags),
thread_id_(thread_id.id == static_cast<uint64_t>(-1) thread_id_(thread_id.id == static_cast<uint64_t>(-1)
? AllocateEnvironmentThreadId().id ? AllocateEnvironmentThreadId().id
: thread_id.id) { : thread_id.id),
thread_name_(thread_name) {
if (!is_main_thread()) { if (!is_main_thread()) {
// If this is a Worker thread, we can always safely use the parent's // If this is a Worker thread, we can always safely use the parent's
// Isolate's code cache because of the shared read-only heap. // Isolate's code cache because of the shared read-only heap.

View File

@ -660,7 +660,8 @@ class Environment final : public MemoryRetainer {
const std::vector<std::string>& exec_args, const std::vector<std::string>& exec_args,
const EnvSerializeInfo* env_info, const EnvSerializeInfo* env_info,
EnvironmentFlags::Flags flags, EnvironmentFlags::Flags flags,
ThreadId thread_id); ThreadId thread_id,
std::string_view thread_name = "");
void InitializeMainContext(v8::Local<v8::Context> context, void InitializeMainContext(v8::Local<v8::Context> context,
const EnvSerializeInfo* env_info); const EnvSerializeInfo* env_info);
~Environment() override; ~Environment() override;
@ -807,6 +808,7 @@ class Environment final : public MemoryRetainer {
inline bool should_start_debug_signal_handler() const; inline bool should_start_debug_signal_handler() const;
inline bool no_browser_globals() const; inline bool no_browser_globals() const;
inline uint64_t thread_id() const; inline uint64_t thread_id() const;
inline std::string_view thread_name() const;
inline worker::Worker* worker_context() const; inline worker::Worker* worker_context() const;
Environment* worker_parent_env() const; Environment* worker_parent_env() const;
inline void add_sub_worker_context(worker::Worker* context); inline void add_sub_worker_context(worker::Worker* context);
@ -1172,6 +1174,7 @@ class Environment final : public MemoryRetainer {
uint64_t flags_; uint64_t flags_;
uint64_t thread_id_; uint64_t thread_id_;
std::string thread_name_;
std::unordered_set<worker::Worker*> sub_worker_contexts_; std::unordered_set<worker::Worker*> sub_worker_contexts_;
#if HAVE_INSPECTOR #if HAVE_INSPECTOR

View File

@ -390,6 +390,7 @@
V(table_string, "table") \ V(table_string, "table") \
V(target_string, "target") \ V(target_string, "target") \
V(thread_id_string, "threadId") \ V(thread_id_string, "threadId") \
V(thread_name_string, "threadName") \
V(ticketkeycallback_string, "onticketkeycallback") \ V(ticketkeycallback_string, "onticketkeycallback") \
V(timeout_string, "timeout") \ V(timeout_string, "timeout") \
V(time_to_first_byte_string, "timeToFirstByte") \ V(time_to_first_byte_string, "timeToFirstByte") \

View File

@ -685,6 +685,16 @@ NODE_EXTERN Environment* CreateEnvironment(
ThreadId thread_id = {} /* allocates a thread id automatically */, ThreadId thread_id = {} /* allocates a thread id automatically */,
std::unique_ptr<InspectorParentHandle> inspector_parent_handle = {}); std::unique_ptr<InspectorParentHandle> inspector_parent_handle = {});
NODE_EXTERN Environment* CreateEnvironment(
IsolateData* isolate_data,
v8::Local<v8::Context> context,
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args,
EnvironmentFlags::Flags flags,
ThreadId thread_id,
std::unique_ptr<InspectorParentHandle> inspector_parent_handle,
std::string_view thread_name);
// Returns a handle that can be passed to `LoadEnvironment()`, making the // Returns a handle that can be passed to `LoadEnvironment()`, making the
// child Environment accessible to the inspector as if it were a Node.js Worker. // child Environment accessible to the inspector as if it were a Node.js Worker.
// `child_thread_id` can be created using `AllocateEnvironmentThreadId()` // `child_thread_id` can be created using `AllocateEnvironmentThreadId()`

View File

@ -33,6 +33,7 @@ using v8::Local;
using v8::Locker; using v8::Locker;
using v8::Maybe; using v8::Maybe;
using v8::Name; using v8::Name;
using v8::NewStringType;
using v8::Null; using v8::Null;
using v8::Number; using v8::Number;
using v8::Object; using v8::Object;
@ -89,6 +90,15 @@ Worker::Worker(Environment* env,
Number::New(env->isolate(), static_cast<double>(thread_id_.id))) Number::New(env->isolate(), static_cast<double>(thread_id_.id)))
.Check(); .Check();
object()
->Set(env->context(),
env->thread_name_string(),
String::NewFromUtf8(env->isolate(),
name_.data(),
NewStringType::kNormal,
name_.size())
.ToLocalChecked())
.Check();
// Without this check, to use the permission model with // Without this check, to use the permission model with
// workers (--allow-worker) one would need to pass --allow-inspector as well // workers (--allow-worker) one would need to pass --allow-inspector as well
if (env->permission()->is_granted( if (env->permission()->is_granted(
@ -365,7 +375,8 @@ void Worker::Run() {
std::move(exec_argv_), std::move(exec_argv_),
static_cast<EnvironmentFlags::Flags>(environment_flags_), static_cast<EnvironmentFlags::Flags>(environment_flags_),
thread_id_, thread_id_,
std::move(inspector_parent_handle_))); std::move(inspector_parent_handle_),
name_));
if (is_stopped()) return; if (is_stopped()) return;
CHECK_NOT_NULL(env_); CHECK_NOT_NULL(env_);
env_->set_env_vars(std::move(env_vars_)); env_->set_env_vars(std::move(env_vars_));
@ -1239,6 +1250,16 @@ void CreateWorkerPerContextProperties(Local<Object> target,
Number::New(isolate, static_cast<double>(env->thread_id()))) Number::New(isolate, static_cast<double>(env->thread_id())))
.Check(); .Check();
target
->Set(env->context(),
env->thread_name_string(),
String::NewFromUtf8(isolate,
env->thread_name().data(),
NewStringType::kNormal,
env->thread_name().size())
.ToLocalChecked())
.Check();
target target
->Set(env->context(), ->Set(env->context(),
FIXED_ONE_BYTE_STRING(isolate, "isMainThread"), FIXED_ONE_BYTE_STRING(isolate, "isMainThread"),

View File

@ -0,0 +1,18 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { Worker, threadName, workerData } = require('worker_threads');
const name = 'test-worker-thread-name';
if (workerData?.isWorker) {
assert.strictEqual(threadName, name);
process.exit(0);
} else {
const w = new Worker(__filename, { name, workerData: { isWorker: true } });
assert.strictEqual(w.threadName, name);
w.on('exit', common.mustCall(() => {
assert.strictEqual(w.threadName, null);
}));
}