Make _Thread_local more seamlessly working

This commit is contained in:
Justine Tunney 2022-07-10 08:27:50 -07:00
parent 5f4f6b0e69
commit 5fa77f1e8f
23 changed files with 217 additions and 283 deletions

View file

@ -1,58 +0,0 @@
/*-*- 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 2020 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/bits/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/strace.internal.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
/**
* Detaches thread.
*
* Calling this function will cause the thread to free its own memory
* once it exits. Using this function is mutually exclusive from the
* chtread_join() API.
*
* @return 0 on success or errno number on failure
* @raises EINVAL if thread isn't joinable
* @raises ESRCH if no such thread exists
* @threadsafe
*/
int cthread_detach(cthread_t td) {
int rc, tid;
if (!td || (IsAsan() && !__asan_is_valid(td, sizeof(*td)))) {
rc = ESRCH;
tid = -1;
} else if ((tid = td->tid) == gettid()) {
rc = EDEADLK;
} else if (atomic_load(&td->state) & (cthread_detached | cthread_joining)) {
rc = EINVAL;
} else if (!atomic_fetch_add(&td->state, cthread_detached) &
cthread_finished) {
rc = 0;
} else if (!munmap(td->alloc.bottom, td->alloc.top - td->alloc.bottom)) {
rc = 0;
} else {
rc = errno;
}
STRACE("cthread_detached(%d) → %s", tid, !rc ? "0" : strerrno(rc));
return rc;
}

View file

@ -1,33 +0,0 @@
/*-*- 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 2020 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/strace.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/thread.h"
/**
* Exits cosmopolitan thread.
*
* @param exitcode is passed along to cthread_join()
* @threadsafe
* @noreturn
*/
wontreturn void cthread_exit(void *exitcode) {
STRACE("cthread_exit(%p)", exitcode);
longerjmp(cthread_self()->exiter, (intptr_t)exitcode);
}

View file

@ -4,8 +4,9 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern uint64_t _pthread_key_usage[(PTHREAD_KEYS_MAX + 63) / 64];
extern pthread_key_dtor _pthread_key_dtor[PTHREAD_KEYS_MAX];
hidden extern uint64_t _pthread_key_usage[(PTHREAD_KEYS_MAX + 63) / 64];
hidden extern pthread_key_dtor _pthread_key_dtor[PTHREAD_KEYS_MAX];
hidden extern _Thread_local void *_pthread_keys[PTHREAD_KEYS_MAX];
void _pthread_key_destruct(void *[PTHREAD_KEYS_MAX]);

View file

@ -18,5 +18,11 @@
*/
#include "libc/thread/internal.h"
// tls value slots for pthread keys api
_Thread_local void *_pthread_keys[PTHREAD_KEYS_MAX];
// bitset of tls key registrations
uint64_t _pthread_key_usage[(PTHREAD_KEYS_MAX + 63) / 64];
// pthread tls key destructors
pthread_key_dtor _pthread_key_dtor[PTHREAD_KEYS_MAX];

View file

@ -18,16 +18,15 @@
*/
#include "libc/errno.h"
#include "libc/nexgen32e/gettls.h"
#include "libc/thread/internal.h"
#include "libc/thread/thread.h"
STATIC_YOINK("_main_thread_ctor");
/**
* Gets value of TLS slot for current thread.
*/
void *pthread_getspecific(pthread_key_t key) {
if (key < PTHREAD_KEYS_MAX) {
return ((cthread_t)__get_tls_inline())->key[key];
if (0 <= key && key < PTHREAD_KEYS_MAX) {
return _pthread_keys[key];
} else {
return 0;
}

View file

@ -23,8 +23,6 @@
#include "libc/thread/internal.h"
#include "libc/thread/thread.h"
STATIC_YOINK("_main_thread_ctor");
/**
* Allocates TLS slot.
*/
@ -43,7 +41,7 @@ int pthread_key_create(pthread_key_t *key, pthread_key_dtor dtor) {
}
static textexit void _pthread_key_atexit(void) {
_pthread_key_destruct(((cthread_t)__get_tls())->key);
_pthread_key_destruct(_pthread_keys);
}
__attribute__((__constructor__)) static textstartup void _pthread_key_init() {

View file

@ -18,16 +18,15 @@
*/
#include "libc/errno.h"
#include "libc/nexgen32e/gettls.h"
#include "libc/thread/internal.h"
#include "libc/thread/thread.h"
STATIC_YOINK("_main_thread_ctor");
/**
* Sets value of TLS slot for current thread.
*/
int pthread_setspecific(pthread_key_t key, void *val) {
if (key < PTHREAD_KEYS_MAX) {
((cthread_t)__get_tls_inline())->key[key] = val;
if (0 <= key && key < PTHREAD_KEYS_MAX) {
_pthread_keys[key] = val;
return 0;
} else {
return EINVAL;

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/atomic.h"
#include "libc/calls/calls.h"
#include "libc/thread/thread.h"
STATIC_YOINK("_main_thread_ctor");
@ -29,7 +30,7 @@ static void Pause(int attempt) {
__builtin_ia32_pause();
}
} else {
cthread_yield();
sched_yield();
}
}

View file

@ -35,7 +35,19 @@
STATIC_YOINK("_main_thread_ctor");
/**
* @fileoverview Simple System Threads API
* @fileoverview Simple threading API
*
* This API is supported on all six operating systems. We have this
* because the POSIX threads API is positively enormous. We currently
* only implement a small subset of POSIX threads, e.g. mutexes. So
* until we can implement all of POSIX threads, this API is great. If we
* consider that the classic forking concurrency library consists of a
* single function, it's a shame POSIX didn't define threads in the past
* to just be this. Since create/join/atomics is really all we need.
*
* Your spawn library abstracts clone() which also works on all
* platforms; however our implementation of clone() is significantly
* complicated so we strongly recommend always favoring this API.
*/
#define _TLSZ ((intptr_t)_tls_size)
@ -50,7 +62,7 @@ STATIC_YOINK("_main_thread_ctor");
* @param arg shall be passed to `fun`
* @param opt_out_thread needn't be initialiized and is always clobbered
* except when it isn't specified, in which case, the thread is kind
* of detached and will leak in stack / tls memory
* of detached and will (currently) just leak the stack / tls memory
* @return 0 on success, or -1 w/ errno
*/
int _spawn(int fun(void *, int), void *arg, struct spawn *opt_out_thread) {

View file

@ -23,17 +23,11 @@ struct cthread_descriptor_t {
int32_t __pad0; /* 0x10 */
int32_t state; /* 0x14 */
void *arg; /* 0x18 */
void *pthread_ret_ptr; /* 0x20 */
int64_t __pad1; /* 0x28 */
int64_t __pad1; /* 0x20 */
int64_t __pad2; /* 0x28 */
struct cthread_descriptor_t *self2; /* 0x30 */
int32_t tid; /* 0x38 */
int32_t err; /* 0x3c */
void *exitcode;
struct {
char *top, *bottom;
} stack, alloc;
jmp_buf exiter;
void *key[PTHREAD_KEYS_MAX];
};
typedef struct cthread_descriptor_t *cthread_t;
@ -49,16 +43,7 @@ typedef struct cthread_attr_t {
int mode;
} cthread_attr_t;
extern const void *const _main_thread_ctor[];
int cthread_create(cthread_t *, const cthread_attr_t *, void *(*)(void *),
void *);
int cthread_yield(void);
cthread_t cthread_self(void);
int cthread_join(cthread_t, void **);
void cthread_exit(void *) wontreturn;
int cthread_detach(cthread_t);
int cthread_attr_init(cthread_attr_t *);
int cthread_attr_destroy(cthread_attr_t *);
int cthread_attr_setstacksize(cthread_attr_t *, size_t);

View file

@ -1,27 +0,0 @@
/*-*- 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 2020 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/thread/thread.h"
/**
* Asks operating system to handoff remaining time slice.
*/
int cthread_yield(void) {
return sched_yield();
}

View file

@ -1,51 +0,0 @@
/*-*- 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/bits/atomic.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/thread.h"
static struct Zombie {
struct Zombie *next;
cthread_t td;
} * cthread_zombies;
void cthread_zombies_add(cthread_t td) {
struct Zombie *z;
if ((z = malloc(sizeof(struct Zombie)))) {
z->td = td;
z->next = atomic_load(&cthread_zombies);
for (;;) {
if (atomic_compare_exchange_weak(&cthread_zombies, &z->next, z)) {
break;
}
}
}
}
void cthread_zombies_reap(void) {
struct Zombie *z;
// TODO(jart): Is this right? Update to not use malloc/free?
while ((z = atomic_load(&cthread_zombies)) && !atomic_load(&z->td->tid)) {
if (atomic_compare_exchange_weak(&cthread_zombies, &z, z->next)) {
munmap(z->td->alloc.bottom, z->td->alloc.top - z->td->alloc.bottom);
free(z);
}
}
}