Make POSIX threads improvements

- Ensure SIGTHR isn't blocked in newly created threads
- Use TIB rather than thread_local for thread atexits
- Make POSIX thread keys atomic within thread
- Don't bother logging prctl() to --strace
- Log thread destructor names to --strace
This commit is contained in:
Justine Tunney 2024-06-30 15:38:59 -07:00
parent 387310c659
commit 76957983cf
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
11 changed files with 57 additions and 71 deletions

View file

@ -53,15 +53,5 @@ int prctl(int operation, ...) {
rc = enosys();
}
#if SYSDEBUG
if (operation == PR_CAPBSET_READ || operation == PR_CAPBSET_DROP) {
STRACE("prctl(%s, %s) → %d% m", DescribePrctlOperation(operation),
DescribeCapability(a), rc);
} else {
STRACE("prctl(%s, %p, %p, %p, %p) → %d% m",
DescribePrctlOperation(operation), a, b, c, d, rc);
}
#endif
return rc;
}

View file

@ -13,7 +13,6 @@ const char *DescribeFlags(char *, size_t, const struct DescribeFlags *, size_t,
const char *DescribeArchPrctlCode(char[12], int) libcesque;
const char *DescribeCancelState(char[12], int, int *) libcesque;
const char *DescribeCapability(char[32], int) libcesque;
const char *DescribeClockName(char[32], int) libcesque;
const char *DescribeControlKeyState(char[64], uint32_t) libcesque;
const char *DescribeDirfd(char[12], int) libcesque;
@ -47,7 +46,6 @@ const char *DescribeOpenFlags(char[128], int) libcesque;
const char *DescribeOpenMode(char[15], int, int) libcesque;
const char *DescribePersonalityFlags(char[128], int) libcesque;
const char *DescribePollFlags(char[64], int) libcesque;
const char *DescribePrctlOperation(int) libcesque;
const char *DescribeProtFlags(char[48], int) libcesque;
const char *DescribePtrace(char[12], int) libcesque;
const char *DescribePtraceEvent(char[32], int) libcesque;
@ -69,9 +67,7 @@ const char *DescribeVirtualKeyCode(char[32], uint32_t) libcesque;
const char *DescribeWhence(char[12], int) libcesque;
const char *DescribeWhichPrio(char[12], int) libcesque;
#define DescribeArchPrctlCode(x) DescribeArchPrctlCode(alloca(12), x)
#define DescribeCancelState(x, y) DescribeCancelState(alloca(12), x, y)
#define DescribeCapability(x) DescribeCapability(alloca(32), x)
#define DescribeClockName(x) DescribeClockName(alloca(32), x)
#define DescribeControlKeyState(x) DescribeControlKeyState(alloca(64), x)
#define DescribeDirfd(x) DescribeDirfd(alloca(12), x)

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/cxaatexit.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/gc.internal.h"
@ -30,8 +31,6 @@ struct Dtor {
struct Dtor *next;
};
static _Thread_local struct Dtor *__cxa_thread_atexit_list;
static void _pthread_unwind(struct CosmoTib *tib) {
struct PosixThread *pt;
struct _pthread_cleanup_buffer *cb;
@ -42,34 +41,33 @@ static void _pthread_unwind(struct CosmoTib *tib) {
}
}
static void __cxa_thread_unkey(struct CosmoTib *tib) {
static void _pthread_unkey(struct CosmoTib *tib) {
for (int j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; ++j) {
bool gotsome = false;
for (int i = 0; i < PTHREAD_KEYS_MAX; ++i) {
void *val;
int i, j, gotsome;
pthread_key_dtor dtor;
for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; ++j) {
for (gotsome = i = 0; i < PTHREAD_KEYS_MAX; ++i) {
if ((val = tib->tib_keys[i]) &&
(dtor = atomic_load_explicit(_pthread_key_dtor + i,
memory_order_relaxed)) &&
dtor != (pthread_key_dtor)-1) {
gotsome = 1;
tib->tib_keys[i] = 0;
if ((dtor = atomic_load_explicit(&_pthread_key_dtor[i],
memory_order_acquire)) &&
dtor != (pthread_key_dtor)-1 &&
(val = atomic_exchange_explicit(&tib->tib_keys[i], 0,
memory_order_relaxed))) {
STRACE("_pthread_unkey(%t, %p)", dtor, val);
gotsome = true;
dtor(val);
}
}
if (!gotsome) {
if (!gotsome)
break;
}
}
}
static void _pthread_ungarbage(struct CosmoTib *tib) {
struct Garbages *g;
while ((g = tib->tib_garbages)) {
tib->tib_garbages = 0;
while (g->i--) {
while (g->i--)
((void (*)(intptr_t))g->p[g->i].fn)(g->p[g->i].arg);
}
_weaken(free)(g->p);
_weaken(free)(g);
}
@ -82,10 +80,11 @@ void __cxa_thread_finalize(void) {
_pthread_unwind(tib);
if (tib->tib_nsync)
_weaken(nsync_waiter_destroy)(tib->tib_nsync);
__cxa_thread_unkey(tib);
_pthread_unkey(tib);
_pthread_ungarbage(tib);
while ((dtor = __cxa_thread_atexit_list)) {
__cxa_thread_atexit_list = dtor->next;
while ((dtor = tib->tib_atexit)) {
STRACE("__cxa_finalize(%t, %p)", dtor->fun, dtor->arg);
tib->tib_atexit = dtor->next;
((void (*)(void *))dtor->fun)(dtor->arg);
_weaken(free)(dtor);
}
@ -97,9 +96,11 @@ int __cxa_thread_atexit_impl(void *fun, void *arg, void *dso_symbol) {
return -1;
if (!(dtor = _weaken(malloc)(sizeof(struct Dtor))))
return -1;
struct CosmoTib *tib;
tib = __get_tls();
dtor->fun = fun;
dtor->arg = arg;
dtor->next = __cxa_thread_atexit_list;
__cxa_thread_atexit_list = dtor;
dtor->next = tib->tib_atexit;
tib->tib_atexit = dtor;
return 0;
}

View file

@ -54,9 +54,8 @@ long _pthread_cancel_ack(void) {
pthread_exit(PTHREAD_CANCELED);
}
pt->pt_flags |= PT_NOCANCEL;
if (IsOpenbsd()) {
if (IsOpenbsd())
pt->pt_flags |= PT_OPENBSD_KLUDGE;
}
return ecanceled();
}
@ -351,6 +350,7 @@ static errno_t _pthread_cancel_everyone(void) {
* @param thread may be 0 to cancel all threads except self
* @return 0 on success, or errno on error
* @raise ESRCH if system thread wasn't alive or we lost a race
* @cancelationpoint
*/
errno_t pthread_cancel(pthread_t thread) {
struct PosixThread *arg;
@ -401,6 +401,7 @@ void pthread_testcancel(void) {
*
* @return 0 if not cancelled or cancelation is blocked or `ECANCELED`
* in masked mode when the calling thread has been cancelled
* @cancelationpoint
*/
errno_t pthread_testcancel_np(void) {
struct PosixThread *pt;

View file

@ -117,6 +117,7 @@ static int PosixThread(void *arg, int tid) {
}
// set long jump handler so pthread_exit can bring control back here
if (!setjmp(pt->pt_exiter)) {
sigdelset(&pt->pt_attr.__sigmask, SIGTHR);
if (IsWindows()) {
atomic_store_explicit(&__get_tls()->tib_sigmask, pt->pt_attr.__sigmask,
memory_order_release);

View file

@ -48,9 +48,9 @@ int pthread_key_create(pthread_key_t *key, pthread_key_dtor dtor) {
if (!dtor)
dtor = (pthread_key_dtor)-1;
for (i = 0; i < PTHREAD_KEYS_MAX; ++i) {
if (!(expect = atomic_load_explicit(_pthread_key_dtor + i,
memory_order_acquire)) &&
atomic_compare_exchange_strong_explicit(_pthread_key_dtor + i, &expect,
if (!(expect = atomic_load_explicit(&_pthread_key_dtor[i],
memory_order_relaxed)) &&
atomic_compare_exchange_strong_explicit(&_pthread_key_dtor[i], &expect,
dtor, memory_order_release,
memory_order_relaxed)) {
*key = i;

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/thread/posixthread.internal.h"
@ -32,10 +31,12 @@
*
* @param key was created by pthread_key_create()
* @return 0 on success, or errno on error
* @raise EINVAL if `key` is invalid
*/
int pthread_key_delete(pthread_key_t k) {
unassert(0 <= k && k < PTHREAD_KEYS_MAX);
unassert(atomic_load_explicit(_pthread_key_dtor + k, memory_order_acquire));
atomic_store_explicit(_pthread_key_dtor + k, 0, memory_order_release);
if (!(0 <= k && k < PTHREAD_KEYS_MAX))
return EINVAL; // corrupt key identifier
if (!atomic_exchange_explicit(&_pthread_key_dtor[k], 0, memory_order_acq_rel))
return EINVAL; // delete called twice
return 0;
}

View file

@ -1,7 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_THREAD_THREAD_H_
#define COSMOPOLITAN_LIBC_THREAD_THREAD_H_
#define PTHREAD_KEYS_MAX 48
#define PTHREAD_KEYS_MAX 46
#define PTHREAD_STACK_MIN 65536
#define PTHREAD_DESTRUCTOR_ITERATIONS 4

View file

@ -39,7 +39,8 @@ struct CosmoTib {
uint32_t tib_sigstack_flags;
_Atomic(int) tib_relock_maps;
void *tib_nsync;
void *tib_keys[47];
void *tib_atexit;
_Atomic(void *) tib_keys[46];
} __attribute__((__aligned__(64)));
extern int __threaded;

View file

@ -20,9 +20,9 @@
#include "ctl/array.h"
#include "libc/mem/leaks.h"
#include <algorithm>
#include <array>
#define ctl std
// #include <algorithm>
// #include <array>
// #define ctl std
int
main()

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Copyright 2024 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
@ -16,24 +16,19 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/describeflags.internal.h"
#include "libc/sysv/consts/pr.h"
#include "libc/calls/calls.h"
#include "libc/testlib/testlib.h"
#include "libc/thread/thread.h"
const char *DescribePrctlOperation(int x) {
switch (x) {
case PR_GET_NAME:
return "PR_GET_NAME";
case PR_SET_NO_NEW_PRIVS:
return "PR_SET_NO_NEW_PRIVS";
case PR_SET_SECCOMP:
return "PR_SET_SECCOMP";
case PR_GET_SECCOMP:
return "PR_GET_SECCOMP";
case PR_CAPBSET_READ:
return "PR_CAPBSET_READ";
case PR_CAPBSET_DROP:
return "PR_CAPBSET_DROP";
default:
return "PRCTL_???";
}
void *wut(void *arg) {
pthread_id_np_t tid;
pthread_getunique_np(pthread_self(), &tid);
ASSERT_EQ(gettid(), tid);
return 0;
}
TEST(pthread_getunique_np, test) {
pthread_t id;
ASSERT_EQ(0, pthread_create(&id, 0, wut, (void *)1));
ASSERT_EQ(0, pthread_join(id, 0));
}