diff --git a/doc/api/worker_threads.md b/doc/api/worker_threads.md index 4e63345d208493..45fe04d37fe823 100644 --- a/doc/api/worker_threads.md +++ b/doc/api/worker_threads.md @@ -1229,9 +1229,18 @@ changes: used for generated code. * `stackSizeMb` {number} The default maximum stack size for the thread. Small values may lead to unusable Worker instances. **Default:** `4`. - * `name` {string} An optional `name` to be appended to the worker title - for debugging/identification purposes, making the final title as - `[worker ${id}] ${name}`. **Default:** `''`. + * `name` {string} An optional `name` to be replaced in the thread name + and to the worker title for debugging/identification purposes, + making the final title as `[worker ${id}] ${name}`. + This parameter has a maximum allowed size, depending on the operating + system. If the provided name exceeds the limit, it will be truncated + * Maximum sizes: + * Windows: 32,767 characters + * macOS: 64 characters + * Linux: 16 characters + * NetBSD: limited to `PTHREAD_MAX_NAMELEN_NP` + * FreeBSD and OpenBSD: limited to `MAXCOMLEN` + **Default:** `'WorkerThread'`. ### Event: `'error'` diff --git a/lib/internal/worker.js b/lib/internal/worker.js index e5a2cd06892c75..39ca1778c7ddf0 100644 --- a/lib/internal/worker.js +++ b/lib/internal/worker.js @@ -204,7 +204,7 @@ class Worker extends EventEmitter { options.env); } - let name = ''; + let name = 'WorkerThread'; if (options.name) { validateString(options.name, 'options.name'); name = StringPrototypeTrim(options.name); diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index be767c1825d64f..f98060b59ba037 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -86,6 +86,7 @@ static void StartIoThreadWakeup(int signo, siginfo_t* info, void* ucontext) { } inline void* StartIoThreadMain(void* unused) { + uv_thread_setname("SignalInspector"); for (;;) { uv_sem_wait(&start_io_thread_semaphore); Mutex::ScopedLock lock(start_io_thread_async_mutex); diff --git a/src/inspector_io.cc b/src/inspector_io.cc index 4e76e529fcf341..b338d45a795c15 100644 --- a/src/inspector_io.cc +++ b/src/inspector_io.cc @@ -283,6 +283,11 @@ void InspectorIo::ThreadMain(void* io) { } void InspectorIo::ThreadMain() { + int thread_name_error = uv_thread_setname("InspectorIo"); + if (!thread_name_error) [[unlikely]] { + per_process::Debug(node::DebugCategory::INSPECTOR_SERVER, + "Failed to set thread name for Inspector\n"); + } uv_loop_t loop; loop.data = nullptr; int err = uv_loop_init(&loop); diff --git a/src/node.cc b/src/node.cc index 480681d0b02ff8..6b5ae7f7e455f8 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1165,6 +1165,7 @@ InitializeOncePerProcessInternal(const std::vector& args, } if (!(flags & ProcessInitializationFlags::kNoInitializeNodeV8Platform)) { + uv_thread_setname("MainThread"); per_process::v8_platform.Initialize( static_cast(per_process::cli_options->v8_thread_pool_size)); result->platform_ = per_process::v8_platform.Platform(); diff --git a/src/node_platform.cc b/src/node_platform.cc index c042bae4e082cf..3d1cc522db380b 100644 --- a/src/node_platform.cc +++ b/src/node_platform.cc @@ -25,6 +25,7 @@ struct PlatformWorkerData { }; static void PlatformWorkerThread(void* data) { + uv_thread_setname("V8Worker"); std::unique_ptr worker_data(static_cast(data)); diff --git a/src/node_worker.cc b/src/node_worker.cc index 1fc3774948dae3..edb43bc796c320 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -740,6 +740,7 @@ void Worker::StartThread(const FunctionCallbackInfo& args) { Worker* w = static_cast(arg); const uintptr_t stack_top = reinterpret_cast(&arg); + uv_thread_setname(w->name_.c_str()); // Leave a few kilobytes just to make sure we're within limits and have // some space to do work in C++ land. w->stack_base_ = stack_top - (w->stack_size_ - kStackBufferSize); diff --git a/test/addons/uv-thread-name/binding.cc b/test/addons/uv-thread-name/binding.cc new file mode 100644 index 00000000000000..e09cc7ff176833 --- /dev/null +++ b/test/addons/uv-thread-name/binding.cc @@ -0,0 +1,29 @@ +#include +#include +#include + +using v8::FunctionCallbackInfo; +using v8::Isolate; +using v8::String; +using v8::Value; + +void GetThreadName(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + uv_thread_t thread; + char thread_name[16]; +#ifdef _WIN32 + /* uv_thread_self isn't defined for the main thread on Windows. */ + thread = GetCurrentThread(); +#else + thread = uv_thread_self(); +#endif + uv_thread_getname(&thread, thread_name, sizeof(thread_name)); + args.GetReturnValue().Set( + String::NewFromUtf8(isolate, thread_name).ToLocalChecked()); +} + +void init(v8::Local exports) { + NODE_SET_METHOD(exports, "getThreadName", GetThreadName); +} + +NODE_MODULE_CONTEXT_AWARE(NODE_GYP_MODULE_NAME, init) diff --git a/test/addons/uv-thread-name/binding.gyp b/test/addons/uv-thread-name/binding.gyp new file mode 100644 index 00000000000000..55fbe7050f18e4 --- /dev/null +++ b/test/addons/uv-thread-name/binding.gyp @@ -0,0 +1,9 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': [ 'binding.cc' ], + 'includes': ['../common.gypi'], + } + ] +} diff --git a/test/addons/uv-thread-name/test.js b/test/addons/uv-thread-name/test.js new file mode 100644 index 00000000000000..f4ddefb55291f7 --- /dev/null +++ b/test/addons/uv-thread-name/test.js @@ -0,0 +1,35 @@ +'use strict'; +const common = require('../../common'); + +if (common.isAIX) { + common.skip('AIX is not supported by libuv'); +} + +const assert = require('node:assert'); +const { parentPort, Worker, isMainThread } = require('node:worker_threads'); +const bindingPath = require.resolve(`./build/${common.buildType}/binding`); +const binding = require(bindingPath); + +if (isMainThread) { + assert.strictEqual(binding.getThreadName(), 'MainThread'); + + const worker = new Worker(__filename); + worker.on('message', common.mustCall((data) => { + assert.strictEqual(data, 'WorkerThread'); + })); + worker.on('error', common.mustNotCall()); + worker.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); + })); + + const namedWorker = new Worker(__filename, { name: 'NamedThread' }); + namedWorker.on('message', common.mustCall((data) => { + assert.strictEqual(data, 'NamedThread'); + })); + namedWorker.on('error', common.mustNotCall()); + namedWorker.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); + })); +} else { + parentPort.postMessage(binding.getThreadName()); +} diff --git a/test/parallel/test-trace-events-worker-metadata.js b/test/parallel/test-trace-events-worker-metadata.js index 6a8702ccadbc7b..844b3769ce201c 100644 --- a/test/parallel/test-trace-events-worker-metadata.js +++ b/test/parallel/test-trace-events-worker-metadata.js @@ -23,7 +23,7 @@ if (isMainThread) { assert(traces.length > 0); assert(traces.some((trace) => trace.cat === '__metadata' && trace.name === 'thread_name' && - trace.args.name === '[worker 1]')); + trace.args.name === '[worker 1] WorkerThread')); })); })); } else {