src: add uv_thread_set/getname() methods (#4599)
`uv_thread_setname()` sets the name of the current thread. Different platforms define different limits on the max number of characters a thread name can be: Linux, IBMi (16), macOS (64), Windows (32767), and NetBSD (32), etc. `uv_thread_setname()` will truncate it in case `name` is larger than the limit of the platform. `uv_thread_getname()` gets the name of the thread specified by `tid`. The thread name is copied into the buffer pointed to by `name`. The `size` parameter specifies the size of the buffer pointed to by `name`. The buffer should be large enough to hold the name of the thread plus the trailing NUL, or it will be truncated to fit.
This commit is contained in:
parent
b7d07d78e9
commit
61c966cf0b
@ -667,6 +667,7 @@ if(LIBUV_BUILD_TESTS)
|
||||
test/test-thread-affinity.c
|
||||
test/test-thread-equal.c
|
||||
test/test-thread.c
|
||||
test/test-thread-name.c
|
||||
test/test-thread-priority.c
|
||||
test/test-threadpool-cancel.c
|
||||
test/test-threadpool.c
|
||||
|
||||
@ -294,6 +294,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
||||
test/test-thread-equal.c \
|
||||
test/test-thread.c \
|
||||
test/test-thread-affinity.c \
|
||||
test/test-thread-name.c \
|
||||
test/test-thread-priority.c \
|
||||
test/test-threadpool-cancel.c \
|
||||
test/test-threadpool.c \
|
||||
|
||||
@ -140,6 +140,23 @@ Threads
|
||||
.. c:function:: int uv_thread_join(uv_thread_t *tid)
|
||||
.. c:function:: int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2)
|
||||
|
||||
.. c:function:: int uv_thread_setname(const char* name)
|
||||
|
||||
Sets the name of the current thread. Different platforms define different limits on the max number of characters
|
||||
a thread name can be: Linux, IBM i (16), macOS (64), Windows (32767), and NetBSD (32), etc. `uv_thread_setname()`
|
||||
will truncate it in case `name` is larger than the limit of the platform.
|
||||
|
||||
.. versionadded:: 1.50.0
|
||||
|
||||
.. c:function:: int uv_thread_getname(uv_thread_t* tid, char* name, size_t* size)
|
||||
|
||||
Gets the name of the thread specified by `tid`. The thread name is copied, with the trailing NUL, into the buffer
|
||||
pointed to by `name`. The `size` parameter specifies the size of the buffer pointed to by `name`.
|
||||
The buffer should be large enough to hold the name of the thread plus the trailing NUL, or it will be truncated to fit
|
||||
with the trailing NUL.
|
||||
|
||||
.. versionadded:: 1.50.0
|
||||
|
||||
.. c:function:: int uv_thread_setpriority(uv_thread_t tid, int priority)
|
||||
If the function succeeds, the return value is 0.
|
||||
If the function fails, the return value is less than zero.
|
||||
|
||||
@ -1900,6 +1900,9 @@ UV_EXTERN int uv_thread_getcpu(void);
|
||||
UV_EXTERN uv_thread_t uv_thread_self(void);
|
||||
UV_EXTERN int uv_thread_join(uv_thread_t *tid);
|
||||
UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2);
|
||||
UV_EXTERN int uv_thread_setname(const char* name);
|
||||
UV_EXTERN int uv_thread_getname(uv_thread_t* tid, char* name, size_t size);
|
||||
|
||||
|
||||
/* The presence of these unions force similar struct layout. */
|
||||
#define XX(_, name) uv_ ## name ## _t name;
|
||||
|
||||
@ -33,25 +33,9 @@
|
||||
#include "darwin-stub.h"
|
||||
#endif
|
||||
|
||||
|
||||
static int uv__pthread_setname_np(const char* name) {
|
||||
char namebuf[64]; /* MAXTHREADNAMESIZE */
|
||||
int err;
|
||||
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
|
||||
err = pthread_setname_np(namebuf);
|
||||
if (err)
|
||||
return UV__ERR(err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__set_process_title(const char* title) {
|
||||
#if TARGET_OS_IPHONE
|
||||
return uv__pthread_setname_np(title);
|
||||
return uv__thread_setname(title);
|
||||
#else
|
||||
CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,
|
||||
const char*,
|
||||
@ -177,7 +161,7 @@ int uv__set_process_title(const char* title) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
uv__pthread_setname_np(title); /* Don't care if it fails. */
|
||||
uv__thread_setname(title); /* Don't care if it fails. */
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
|
||||
@ -327,6 +327,8 @@ void uv__prepare_close(uv_prepare_t* handle);
|
||||
void uv__process_close(uv_process_t* handle);
|
||||
void uv__stream_close(uv_stream_t* handle);
|
||||
void uv__tcp_close(uv_tcp_t* handle);
|
||||
int uv__thread_setname(const char* name);
|
||||
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size);
|
||||
size_t uv__thread_stack_size(void);
|
||||
void uv__udp_close(uv_udp_t* handle);
|
||||
void uv__udp_finish_close(uv_udp_t* handle);
|
||||
|
||||
@ -23,6 +23,9 @@
|
||||
#include "internal.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#ifdef __OpenBSD__
|
||||
#include <pthread_np.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
@ -297,6 +300,18 @@ int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
|
||||
return pthread_equal(*t1, *t2);
|
||||
}
|
||||
|
||||
int uv_thread_setname(const char* name) {
|
||||
if (name == NULL)
|
||||
return UV_EINVAL;
|
||||
return uv__thread_setname(name);
|
||||
}
|
||||
|
||||
int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
if (name == NULL || size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
return uv__thread_getname(tid, name, size);
|
||||
}
|
||||
|
||||
int uv_mutex_init(uv_mutex_t* mutex) {
|
||||
#if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK)
|
||||
@ -881,3 +896,80 @@ void uv_key_set(uv_key_t* key, void* value) {
|
||||
if (pthread_setspecific(*key, value))
|
||||
abort();
|
||||
}
|
||||
|
||||
#if defined(_AIX) || defined(__MVS__) || defined(__PASE__)
|
||||
int uv__thread_setname(const char* name) {
|
||||
return UV_ENOSYS;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
int uv__thread_setname(const char* name) {
|
||||
char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
int err = pthread_setname_np(namebuf);
|
||||
if (err)
|
||||
return UV__ERR(errno);
|
||||
return 0;
|
||||
}
|
||||
#elif defined(__NetBSD__)
|
||||
int uv__thread_setname(const char* name) {
|
||||
char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
return UV__ERR(pthread_setname_np(pthread_self(), "%s", namebuf));
|
||||
}
|
||||
#elif defined(__OpenBSD__)
|
||||
int uv__thread_setname(const char* name) {
|
||||
char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
pthread_set_name_np(pthread_self(), namebuf);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int uv__thread_setname(const char* name) {
|
||||
char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
return UV__ERR(pthread_setname_np(pthread_self(), namebuf));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (defined(__ANDROID_API__) && __ANDROID_API__ < 26) || \
|
||||
defined(_AIX) || \
|
||||
defined(__MVS__) || \
|
||||
defined(__PASE__)
|
||||
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
return UV_ENOSYS;
|
||||
}
|
||||
#elif defined(__OpenBSD__)
|
||||
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
char thread_name[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
pthread_get_name_np(*tid, thread_name, sizeof(thread_name));
|
||||
strncpy(name, thread_name, size - 1);
|
||||
name[size - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
char thread_name[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
if (pthread_getname_np(*tid, thread_name, sizeof(thread_name)) != 0)
|
||||
return UV__ERR(errno);
|
||||
|
||||
strncpy(name, thread_name, size - 1);
|
||||
name[size - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
int r;
|
||||
char thread_name[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
r = pthread_getname_np(*tid, thread_name, sizeof(thread_name));
|
||||
if (r != 0)
|
||||
return UV__ERR(r);
|
||||
|
||||
strncpy(name, thread_name, size - 1);
|
||||
name[size - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -428,4 +428,18 @@ struct uv__loop_internal_fields_s {
|
||||
#endif /* __linux__ */
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP 32767
|
||||
#elif defined(__APPLE__)
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP 64
|
||||
#elif defined(__NetBSD__) || defined(__illumos__)
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP PTHREAD_MAX_NAMELEN_NP
|
||||
#elif defined (__linux__)
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP 16
|
||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP (MAXCOMLEN + 1)
|
||||
#else
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP 16
|
||||
#endif
|
||||
|
||||
#endif /* UV_COMMON_H_ */
|
||||
|
||||
@ -278,6 +278,79 @@ int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_setname(const char* name) {
|
||||
#ifdef __MINGW32__
|
||||
return UV_ENOSYS;
|
||||
#else
|
||||
HRESULT hr;
|
||||
WCHAR* namew;
|
||||
int err;
|
||||
char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
|
||||
if (name == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
|
||||
namew = NULL;
|
||||
err = uv__convert_utf8_to_utf16(namebuf, &namew);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
hr = SetThreadDescription(GetCurrentThread(), namew);
|
||||
uv__free(namew);
|
||||
if (FAILED(hr))
|
||||
return uv_translate_sys_error(HRESULT_CODE(hr));
|
||||
|
||||
return 0;
|
||||
#endif /* __MINGW32__ */
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
#ifdef __MINGW32__
|
||||
return UV_ENOSYS;
|
||||
#else
|
||||
HRESULT hr;
|
||||
WCHAR* namew;
|
||||
char* thread_name;
|
||||
size_t buf_size;
|
||||
int r;
|
||||
DWORD exit_code;
|
||||
|
||||
if (name == NULL || size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (tid == NULL || *tid == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* Check if the thread handle is valid */
|
||||
if (!GetExitCodeThread(*tid, &exit_code) || exit_code != STILL_ACTIVE)
|
||||
return UV_ENOENT;
|
||||
|
||||
namew = NULL;
|
||||
thread_name = NULL;
|
||||
hr = GetThreadDescription(*tid, &namew);
|
||||
if (FAILED(hr))
|
||||
return uv_translate_sys_error(HRESULT_CODE(hr));
|
||||
|
||||
buf_size = size;
|
||||
r = uv__copy_utf16_to_utf8(namew, -1, name, &buf_size);
|
||||
if (r == UV_ENOBUFS) {
|
||||
r = uv__convert_utf16_to_utf8(namew, wcslen(namew), &thread_name);
|
||||
if (r == 0) {
|
||||
uv__strscpy(name, thread_name, size);
|
||||
uv__free(thread_name);
|
||||
}
|
||||
}
|
||||
|
||||
LocalFree(namew);
|
||||
return r;
|
||||
#endif /* __MINGW32__ */
|
||||
}
|
||||
|
||||
|
||||
int uv_mutex_init(uv_mutex_t* mutex) {
|
||||
InitializeCriticalSection(mutex);
|
||||
return 0;
|
||||
|
||||
@ -478,6 +478,7 @@ TEST_DECLARE (thread_create)
|
||||
TEST_DECLARE (thread_equal)
|
||||
TEST_DECLARE (thread_affinity)
|
||||
TEST_DECLARE (thread_priority)
|
||||
TEST_DECLARE (thread_name)
|
||||
TEST_DECLARE (dlerror)
|
||||
#if (defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))) && \
|
||||
!defined(__sun)
|
||||
@ -1193,6 +1194,7 @@ TASK_LIST_START
|
||||
TEST_ENTRY (thread_equal)
|
||||
TEST_ENTRY (thread_affinity)
|
||||
TEST_ENTRY (thread_priority)
|
||||
TEST_ENTRY (thread_name)
|
||||
TEST_ENTRY (dlerror)
|
||||
TEST_ENTRY (ip4_addr)
|
||||
TEST_ENTRY (ip6_addr_link_local)
|
||||
|
||||
143
test/test-thread-name.c
Normal file
143
test/test-thread-name.c
Normal file
@ -0,0 +1,143 @@
|
||||
/* Copyright libuv project contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "task.h"
|
||||
#include "../src/uv-common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
struct semaphores {
|
||||
uv_sem_t main;
|
||||
uv_sem_t worker;
|
||||
};
|
||||
|
||||
static void thread_run(void* arg) {
|
||||
int r;
|
||||
char thread_name[16];
|
||||
struct semaphores* sem;
|
||||
uv_thread_t thread;
|
||||
|
||||
sem = arg;
|
||||
|
||||
#ifdef _WIN32
|
||||
/* uv_thread_self isn't defined for the main thread on Windows. */
|
||||
thread = GetCurrentThread();
|
||||
#else
|
||||
thread = uv_thread_self();
|
||||
#endif
|
||||
|
||||
r = uv_thread_setname("worker-thread");
|
||||
ASSERT_OK(r);
|
||||
|
||||
uv_sem_post(&sem->worker);
|
||||
|
||||
r = uv_thread_getname(&thread, thread_name, sizeof(thread_name));
|
||||
ASSERT_OK(r);
|
||||
|
||||
ASSERT_STR_EQ(thread_name, "worker-thread");
|
||||
|
||||
uv_sem_wait(&sem->main);
|
||||
}
|
||||
|
||||
TEST_IMPL(thread_name) {
|
||||
int r;
|
||||
uv_thread_t threads[2];
|
||||
char tn[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
char thread_name[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
char long_thread_name[UV_PTHREAD_MAX_NAMELEN_NP + 1];
|
||||
struct semaphores sem;
|
||||
|
||||
#if defined(__MINGW32__) || \
|
||||
defined(__ANDROID_API__) && __ANDROID_API__ < 26 || \
|
||||
defined(_AIX) || \
|
||||
defined(__MVS__) || \
|
||||
defined(__PASE__)
|
||||
RETURN_SKIP("API not available on this platform");
|
||||
#endif
|
||||
|
||||
ASSERT_OK(uv_sem_init(&sem.main, 0));
|
||||
ASSERT_OK(uv_sem_init(&sem.worker, 0));
|
||||
|
||||
memset(thread_name, 'a', sizeof(thread_name) - 1);
|
||||
thread_name[sizeof(thread_name) - 1] = '\0';
|
||||
|
||||
memset(long_thread_name, 'a', sizeof(long_thread_name) - 1);
|
||||
long_thread_name[sizeof(long_thread_name) - 1] = '\0';
|
||||
|
||||
#ifdef _WIN32
|
||||
/* uv_thread_self isn't defined for the main thread on Windows. */
|
||||
threads[0] = GetCurrentThread();
|
||||
#else
|
||||
threads[0] = uv_thread_self();
|
||||
#endif
|
||||
|
||||
r = uv_thread_getname(&threads[0], tn, sizeof(tn));
|
||||
ASSERT_OK(r);
|
||||
|
||||
r = uv_thread_setname(long_thread_name);
|
||||
ASSERT_OK(r);
|
||||
|
||||
r = uv_thread_getname(&threads[0], tn, sizeof(tn));
|
||||
ASSERT_OK(r);
|
||||
ASSERT_STR_EQ(tn, thread_name);
|
||||
|
||||
r = uv_thread_setname(thread_name);
|
||||
ASSERT_OK(r);
|
||||
|
||||
r = uv_thread_getname(&threads[0], tn, sizeof(tn));
|
||||
ASSERT_OK(r);
|
||||
ASSERT_STR_EQ(tn, thread_name);
|
||||
|
||||
r = uv_thread_getname(&threads[0], tn, 3);
|
||||
ASSERT_OK(r);
|
||||
ASSERT_EQ(strlen(tn), 2);
|
||||
ASSERT_OK(memcmp(thread_name, tn, 2));
|
||||
|
||||
/* Illumos doesn't support non-ASCII thread names. */
|
||||
#ifndef __illumos__
|
||||
r = uv_thread_setname("~½¬{½");
|
||||
ASSERT_OK(r);
|
||||
|
||||
r = uv_thread_getname(&threads[0], tn, sizeof(tn));
|
||||
ASSERT_OK(r);
|
||||
ASSERT_STR_EQ(tn, "~½¬{½");
|
||||
#endif
|
||||
|
||||
ASSERT_OK(uv_thread_create(threads + 1, thread_run, &sem));
|
||||
|
||||
uv_sem_wait(&sem.worker);
|
||||
|
||||
r = uv_thread_getname(threads + 1, tn, sizeof(tn));
|
||||
ASSERT_OK(r);
|
||||
|
||||
ASSERT_STR_EQ(tn, "worker-thread");
|
||||
|
||||
uv_sem_post(&sem.main);
|
||||
|
||||
ASSERT_OK(uv_thread_join(threads + 1));
|
||||
|
||||
uv_sem_destroy(&sem.main);
|
||||
uv_sem_destroy(&sem.worker);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user