mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
Make some fixups to POSIX threads
This commit is contained in:
parent
de511bc71a
commit
6c323383e5
12 changed files with 168 additions and 69 deletions
|
@ -593,7 +593,7 @@ typedef struct {
|
||||||
|
|
||||||
#define notpossible \
|
#define notpossible \
|
||||||
do { \
|
do { \
|
||||||
asm("hlt"); \
|
asm("ud2"); \
|
||||||
unreachable; \
|
unreachable; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|
|
@ -18,38 +18,56 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/strace.internal.h"
|
#include "libc/calls/strace.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
#include "libc/intrin/asmflag.h"
|
||||||
#include "libc/intrin/promises.internal.h"
|
#include "libc/intrin/promises.internal.h"
|
||||||
#include "libc/nt/thread.h"
|
#include "libc/nt/thread.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/sysv/consts/nr.h"
|
#include "libc/sysv/consts/nr.h"
|
||||||
|
|
||||||
|
__msabi extern typeof(ExitThread) *const __imp_ExitThread;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Terminates thread with raw system call.
|
* Terminates thread with raw system call.
|
||||||
*
|
*
|
||||||
|
* If this is the main thread, or an orphaned child thread, then this
|
||||||
|
* function is equivalent to exiting the process; however, `rc` shall
|
||||||
|
* only be reported to the parent process on Linux, FreeBSD & Windows
|
||||||
|
* whereas on other platforms, it'll be silently coerced to zero.
|
||||||
|
*
|
||||||
* @param rc only works on Linux and Windows
|
* @param rc only works on Linux and Windows
|
||||||
* @see cthread_exit()
|
* @see cthread_exit()
|
||||||
* @threadsafe
|
* @threadsafe
|
||||||
* @noreturn
|
* @noreturn
|
||||||
*/
|
*/
|
||||||
privileged wontreturn void _Exit1(int rc) {
|
privileged wontreturn void _Exit1(int rc) {
|
||||||
struct WinThread *wt;
|
char cf;
|
||||||
|
int ax, dx, di, si;
|
||||||
STRACE("_Exit1(%d)", rc);
|
STRACE("_Exit1(%d)", rc);
|
||||||
if (!IsWindows() && !IsMetal()) {
|
if (!IsWindows() && !IsMetal()) {
|
||||||
if (IsOpenbsd() && !PLEDGED(STDIO)) {
|
// exit() on Linux
|
||||||
|
// thr_exit() on FreeBSD
|
||||||
|
// __threxit() on OpenBSD
|
||||||
|
// __lwp_exit() on NetBSD
|
||||||
|
// __bsdthread_terminate() on XNU
|
||||||
|
asm volatile(CFLAG_ASM("xor\t%%r10d,%%r10d\n\t"
|
||||||
|
"syscall")
|
||||||
|
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx), "=D"(di), "=S"(si)
|
||||||
|
: "1"(__NR_exit), "3"(IsLinux() ? rc : 0), "4"(0), "2"(0)
|
||||||
|
: "rcx", "r8", "r9", "r10", "r11", "memory");
|
||||||
|
if ((IsFreebsd() && !cf && !ax) || (SupportsFreebsd() && IsTiny())) {
|
||||||
|
// FreeBSD checks if this is either the main thread by itself, or
|
||||||
|
// the last running child thread in which case thr_exit() returns
|
||||||
|
// zero with an error. In that case we'll exit the whole process.
|
||||||
|
// FreeBSD thr_exit() can even clobber registers, like r8 and r9!
|
||||||
asm volatile("syscall"
|
asm volatile("syscall"
|
||||||
: /* no outputs */
|
: /* no outputs */
|
||||||
: "a"(__NR_exit), "D"(rc)
|
: "a"(__NR_exit_group), "D"(rc)
|
||||||
: "rcx", "r11", "memory");
|
: "rcx", "r11", "memory");
|
||||||
|
unreachable;
|
||||||
}
|
}
|
||||||
asm volatile("xor\t%%r10d,%%r10d\n\t"
|
|
||||||
"syscall"
|
|
||||||
: /* no outputs */
|
|
||||||
: "a"(__NR_exit), "D"(IsLinux() ? rc : 0), "S"(0), "d"(0)
|
|
||||||
: "rcx", "r10", "r11", "memory");
|
|
||||||
} else if (IsWindows()) {
|
} else if (IsWindows()) {
|
||||||
ExitThread(rc);
|
__imp_ExitThread(rc);
|
||||||
}
|
unreachable;
|
||||||
for (;;) {
|
|
||||||
asm("ud2");
|
|
||||||
}
|
}
|
||||||
|
notpossible;
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ static int pthread_mutex_lock_spin(pthread_mutex_t *mutex, int expect,
|
||||||
* PTHREAD_MUTEX_ERRORCHECK : 917c (296ns)
|
* PTHREAD_MUTEX_ERRORCHECK : 917c (296ns)
|
||||||
*
|
*
|
||||||
* @return 0 on success, or error number on failure
|
* @return 0 on success, or error number on failure
|
||||||
* @see pthread_spin_lock
|
* @see pthread_spin_lock()
|
||||||
*/
|
*/
|
||||||
int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||||
int c, me, owner, tries;
|
int c, me, owner, tries;
|
||||||
|
|
|
@ -10,10 +10,46 @@ COSMOPOLITAN_C_START_
|
||||||
* @fileoverview Cosmopolitan POSIX Thread Internals
|
* @fileoverview Cosmopolitan POSIX Thread Internals
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// LEGAL TRANSITIONS ┌──> TERMINATED
|
||||||
|
// pthread_create ─┬─> JOINABLE ─┴┬─> DETACHED ──> ZOMBIE
|
||||||
|
// └──────────────┘
|
||||||
enum PosixThreadStatus {
|
enum PosixThreadStatus {
|
||||||
|
|
||||||
|
// this is a running thread that needs pthread_join()
|
||||||
|
//
|
||||||
|
// the following transitions are possible:
|
||||||
|
//
|
||||||
|
// - kPosixThreadJoinable -> kPosixThreadTerminated if start_routine()
|
||||||
|
// returns, or is longjmp'd out of by pthread_exit(), and the thread
|
||||||
|
// is waiting to be joined.
|
||||||
|
//
|
||||||
|
// - kPosixThreadJoinable -> kPosixThreadDetached if pthread_detach()
|
||||||
|
// is called on this thread.
|
||||||
kPosixThreadJoinable,
|
kPosixThreadJoinable,
|
||||||
|
|
||||||
|
// this is a managed thread that'll be cleaned up by the library.
|
||||||
|
//
|
||||||
|
// the following transitions are possible:
|
||||||
|
//
|
||||||
|
// - kPosixThreadDetached -> kPosixThreadZombie if start_routine()
|
||||||
|
// returns, or is longjmp'd out of by pthread_exit(), and the thread
|
||||||
|
// is waiting to be joined.
|
||||||
kPosixThreadDetached,
|
kPosixThreadDetached,
|
||||||
|
|
||||||
|
// this is a joinable thread that terminated.
|
||||||
|
//
|
||||||
|
// the following transitions are possible:
|
||||||
|
//
|
||||||
|
// - kPosixThreadTerminated -> _pthread_free() will happen when
|
||||||
|
// pthread_join() is called by the user.
|
||||||
kPosixThreadTerminated,
|
kPosixThreadTerminated,
|
||||||
|
|
||||||
|
// this is a detached thread that terminated.
|
||||||
|
//
|
||||||
|
// the following transitions are possible:
|
||||||
|
//
|
||||||
|
// - kPosixThreadZombie -> _pthread_free() will happen whenever
|
||||||
|
// convenient, e.g. pthread_create() entry or atexit handler.
|
||||||
kPosixThreadZombie,
|
kPosixThreadZombie,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,11 +64,11 @@ struct PosixThread {
|
||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
};
|
};
|
||||||
|
|
||||||
void pthread_free(struct PosixThread *) hidden;
|
void _pthread_free(struct PosixThread *) hidden;
|
||||||
void pthread_wait(struct PosixThread *) hidden;
|
void _pthread_wait(struct PosixThread *) hidden;
|
||||||
void pthread_zombies_add(struct PosixThread *) hidden;
|
void _pthread_zombies_add(struct PosixThread *) hidden;
|
||||||
void pthread_zombies_decimate(void) hidden;
|
void _pthread_zombies_decimate(void) hidden;
|
||||||
void pthread_zombies_harvest(void) hidden;
|
void _pthread_zombies_harvest(void) hidden;
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
|
@ -22,21 +22,23 @@
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
#include "libc/intrin/wait0.internal.h"
|
#include "libc/intrin/wait0.internal.h"
|
||||||
|
#include "libc/intrin/weaken.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/nexgen32e/gettls.h"
|
#include "libc/nexgen32e/gettls.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/sysv/consts/clone.h"
|
#include "libc/sysv/consts/clone.h"
|
||||||
#include "libc/sysv/consts/map.h"
|
#include "libc/sysv/consts/map.h"
|
||||||
#include "libc/sysv/consts/prot.h"
|
#include "libc/sysv/consts/prot.h"
|
||||||
|
#include "libc/thread/internal.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
#include "libc/thread/spawn.h"
|
#include "libc/thread/spawn.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
void pthread_wait(struct PosixThread *pt) {
|
void _pthread_wait(struct PosixThread *pt) {
|
||||||
_wait0(pt->spawn.ctid);
|
_wait0(pt->spawn.ctid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pthread_free(struct PosixThread *pt) {
|
void _pthread_free(struct PosixThread *pt) {
|
||||||
free(pt->spawn.tls);
|
free(pt->spawn.tls);
|
||||||
if (pt->stacksize) {
|
if (pt->stacksize) {
|
||||||
munmap(&pt->spawn.stk, pt->stacksize);
|
munmap(&pt->spawn.stk, pt->stacksize);
|
||||||
|
@ -51,6 +53,9 @@ static int PosixThread(void *arg, int tid) {
|
||||||
((cthread_t)__get_tls())->pthread = pt;
|
((cthread_t)__get_tls())->pthread = pt;
|
||||||
pt->rc = pt->start_routine(pt->arg);
|
pt->rc = pt->start_routine(pt->arg);
|
||||||
}
|
}
|
||||||
|
if (weaken(_pthread_key_destruct)) {
|
||||||
|
weaken(_pthread_key_destruct)(0);
|
||||||
|
}
|
||||||
if (atomic_load_explicit(&pt->status, memory_order_acquire) ==
|
if (atomic_load_explicit(&pt->status, memory_order_acquire) ==
|
||||||
kPosixThreadDetached) {
|
kPosixThreadDetached) {
|
||||||
atomic_store_explicit(&pt->status, kPosixThreadZombie,
|
atomic_store_explicit(&pt->status, kPosixThreadZombie,
|
||||||
|
@ -71,18 +76,18 @@ static int PosixThread(void *arg, int tid) {
|
||||||
* │ pthread_create() │ - Standard
|
* │ pthread_create() │ - Standard
|
||||||
* └─────────┬────────┘ Abstraction
|
* └─────────┬────────┘ Abstraction
|
||||||
* ┌─────────┴────────┐
|
* ┌─────────┴────────┐
|
||||||
* │ _spawn() │ - Cosmopolitan
|
|
||||||
* └─────────┬────────┘ Abstraction
|
|
||||||
* ┌─────────┴────────┐
|
|
||||||
* │ clone() │ - Polyfill
|
* │ clone() │ - Polyfill
|
||||||
* └─────────┬────────┘
|
* └─────────┬────────┘
|
||||||
* ┌────────┬──┴──┬─┬─────────┐ - Kernel
|
* ┌────────┬──┴┬─┬─┬─────────┐ - Kernel
|
||||||
* ┌─────┴─────┐ │ │┌┴──────┐ │ Interfaces
|
* ┌─────┴─────┐ │ │ │┌┴──────┐ │ Interfaces
|
||||||
* │ sys_clone │ │ ││ tfork │ │
|
* │ sys_clone │ │ │ ││ tfork │ ┌┴─────────────┐
|
||||||
* └───────────┘ │ │└───────┘ ┌┴─────────────┐
|
* └───────────┘ │ │ │└───────┘ │ CreateThread │
|
||||||
* ┌───────────────┴──┐ ┌┴────────┐ │ CreateThread │
|
* ┌───────────────┴──┐│┌┴────────┐ └──────────────┘
|
||||||
* │ bsdthread_create │ │ thr_new │ └──────────────┘
|
* │ bsdthread_create │││ thr_new │
|
||||||
* └──────────────────┘ └─────────┘
|
* └──────────────────┘│└─────────┘
|
||||||
|
* ┌───────┴──────┐
|
||||||
|
* │ _lwp_create │
|
||||||
|
* └──────────────┘
|
||||||
*
|
*
|
||||||
* @param thread if non-null is used to output the thread id
|
* @param thread if non-null is used to output the thread id
|
||||||
* upon successful completion
|
* upon successful completion
|
||||||
|
@ -96,13 +101,14 @@ static int PosixThread(void *arg, int tid) {
|
||||||
* @raise EINVAL if `attr` was supplied and had unnaceptable data
|
* @raise EINVAL if `attr` was supplied and had unnaceptable data
|
||||||
* @raise EPERM if scheduling policy was requested and user account
|
* @raise EPERM if scheduling policy was requested and user account
|
||||||
* isn't authorized to use it
|
* isn't authorized to use it
|
||||||
|
* @threadsafe
|
||||||
*/
|
*/
|
||||||
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||||
void *(*start_routine)(void *), void *arg) {
|
void *(*start_routine)(void *), void *arg) {
|
||||||
int rc, e = errno;
|
int rc, e = errno;
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
pthread_attr_t default_attr;
|
pthread_attr_t default_attr;
|
||||||
pthread_zombies_decimate();
|
_pthread_zombies_decimate();
|
||||||
|
|
||||||
// default attributes
|
// default attributes
|
||||||
if (!attr) {
|
if (!attr) {
|
||||||
|
@ -140,7 +146,7 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||||
pt->stacksize = attr->stacksize;
|
pt->stacksize = attr->stacksize;
|
||||||
} else {
|
} else {
|
||||||
rc = errno;
|
rc = errno;
|
||||||
pthread_free(pt);
|
_pthread_free(pt);
|
||||||
errno = e;
|
errno = e;
|
||||||
if (rc == EINVAL || rc == EOVERFLOW) {
|
if (rc == EINVAL || rc == EOVERFLOW) {
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
|
@ -170,7 +176,7 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// save the attributes for descriptive purposes
|
// we only need to save this to support pthread_getattr_np()
|
||||||
pt->attr = *attr;
|
pt->attr = *attr;
|
||||||
|
|
||||||
// set initial status
|
// set initial status
|
||||||
|
@ -180,10 +186,10 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||||
break;
|
break;
|
||||||
case PTHREAD_CREATE_DETACHED:
|
case PTHREAD_CREATE_DETACHED:
|
||||||
pt->status = kPosixThreadDetached;
|
pt->status = kPosixThreadDetached;
|
||||||
pthread_zombies_add(pt);
|
_pthread_zombies_add(pt);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pthread_free(pt);
|
_pthread_free(pt);
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +201,7 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||||
CLONE_CHILD_CLEARTID,
|
CLONE_CHILD_CLEARTID,
|
||||||
pt, &pt->spawn.ptid, pt->spawn.tib, pt->spawn.ctid) == -1) {
|
pt, &pt->spawn.ptid, pt->spawn.tib, pt->spawn.ctid) == -1) {
|
||||||
rc = errno;
|
rc = errno;
|
||||||
pthread_free(pt);
|
_pthread_free(pt);
|
||||||
errno = e;
|
errno = e;
|
||||||
if (rc == EINVAL) {
|
if (rc == EINVAL) {
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
|
|
|
@ -34,17 +34,24 @@ int pthread_detach(pthread_t thread) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
status = atomic_load_explicit(&pt->status, memory_order_relaxed);
|
status = atomic_load_explicit(&pt->status, memory_order_relaxed);
|
||||||
if (status == kPosixThreadDetached || status == kPosixThreadZombie) {
|
if (status == kPosixThreadDetached || status == kPosixThreadZombie) {
|
||||||
|
// these two states indicate the thread was already detached, in
|
||||||
|
// which case it's already listed under _pthread_zombies.
|
||||||
break;
|
break;
|
||||||
} else if (status == kPosixThreadTerminated) {
|
} else if (status == kPosixThreadTerminated) {
|
||||||
pthread_wait(pt);
|
// thread was joinable and finished running. since pthread_join
|
||||||
pthread_free(pt);
|
// won't be called, it's safe to free the thread resources now.
|
||||||
break;
|
_pthread_wait(pt);
|
||||||
} else if (status == kPosixThreadJoinable &&
|
_pthread_free(pt);
|
||||||
atomic_compare_exchange_weak_explicit(
|
|
||||||
&pt->status, &status, kPosixThreadDetached,
|
|
||||||
memory_order_acquire, memory_order_relaxed)) {
|
|
||||||
pthread_zombies_add(pt);
|
|
||||||
break;
|
break;
|
||||||
|
} else if (status == kPosixThreadJoinable) {
|
||||||
|
if (atomic_compare_exchange_weak_explicit(
|
||||||
|
&pt->status, &status, kPosixThreadDetached, memory_order_acquire,
|
||||||
|
memory_order_relaxed)) {
|
||||||
|
_pthread_zombies_add(pt);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
notpossible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -24,10 +24,22 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Terminates current POSIX thread.
|
* Terminates current POSIX thread.
|
||||||
|
*
|
||||||
|
* If this function is called from the main thread, or a thread created
|
||||||
|
* with clone() or _spawn(), then this function is the same as _Exit1()
|
||||||
|
* in which case `rc` is coerced to a `uint8_t` exit status, which will
|
||||||
|
* only be reported to the parent process on Linux, FreeBSD and Windows
|
||||||
|
*
|
||||||
|
* @param rc is reported later to pthread_join()
|
||||||
|
* @threadsafe
|
||||||
|
* @noreturn
|
||||||
*/
|
*/
|
||||||
void pthread_exit(void *rc) {
|
wontreturn void pthread_exit(void *rc) {
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
pt = ((cthread_t)__get_tls())->pthread;
|
if ((pt = ((cthread_t)__get_tls())->pthread)) {
|
||||||
pt->rc = rc;
|
pt->rc = rc;
|
||||||
longjmp(pt->exiter, 1);
|
longjmp(pt->exiter, 1);
|
||||||
|
} else {
|
||||||
|
_Exit1((int)(intptr_t)rc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,14 +32,15 @@
|
||||||
*/
|
*/
|
||||||
int pthread_join(pthread_t thread, void **value_ptr) {
|
int pthread_join(pthread_t thread, void **value_ptr) {
|
||||||
struct PosixThread *pt = thread;
|
struct PosixThread *pt = thread;
|
||||||
if (pt->status == kPosixThreadDetached) {
|
if (pt->status == kPosixThreadDetached || //
|
||||||
|
pt->status == kPosixThreadZombie) {
|
||||||
assert(!"badjoin");
|
assert(!"badjoin");
|
||||||
return EDEADLK;
|
return EDEADLK;
|
||||||
}
|
}
|
||||||
pthread_wait(pt);
|
_pthread_wait(pt);
|
||||||
if (value_ptr) {
|
if (value_ptr) {
|
||||||
*value_ptr = pt->rc;
|
*value_ptr = pt->rc;
|
||||||
}
|
}
|
||||||
pthread_free(pt);
|
_pthread_free(pt);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ int pthread_key_create(pthread_key_t *key, pthread_key_dtor dtor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static textexit void _pthread_key_atexit(void) {
|
static textexit void _pthread_key_atexit(void) {
|
||||||
_pthread_key_destruct(_pthread_keys);
|
_pthread_key_destruct(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((__constructor__)) static textstartup void _pthread_key_init() {
|
__attribute__((__constructor__)) static textstartup void _pthread_key_init() {
|
||||||
|
|
|
@ -25,6 +25,7 @@ void _pthread_key_destruct(void *key[PTHREAD_KEYS_MAX]) {
|
||||||
uint64_t x;
|
uint64_t x;
|
||||||
void *value;
|
void *value;
|
||||||
pthread_key_dtor dtor;
|
pthread_key_dtor dtor;
|
||||||
|
if (!key) key = _pthread_keys;
|
||||||
StartOver:
|
StartOver:
|
||||||
for (i = 0; i < (PTHREAD_KEYS_MAX + 63) / 64; ++i) {
|
for (i = 0; i < (PTHREAD_KEYS_MAX + 63) / 64; ++i) {
|
||||||
x = _pthread_key_usage[i];
|
x = _pthread_key_usage[i];
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
|
#include "libc/intrin/pthread.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
|
@ -29,46 +30,46 @@
|
||||||
static struct Zombie {
|
static struct Zombie {
|
||||||
struct Zombie *next;
|
struct Zombie *next;
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
} * pthread_zombies;
|
} * _pthread_zombies;
|
||||||
|
|
||||||
void pthread_zombies_add(struct PosixThread *pt) {
|
void _pthread_zombies_add(struct PosixThread *pt) {
|
||||||
struct Zombie *z;
|
struct Zombie *z;
|
||||||
if ((z = malloc(sizeof(struct Zombie)))) {
|
if ((z = malloc(sizeof(struct Zombie)))) {
|
||||||
z->pt = pt;
|
z->pt = pt;
|
||||||
z->next = atomic_load(&pthread_zombies);
|
z->next = atomic_load(&_pthread_zombies);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (atomic_compare_exchange_weak(&pthread_zombies, &z->next, z)) {
|
if (atomic_compare_exchange_weak(&_pthread_zombies, &z->next, z)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pthread_zombies_collect(struct Zombie *z) {
|
static void _pthread_zombies_collect(struct Zombie *z) {
|
||||||
pthread_wait(z->pt);
|
_pthread_wait(z->pt);
|
||||||
pthread_free(z->pt);
|
_pthread_free(z->pt);
|
||||||
free(z);
|
free(z);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pthread_zombies_decimate(void) {
|
void _pthread_zombies_decimate(void) {
|
||||||
struct Zombie *z;
|
struct Zombie *z;
|
||||||
while ((z = atomic_load(&pthread_zombies)) &&
|
while ((z = atomic_load_explicit(&_pthread_zombies, memory_order_relaxed)) &&
|
||||||
atomic_load(&z->pt->status) == kPosixThreadZombie) {
|
atomic_load(&z->pt->status) == kPosixThreadZombie) {
|
||||||
if (atomic_compare_exchange_strong(&pthread_zombies, &z, z->next)) {
|
if (atomic_compare_exchange_weak(&_pthread_zombies, &z, z->next)) {
|
||||||
pthread_zombies_collect(z);
|
_pthread_zombies_collect(z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pthread_zombies_harvest(void) {
|
void _pthread_zombies_harvest(void) {
|
||||||
struct Zombie *z;
|
struct Zombie *z;
|
||||||
while ((z = atomic_load(&pthread_zombies))) {
|
while ((z = atomic_load_explicit(&_pthread_zombies, memory_order_relaxed))) {
|
||||||
if (atomic_compare_exchange_weak(&pthread_zombies, &z, z->next)) {
|
if (atomic_compare_exchange_weak(&_pthread_zombies, &z, z->next)) {
|
||||||
pthread_zombies_collect(z);
|
_pthread_zombies_collect(z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((__constructor__)) static void init(void) {
|
__attribute__((__constructor__)) static void init(void) {
|
||||||
atexit(pthread_zombies_harvest);
|
atexit(_pthread_zombies_harvest);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,15 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/stack.h"
|
#include "libc/runtime/stack.h"
|
||||||
|
#include "libc/testlib/subprocess.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
|
#if 0
|
||||||
static void *Increment(void *arg) {
|
static void *Increment(void *arg) {
|
||||||
ASSERT_EQ(gettid(), pthread_getthreadid_np());
|
ASSERT_EQ(gettid(), pthread_getthreadid_np());
|
||||||
return (void *)((uintptr_t)arg + 1);
|
return (void *)((uintptr_t)arg + 1);
|
||||||
|
@ -98,3 +101,17 @@ TEST(pthread_detach, testCustomStack_withReallySmallSize) {
|
||||||
ASSERT_EQ(0, pthread_join(id, 0));
|
ASSERT_EQ(0, pthread_join(id, 0));
|
||||||
free(stk);
|
free(stk);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TEST(pthread_exit, mainThreadWorks) {
|
||||||
|
// _Exit1() can't set process exit code on XNU/NetBSD/OpenBSD.
|
||||||
|
if (IsLinux() || IsFreebsd() || IsWindows()) {
|
||||||
|
SPAWN(fork);
|
||||||
|
pthread_exit((void *)2);
|
||||||
|
EXITS(2);
|
||||||
|
} else {
|
||||||
|
SPAWN(fork);
|
||||||
|
pthread_exit((void *)0);
|
||||||
|
EXITS(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue