mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Add phtread_setname_np() and pthread_getname_np()
This commit is contained in:
parent
114176c304
commit
de511bc71a
8 changed files with 355 additions and 4 deletions
|
@ -91,6 +91,8 @@ void pthread_exit(void *) wontreturn;
|
|||
pthread_t pthread_self(void) pureconst;
|
||||
pthread_id_np_t pthread_getthreadid_np(void);
|
||||
int64_t pthread_getunique_np(pthread_t);
|
||||
int pthread_setname_np(pthread_t, const char *);
|
||||
int pthread_getname_np(pthread_t, char *, size_t);
|
||||
int pthread_getattr_np(pthread_t, pthread_attr_t *);
|
||||
int pthread_attr_init(pthread_attr_t *);
|
||||
int pthread_attr_destroy(pthread_attr_t *);
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
* The top 16 bytes of a stack can't be used due to openbsd:stackbound
|
||||
* and those bytes are also poisoned under ASAN build modes.
|
||||
*
|
||||
* @return stack bottom address on success, or null w/ errrno
|
||||
* @return stack bottom address on success, or null w/ errno
|
||||
*/
|
||||
void *_mapstack(void) {
|
||||
char *p;
|
||||
|
|
|
@ -798,6 +798,24 @@ scall setrtable 0xfff136ffffffffff globl
|
|||
scall swapctl 0x10f0c1ffffffffff globl
|
||||
scall thrkill 0xfff077ffffffffff globl
|
||||
scall sys_unveil 0xfff072ffffffffff globl hidden
|
||||
#──────────────────────────NETBSD────────────────────────────
|
||||
#scall _lwp_create 0x135fffffffffffff globl # int _lwp_create(const struct ucontext_netbsd *ucp, uint64_t flags, int *new_lwp)
|
||||
#scall _lwp_exit 0x136fffffffffffff globl # int _lwp_exit(void)
|
||||
#scall _lwp_self 0x137fffffffffffff globl # int _lwp_self(void)
|
||||
#scall _lwp_wait 0x138fffffffffffff globl # int _lwp_wait(int wait_for, int *departed)
|
||||
#scall _lwp_suspend 0x139fffffffffffff globl # int _lwp_suspend(int target)
|
||||
#scall _lwp_continue 0x13afffffffffffff globl # int _lwp_continue(int target)
|
||||
#scall _lwp_wakeup 0x13bfffffffffffff globl # int _lwp_wakeup(int target)
|
||||
#scall _lwp_getprivate 0x13cfffffffffffff globl # void *_lwp_getprivate(void)
|
||||
#scall _lwp_setprivate 0x13dfffffffffffff globl # int _lwp_setprivate(void *ptr)
|
||||
#scall _lwp_kill 0x13efffffffffffff globl # int _lwp_kill(int target, int signo)
|
||||
#scall _lwp_detach 0x13ffffffffffffff globl # int _lwp_park(int clock_id, int flags, struct timespec *ts, int unpark, const void *hint, const void *unparkhint)
|
||||
#scall _lwp_park 0x1defffffffffffff globl # int _lwp_park(int clock_id, int flags, struct timespec *ts, int unpark, const void *hint, const void *unparkhint)
|
||||
#scall _lwp_unpark 0x141fffffffffffff globl # int _lwp_unpark_all(int target, const void *hint)
|
||||
#scall _lwp_unpark_all 0x142fffffffffffff globl # int _lwp_unpark_all(const int *targets, size_t ntargets, const void *hint)
|
||||
#scall _lwp_setname 0x143fffffffffffff globl # int _lwp_setname(int target, const char *name)
|
||||
#scall _lwp_getname 0x144fffffffffffff globl # int _lwp_getname(int target, char *name, size_t len)
|
||||
#scall _lwp_ctl 0x145fffffffffffff globl # int _lwp_ctl(int features, struct lwpctl **address)
|
||||
|
||||
# The Fifth Bell System Interface, Community Edition
|
||||
# » beyond the pale
|
||||
|
|
|
@ -35,12 +35,13 @@
|
|||
*
|
||||
* pthread_t id;
|
||||
* pthread_attr_t attr;
|
||||
* char *stk = _mapstack();
|
||||
* pthread_attr_init(&attr);
|
||||
* pthread_attr_setstack(&attr, gc(malloc(GetStackSize())),
|
||||
* GetStackSize());
|
||||
* pthread_attr_setstack(&attr, stk, GetStackSize());
|
||||
* pthread_create(&id, &attr, func, 0);
|
||||
* pthread_attr_destroy(&attr);
|
||||
* pthread_join(id, 0);
|
||||
* _freestack(stk);
|
||||
*
|
||||
* Your stack must have at least `PTHREAD_STACK_MIN` bytes, which
|
||||
* Cosmpolitan Libc defines as `GetStackSize()`. It's a link-time
|
||||
|
@ -52,11 +53,16 @@
|
|||
* (e.g. kprintf) assumes that stack sizes are two-powers and are
|
||||
* aligned to that two-power. Conformance isn't required since we
|
||||
* say caveat emptor to those who don't maintain these invariants
|
||||
* please consider using _mapstack() which always does it perfect
|
||||
* or use `mmap(0, GetStackSize() << 1, ...)` for a bigger stack.
|
||||
*
|
||||
* Unlike pthread_attr_setstacksize(), this function permits just
|
||||
* about any parameters and will change the values and allocation
|
||||
* as needed to conform to the mandatory requirements of the host
|
||||
* operating system.
|
||||
* operating system even if it doesn't meet the stricter needs of
|
||||
* Cosmopolitan Libc userspace libraries. For example with malloc
|
||||
* allocations, things like page size alignment, shall be handled
|
||||
* automatically for compatibility with existing codebases.
|
||||
*
|
||||
* @param stackaddr is address of stack allocated by caller, and
|
||||
* may be NULL in which case default behavior is restored
|
||||
|
|
118
libc/thread/pthread_getname_np.c
Normal file
118
libc/thread/pthread_getname_np.c
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/asmflag.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/pr.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
* Gets name of thread registered with system, e.g.
|
||||
*
|
||||
* char name[64];
|
||||
* pthread_getname_np(thread, name, sizeof(name));
|
||||
*
|
||||
* If the thread doesn't have a name, then empty string is returned.
|
||||
* This implementation guarantees `buf` is always modified, even on
|
||||
* error, and will always be nul-terminated. If `size` is 0 then this
|
||||
* function returns 0. Your `buf` is also chomped to remove newlines.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ERANGE if `size` wasn't large enough, in which case your
|
||||
* result will still be returned truncated if possible
|
||||
* @raise ENOSYS on MacOS, Windows, FreeBSD, and OpenBSD
|
||||
*/
|
||||
int pthread_getname_np(pthread_t thread, char *name, size_t size) {
|
||||
int e, fd, rc, tid, len;
|
||||
|
||||
if (!size) return 0;
|
||||
bzero(name, size);
|
||||
tid = ((struct PosixThread *)thread)->spawn.ptid;
|
||||
|
||||
if (IsLinux()) {
|
||||
// TASK_COMM_LEN is 16 on Linux so we're just being paranoid.
|
||||
char buf[256] = {0};
|
||||
if (tid == gettid()) {
|
||||
e = errno;
|
||||
if (prctl(PR_GET_NAME, buf) == -1) {
|
||||
rc = errno;
|
||||
errno = e;
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
char path[128], *p = path;
|
||||
p = stpcpy(p, "/proc/self/task/");
|
||||
p = FormatUint32(p, tid);
|
||||
p = stpcpy(p, "/comm");
|
||||
e = errno;
|
||||
if ((fd = sys_open(path, O_RDONLY | O_CLOEXEC, 0)) == -1) {
|
||||
rc = errno;
|
||||
errno = e;
|
||||
return rc;
|
||||
}
|
||||
rc = sys_read(fd, buf, sizeof(buf) - 1);
|
||||
rc |= sys_close(fd);
|
||||
if (rc == -1) {
|
||||
rc = errno;
|
||||
errno = e;
|
||||
return rc;
|
||||
}
|
||||
_chomp(buf);
|
||||
}
|
||||
if ((len = strlen(buf))) {
|
||||
memcpy(name, buf, MIN(len, size - 1));
|
||||
}
|
||||
if (len > size - 1) {
|
||||
return ERANGE;
|
||||
}
|
||||
return 0;
|
||||
|
||||
} else if (IsNetbsd()) {
|
||||
char cf;
|
||||
int ax, dx;
|
||||
// NetBSD doesn't document the subtleties of its nul-terminator
|
||||
// behavior, so like Linux we shall take the paranoid approach.
|
||||
asm volatile(CFLAG_ASM("syscall")
|
||||
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
|
||||
: "1"(324 /* _lwp_getname */), "D"(tid), "S"(name),
|
||||
"d"(size - 1)
|
||||
: "rcx", "r11", "memory");
|
||||
if (!cf) {
|
||||
// if size + our nul + kernel's nul is the buffer size, then we
|
||||
// can't say with absolute confidence truncation didn't happen.
|
||||
if (strlen(name) + 1 + 1 <= size) {
|
||||
return 0;
|
||||
} else {
|
||||
return ERANGE;
|
||||
}
|
||||
} else {
|
||||
return ax;
|
||||
}
|
||||
|
||||
} else {
|
||||
return ENOSYS;
|
||||
}
|
||||
}
|
118
libc/thread/pthread_setname_np.c
Normal file
118
libc/thread/pthread_setname_np.c
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/asmflag.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/pr.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
* Registers custom name of thread with system, e.g.
|
||||
*
|
||||
* void *worker(void *arg) {
|
||||
* pthread_setname_np(pthread_self(), "justine");
|
||||
* pause();
|
||||
* return 0;
|
||||
* }
|
||||
*
|
||||
* int main(int argc, char *argv[]) {
|
||||
* pthread_t id;
|
||||
* pthread_create(&id, 0, worker, 0);
|
||||
* pthread_join(id, 0);
|
||||
* }
|
||||
*
|
||||
* ProTip: The `htop` software is good at displaying thread names.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ERANGE if length of `name` exceeded system limit, in which
|
||||
* case the name may have still been set with os using truncation
|
||||
* @raise ENOSYS on MacOS, Windows, and OpenBSD
|
||||
* @see pthread_getname_np()
|
||||
*/
|
||||
int pthread_setname_np(pthread_t thread, const char *name) {
|
||||
char path[128], *p;
|
||||
int e, fd, rc, tid, len;
|
||||
|
||||
tid = ((struct PosixThread *)thread)->spawn.ptid;
|
||||
len = strlen(name);
|
||||
|
||||
if (IsLinux()) {
|
||||
if (tid == gettid()) {
|
||||
e = errno;
|
||||
if (prctl(PR_SET_NAME, name) == -1) {
|
||||
rc = errno;
|
||||
errno = e;
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
p = path;
|
||||
p = stpcpy(p, "/proc/self/task/");
|
||||
p = FormatUint32(p, tid);
|
||||
p = stpcpy(p, "/comm");
|
||||
e = errno;
|
||||
if ((fd = sys_open(path, O_WRONLY | O_CLOEXEC, 0)) == -1) {
|
||||
rc = errno;
|
||||
errno = e;
|
||||
return rc;
|
||||
}
|
||||
rc = sys_write(fd, name, len);
|
||||
rc |= sys_close(fd);
|
||||
if (rc == -1) {
|
||||
rc = errno;
|
||||
errno = e;
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if (len > 15) {
|
||||
// linux is documented as truncating here. we still set the name
|
||||
// since the limit might be raised in the future checking return
|
||||
// value of this function is a bummer. Grep it for TASK_COMM_LEN
|
||||
return ERANGE;
|
||||
}
|
||||
return 0;
|
||||
|
||||
} else if (IsFreebsd()) {
|
||||
char cf;
|
||||
int ax, dx;
|
||||
asm volatile(CFLAG_ASM("syscall")
|
||||
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
|
||||
: "1"(323 /* thr_set_name */), "D"(tid), "S"(name)
|
||||
: "rcx", "r11", "memory");
|
||||
return !cf ? 0 : ax;
|
||||
|
||||
} else if (IsNetbsd()) {
|
||||
char cf;
|
||||
int ax, dx;
|
||||
asm volatile(CFLAG_ASM("syscall")
|
||||
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
|
||||
: "1"(323 /* _lwp_setname */), "D"(tid), "S"(name)
|
||||
: "rcx", "r11", "memory");
|
||||
return !cf ? 0 : ax;
|
||||
|
||||
} else {
|
||||
return ENOSYS;
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ LIBC_THREAD_A_DIRECTDEPS = \
|
|||
LIBC_INTRIN \
|
||||
LIBC_MEM \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_NEXGEN32E
|
||||
|
|
88
test/libc/thread/pthread_setname_np_test.c
Normal file
88
test/libc/thread/pthread_setname_np_test.c
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/pthread.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
static void *SetName(void *arg) {
|
||||
ASSERT_EQ(0, pthread_setname_np(pthread_self(), "justine"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(pthread_setname_np, SetName_SystemCallSucceeds) {
|
||||
pthread_t id;
|
||||
if (!IsLinux() || !IsNetbsd() || !IsFreebsd()) return;
|
||||
ASSERT_EQ(0, pthread_create(&id, 0, SetName, 0));
|
||||
ASSERT_EQ(0, pthread_join(id, 0));
|
||||
}
|
||||
|
||||
static void *SetGetNameOfSelf(void *arg) {
|
||||
char me[16];
|
||||
ASSERT_EQ(0, pthread_setname_np(pthread_self(), "justine"));
|
||||
ASSERT_EQ(0, pthread_getname_np(pthread_self(), me, sizeof(me)));
|
||||
EXPECT_STREQ("justine", me);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(pthread_setname_np, SetGetNameOfSelf) {
|
||||
pthread_t id;
|
||||
if (!IsLinux() || !IsNetbsd()) return;
|
||||
ASSERT_EQ(0, pthread_create(&id, 0, SetGetNameOfSelf, 0));
|
||||
ASSERT_EQ(0, pthread_join(id, 0));
|
||||
}
|
||||
|
||||
static void *GetDefaultName(void *arg) {
|
||||
char me[16];
|
||||
ASSERT_EQ(0, pthread_getname_np(pthread_self(), me, sizeof(me)));
|
||||
EXPECT_STREQ("", me);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(pthread_setname_np, GetDefaultName_IsEmptyString) {
|
||||
pthread_t id;
|
||||
if (!IsLinux() || !IsNetbsd()) return;
|
||||
ASSERT_EQ(0, pthread_create(&id, 0, GetDefaultName, 0));
|
||||
ASSERT_EQ(0, pthread_join(id, 0));
|
||||
}
|
||||
|
||||
_Atomic(char) sync1, sync2;
|
||||
|
||||
static void *GetNameOfOtherThreadWorker(void *arg) {
|
||||
pthread_setname_np(pthread_self(), "justine");
|
||||
atomic_store(&sync1, 1);
|
||||
while (!atomic_load(&sync2)) pthread_yield();
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(pthread_setname_np, GetNameOfOtherThread) {
|
||||
char me[16];
|
||||
pthread_t id;
|
||||
if (!IsLinux() || !IsNetbsd()) return;
|
||||
ASSERT_EQ(0, pthread_create(&id, 0, GetNameOfOtherThreadWorker, 0));
|
||||
while (!atomic_load(&sync1)) pthread_yield();
|
||||
ASSERT_EQ(0, pthread_getname_np(id, me, sizeof(me)));
|
||||
EXPECT_STREQ("justine", me);
|
||||
ASSERT_EQ(0, pthread_setname_np(id, "tunney"));
|
||||
ASSERT_EQ(0, pthread_getname_np(id, me, sizeof(me)));
|
||||
EXPECT_STREQ("tunney", me);
|
||||
atomic_store(&sync2, 1);
|
||||
ASSERT_EQ(0, pthread_join(id, 0));
|
||||
}
|
Loading…
Reference in a new issue