mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-24 23:09:02 +00:00
Make more improvements to threading support
- fix rare thread exit race condition on openbsd - pthread_getattr_np() now supplies detached status - child threads may now pthread_join() the main thread - introduce sigandset(), sigorset(), and sigisemptyset() - introduce pthread_cleanup_push() and pthread_cleanup_pop()
This commit is contained in:
parent
38df0a4186
commit
4a6fd3d910
52 changed files with 586 additions and 241 deletions
libc
assert.h
calls/struct
intrin
log
nexgen32e
runtime
stdio
thread
posixthread.internal.hpthread_attr_getdetachstate.cpthread_attr_getguardsize.cpthread_attr_getinheritsched.cpthread_attr_getschedparam.cpthread_attr_getschedpolicy.cpthread_attr_getscope.cpthread_attr_getstack.cpthread_attr_getstacksize.cpthread_attr_init.cpthread_attr_setdetachstate.cpthread_attr_setguardsize.cpthread_attr_setinheritsched.cpthread_attr_setschedparam.cpthread_attr_setschedpolicy.cpthread_attr_setscope.cpthread_attr_setstack.cpthread_attr_setstacksize.cpthread_cleanup.cpthread_cleanup_pop.cpthread_cleanup_push.cpthread_create.cpthread_exit.cpthread_getattr_np.cpthread_getschedparam.cpthread_join.cpthread_main.cpthread_reschedule.cpthread_self.cpthread_setschedparam.cpthread_ungarbage.cthread.htls.hwait0.c
net/turfwar
test/libc
|
@ -2,6 +2,7 @@
|
|||
#define COSMOPOLITAN_LIBC_ASSERT_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
#include "libc/intrin/kprintf.h"
|
||||
|
||||
extern bool __assert_disable;
|
||||
void __assert_fail(const char *, const char *, int) hidden relegated;
|
||||
|
@ -23,11 +24,12 @@ void __assert_fail(const char *, const char *, int) hidden relegated;
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
#define _npassert(x) \
|
||||
do { \
|
||||
if (__builtin_expect(!(x), 0)) { \
|
||||
notpossible; \
|
||||
} \
|
||||
#define _npassert(x) \
|
||||
do { \
|
||||
if (__builtin_expect(!(x), 0)) { \
|
||||
kprintf("%s:%d: oh no!\n", __FILE__, __LINE__); \
|
||||
notpossible; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
|
|
|
@ -11,6 +11,9 @@ int sigaddset(sigset_t *, int) paramsnonnull();
|
|||
int sigdelset(sigset_t *, int) paramsnonnull();
|
||||
int sigemptyset(sigset_t *) paramsnonnull();
|
||||
int sigfillset(sigset_t *) paramsnonnull();
|
||||
int sigandset(sigset_t *, const sigset_t *, const sigset_t *) paramsnonnull();
|
||||
int sigorset(sigset_t *, const sigset_t *, const sigset_t *) paramsnonnull();
|
||||
int sigisemptyset(const sigset_t *) paramsnonnull();
|
||||
int sigismember(const sigset_t *, int) paramsnonnull() nosideeffect;
|
||||
int sigprocmask(int, const sigset_t *, sigset_t *);
|
||||
int sigsuspend(const sigset_t *);
|
||||
|
|
|
@ -16,19 +16,24 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asmflag.h"
|
||||
#include "libc/intrin/promises.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/nr.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
__msabi extern typeof(ExitThread) *const __imp_ExitThread;
|
||||
|
||||
/**
|
||||
* Terminates thread with raw system call.
|
||||
*
|
||||
* The function you want is pthread_exit(). If you call this function
|
||||
* whilst using the pthreads then your joiners might not get woken up
|
||||
* on non-Linux platforms where we zero __get_tls()->tib_tid manually
|
||||
*
|
||||
* 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
|
||||
|
|
36
libc/intrin/sigandset.c
Normal file
36
libc/intrin/sigandset.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*-*- 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/struct/sigset.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Bitwise ANDs two signal sets.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int sigandset(sigset_t *set, const sigset_t *x, const sigset_t *y) {
|
||||
int i;
|
||||
for (i = 0; i < ARRAYLEN(set->__bits); ++i) {
|
||||
set->__bits[i] = x->__bits[i] & y->__bits[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
38
libc/intrin/sigisemptyset.c
Normal file
38
libc/intrin/sigisemptyset.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*-*- 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/struct/sigset.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Determines if signal set is empty.
|
||||
*
|
||||
* @return 1 if empty, 0 if non-empty, or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int sigisemptyset(const sigset_t *set) {
|
||||
int i;
|
||||
for (i = 0; i < ARRAYLEN(set->__bits); ++i) {
|
||||
if (set->__bits[i]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
36
libc/intrin/sigorset.c
Normal file
36
libc/intrin/sigorset.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*-*- 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/struct/sigset.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Bitwise ORs two signal sets.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int sigorset(sigset_t *set, const sigset_t *x, const sigset_t *y) {
|
||||
int i;
|
||||
for (i = 0; i < ARRAYLEN(set->__bits); ++i) {
|
||||
set->__bits[i] = x->__bits[i] | y->__bits[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -16,12 +16,12 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/color.internal.h"
|
||||
#include "libc/log/internal.h"
|
||||
|
@ -71,9 +71,6 @@ relegated void __check_fail(const char *suffix, const char *opstr,
|
|||
kprintf(" %s", __argv[i]);
|
||||
}
|
||||
kprintf("%s\n", RESET);
|
||||
if (!IsTiny() && e == ENOMEM) {
|
||||
__print_maps();
|
||||
}
|
||||
__die();
|
||||
unreachable;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "third_party/dlmalloc/dlmalloc.h"
|
||||
|
||||
/**
|
||||
|
|
|
@ -223,10 +223,10 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si,
|
|||
host, getpid(), gettid(), program_invocation_name, names.sysname,
|
||||
names.version, names.nodename, names.release);
|
||||
if (ctx) {
|
||||
kprintf("\n");
|
||||
ShowFunctionCalls(ctx);
|
||||
ShowGeneralRegisters(ctx);
|
||||
ShowSseRegisters(ctx);
|
||||
kprintf("\n");
|
||||
ShowFunctionCalls(ctx);
|
||||
}
|
||||
kprintf("\n");
|
||||
__print_maps();
|
||||
|
|
|
@ -57,4 +57,5 @@ __gc: mov %fs:0,%rcx # __get_tls()
|
|||
leave
|
||||
ret
|
||||
9: ud2
|
||||
nop
|
||||
.endfn __gc,globl,hidden
|
||||
|
|
|
@ -240,12 +240,14 @@ static wontreturn void FreebsdThreadMain(void *p) {
|
|||
// we no longer use the stack after this point
|
||||
// void thr_exit(%rdi = long *state);
|
||||
asm volatile("movl\t$0,%0\n\t" // *wt->ztid = 0
|
||||
"syscall\n\t" // _umtx_op()
|
||||
"movl\t$431,%%eax\n\t" // thr_exit()
|
||||
"xor\t%%edi,%%edi\n\t"
|
||||
"syscall"
|
||||
"syscall\n\t" // _umtx_op(wt->ztid, WAKE, INT_MAX)
|
||||
"movl\t$431,%%eax\n\t" // thr_exit(long *nonzeroes_and_wake)
|
||||
"xor\t%%edi,%%edi\n\t" // sad we can't use this free futex op
|
||||
"syscall\n\t" // exit1() fails if thread is orphaned
|
||||
"movl\t$1,%%eax\n\t" // exit()
|
||||
"syscall" //
|
||||
: "=m"(*wt->ztid)
|
||||
: "a"(454), "D"(wt->ztid), "S"(UMTX_OP_WAKE)
|
||||
: "a"(454), "D"(wt->ztid), "S"(UMTX_OP_WAKE), "d"(INT_MAX)
|
||||
: "rcx", "r8", "r9", "r10", "r11", "memory");
|
||||
notpossible;
|
||||
}
|
||||
|
@ -289,15 +291,6 @@ static int CloneFreebsd(int (*func)(void *, int), char *stk, size_t stksz,
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// OPEN BESIYATA DISHMAYA
|
||||
|
||||
static void *oldrsp;
|
||||
|
||||
__attribute__((__constructor__)) static void OpenbsdGetSafeRsp(void) {
|
||||
// main thread stack should never be freed during process lifetime. we
|
||||
// won't actually change this stack below. we just need need a place
|
||||
// where threads can park RSP for a few instructions while dying.
|
||||
oldrsp = __builtin_frame_address(0);
|
||||
}
|
||||
|
||||
// we can't use address sanitizer because:
|
||||
// 1. __asan_handle_no_return wipes stack [todo?]
|
||||
noasan static wontreturn void OpenbsdThreadMain(void *p) {
|
||||
|
@ -305,21 +298,15 @@ noasan static wontreturn void OpenbsdThreadMain(void *p) {
|
|||
*wt->ptid = wt->tid;
|
||||
*wt->ctid = wt->tid;
|
||||
wt->func(wt->arg, wt->tid);
|
||||
// we no longer use the stack after this point. however openbsd
|
||||
// validates the rsp register too so a race condition can still
|
||||
// happen if the parent tries to free the stack. we'll solve it
|
||||
// by simply changing rsp back to the old value before exiting!
|
||||
// although ideally there should be a better solution.
|
||||
//
|
||||
// void __threxit(%rdi = int32_t *notdead);
|
||||
asm volatile("mov\t%2,%%rsp\n\t"
|
||||
"movl\t$0,(%%rdi)\n\t" // *wt->ztid = 0
|
||||
"syscall\n\t" // futex()
|
||||
"mov\t$302,%%eax\n\t" // threxit()
|
||||
asm volatile("mov\t%2,%%rsp\n\t" // so syscall can validate stack exists
|
||||
"movl\t$0,(%%rdi)\n\t" // *wt->ztid = 0 (old stack now free'd)
|
||||
"syscall\n\t" // futex(int*, op, val) will wake wait0
|
||||
"xor\t%%edi,%%edi\n\t" // so kernel doesn't write to old stack
|
||||
"mov\t$302,%%eax\n\t" // __threxit(int *notdead) doesn't wake
|
||||
"syscall"
|
||||
: "=m"(*wt->ztid)
|
||||
: "a"(83), "m"(oldrsp), "D"(wt->ztid), "S"(FUTEX_WAKE),
|
||||
"d"(INT_MAX)
|
||||
: "a"(83), "m"(__oldstack), "D"(wt->ztid),
|
||||
"S"(2 /* FUTEX_WAKE */), "d"(INT_MAX)
|
||||
: "rcx", "r11", "memory");
|
||||
notpossible;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "libc/macros.internal.h"
|
||||
|
||||
// Returns granularity of memory manager.
|
||||
//
|
||||
// @see sysconf(_SC_PAGE_SIZE) which is portable
|
||||
getpagesize:
|
||||
.leafprologue
|
||||
.profilable
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/lock.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
* Acquires reentrant lock on stdio object, blocking if needed.
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/lock.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
* Tries to acquire reentrant stdio object lock.
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/lock.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
* Releases lock on stdio object.
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#define PT_OWNSTACK 1
|
||||
#define PT_MAINTHREAD 2
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
|
@ -52,9 +56,6 @@ enum PosixThreadStatus {
|
|||
// - kPosixThreadZombie -> _pthread_free() will happen whenever
|
||||
// convenient, e.g. pthread_create() entry or atexit handler.
|
||||
kPosixThreadZombie,
|
||||
|
||||
// special main thread
|
||||
kPosixThreadMain,
|
||||
};
|
||||
|
||||
struct PosixThread {
|
||||
|
@ -62,15 +63,17 @@ struct PosixThread {
|
|||
void *(*start_routine)(void *);
|
||||
void *arg; // start_routine's parameter
|
||||
void *rc; // start_routine's return value
|
||||
bool ownstack; // should we free it
|
||||
int flags; // see PT_* constants
|
||||
int tid; // clone parent tid
|
||||
char *altstack; // thread sigaltstack
|
||||
char *tls; // bottom of tls allocation
|
||||
struct CosmoTib *tib; // middle of tls allocation
|
||||
jmp_buf exiter; // for pthread_exit
|
||||
pthread_attr_t attr;
|
||||
struct _pthread_cleanup_buffer *cleanup;
|
||||
};
|
||||
|
||||
extern struct PosixThread _pthread_main;
|
||||
hidden extern pthread_spinlock_t _pthread_keys_lock;
|
||||
hidden extern uint64_t _pthread_key_usage[(PTHREAD_KEYS_MAX + 63) / 64];
|
||||
hidden extern pthread_key_dtor _pthread_key_dtor[PTHREAD_KEYS_MAX];
|
||||
|
@ -79,6 +82,7 @@ hidden extern _Thread_local void *_pthread_keys[PTHREAD_KEYS_MAX];
|
|||
int _pthread_reschedule(struct PosixThread *) hidden;
|
||||
int _pthread_setschedparam_freebsd(int, int, const struct sched_param *) hidden;
|
||||
void _pthread_free(struct PosixThread *) hidden;
|
||||
void _pthread_cleanup(struct PosixThread *) hidden;
|
||||
void _pthread_ungarbage(void) hidden;
|
||||
void _pthread_wait(struct PosixThread *) hidden;
|
||||
void _pthread_zombies_add(struct PosixThread *) hidden;
|
||||
|
|
|
@ -27,6 +27,6 @@
|
|||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) {
|
||||
*detachstate = attr->detachstate;
|
||||
*detachstate = attr->__detachstate;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,6 @@
|
|||
*/
|
||||
errno_t pthread_attr_getguardsize(const pthread_attr_t *attr,
|
||||
size_t *guardsize) {
|
||||
*guardsize = attr->guardsize;
|
||||
*guardsize = attr->__guardsize;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,6 @@
|
|||
*/
|
||||
int pthread_attr_getinheritsched(const pthread_attr_t *attr,
|
||||
int *inheritsched) {
|
||||
*inheritsched = attr->inheritsched;
|
||||
*inheritsched = attr->__inheritsched;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,6 @@
|
|||
*/
|
||||
int pthread_attr_getschedparam(const pthread_attr_t *attr,
|
||||
struct sched_param *param) {
|
||||
*param = (struct sched_param){attr->schedparam};
|
||||
*param = (struct sched_param){attr->__schedparam};
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,6 @@
|
|||
* Gets thread scheduler policy attribute
|
||||
*/
|
||||
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) {
|
||||
*policy = attr->schedpolicy;
|
||||
*policy = attr->__schedpolicy;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,6 @@
|
|||
#include "libc/thread/thread.h"
|
||||
|
||||
int pthread_attr_getscope(const pthread_attr_t *a, int *x) {
|
||||
*x = a->scope;
|
||||
*x = a->__scope;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
*/
|
||||
errno_t pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr,
|
||||
size_t *stacksize) {
|
||||
*stackaddr = attr->stackaddr;
|
||||
*stacksize = attr->stacksize;
|
||||
*stackaddr = attr->__stackaddr;
|
||||
*stacksize = attr->__stacksize;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -29,8 +29,8 @@
|
|||
* @see pthread_attr_setstacksize()
|
||||
*/
|
||||
errno_t pthread_attr_getstacksize(const pthread_attr_t *a, size_t *x) {
|
||||
if (a->stacksize) {
|
||||
*x = a->stacksize;
|
||||
if (a->__stacksize) {
|
||||
*x = a->__stacksize;
|
||||
} else {
|
||||
*x = GetStackSize();
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
*/
|
||||
errno_t pthread_attr_init(pthread_attr_t *attr) {
|
||||
*attr = (pthread_attr_t){
|
||||
.stacksize = GetStackSize(),
|
||||
.guardsize = PAGESIZE,
|
||||
.__stacksize = GetStackSize(),
|
||||
.__guardsize = PAGESIZE,
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) {
|
|||
switch (detachstate) {
|
||||
case PTHREAD_CREATE_JOINABLE:
|
||||
case PTHREAD_CREATE_DETACHED:
|
||||
attr->detachstate = detachstate;
|
||||
attr->__detachstate = detachstate;
|
||||
return 0;
|
||||
default:
|
||||
return EINVAL;
|
||||
|
|
|
@ -25,6 +25,6 @@
|
|||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
errno_t pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) {
|
||||
attr->guardsize = guardsize;
|
||||
attr->__guardsize = guardsize;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ errno_t pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched) {
|
|||
switch (inheritsched) {
|
||||
case PTHREAD_INHERIT_SCHED:
|
||||
case PTHREAD_EXPLICIT_SCHED:
|
||||
attr->inheritsched = inheritsched;
|
||||
attr->__inheritsched = inheritsched;
|
||||
return 0;
|
||||
default:
|
||||
assert(!"badval");
|
||||
|
|
|
@ -43,6 +43,6 @@
|
|||
int pthread_attr_setschedparam(pthread_attr_t *attr,
|
||||
const struct sched_param *param) {
|
||||
if (!param) return EINVAL;
|
||||
attr->schedparam = param->sched_priority;
|
||||
attr->__schedparam = param->sched_priority;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -42,6 +42,6 @@
|
|||
* @see sched_setscheduler()
|
||||
*/
|
||||
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) {
|
||||
attr->schedpolicy = policy;
|
||||
attr->__schedpolicy = policy;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,6 @@
|
|||
#include "libc/thread/thread.h"
|
||||
|
||||
int pthread_attr_setscope(pthread_attr_t *a, int x) {
|
||||
a->scope = x;
|
||||
a->__scope = x;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -65,15 +65,15 @@
|
|||
errno_t pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr,
|
||||
size_t stacksize) {
|
||||
if (!stackaddr) {
|
||||
attr->stackaddr = 0;
|
||||
attr->stacksize = 0;
|
||||
attr->__stackaddr = 0;
|
||||
attr->__stacksize = 0;
|
||||
return 0;
|
||||
}
|
||||
if (stacksize < PTHREAD_STACK_MIN ||
|
||||
(IsAsan() && !__asan_is_valid(stackaddr, stacksize))) {
|
||||
return EINVAL;
|
||||
}
|
||||
attr->stackaddr = stackaddr;
|
||||
attr->stacksize = stacksize;
|
||||
attr->__stackaddr = stackaddr;
|
||||
attr->__stacksize = stacksize;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,6 @@
|
|||
*/
|
||||
errno_t pthread_attr_setstacksize(pthread_attr_t *a, size_t stacksize) {
|
||||
if (stacksize < PTHREAD_STACK_MIN) return EINVAL;
|
||||
a->stacksize = stacksize;
|
||||
a->__stacksize = stacksize;
|
||||
return 0;
|
||||
}
|
||||
|
|
38
libc/thread/pthread_cleanup.c
Normal file
38
libc/thread/pthread_cleanup.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*-*- 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/assert.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
void _pthread_cleanup(struct PosixThread *pt) {
|
||||
_pthread_ungarbage();
|
||||
if (_weaken(_pthread_key_destruct)) {
|
||||
_weaken(_pthread_key_destruct)(0);
|
||||
}
|
||||
if (atomic_load_explicit(&pt->status, memory_order_acquire) ==
|
||||
kPosixThreadDetached) {
|
||||
atomic_store_explicit(&pt->status, kPosixThreadZombie,
|
||||
memory_order_release);
|
||||
} else {
|
||||
atomic_store_explicit(&pt->status, kPosixThreadTerminated,
|
||||
memory_order_release);
|
||||
}
|
||||
}
|
30
libc/thread/pthread_cleanup_pop.c
Normal file
30
libc/thread/pthread_cleanup_pop.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*-*- 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/assert.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
void _pthread_cleanup_pop(struct _pthread_cleanup_buffer *cb, int execute) {
|
||||
struct PosixThread *pt = (struct PosixThread *)pthread_self();
|
||||
_npassert(cb == pt->cleanup);
|
||||
pt->cleanup = cb->__prev;
|
||||
if (execute) {
|
||||
cb->__routine(cb->__arg);
|
||||
}
|
||||
}
|
29
libc/thread/pthread_cleanup_push.c
Normal file
29
libc/thread/pthread_cleanup_push.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*-*- 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/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
void _pthread_cleanup_push(struct _pthread_cleanup_buffer *cb,
|
||||
void (*routine)(void *), void *arg) {
|
||||
struct PosixThread *pt = (struct PosixThread *)pthread_self();
|
||||
cb->__routine = routine;
|
||||
cb->__arg = arg;
|
||||
cb->__prev = pt->cleanup;
|
||||
pt->cleanup = cb;
|
||||
}
|
|
@ -57,11 +57,12 @@ void _pthread_wait(struct PosixThread *pt) {
|
|||
}
|
||||
|
||||
void _pthread_free(struct PosixThread *pt) {
|
||||
if (pt->flags & PT_MAINTHREAD) return;
|
||||
free(pt->tls);
|
||||
if (pt->ownstack && //
|
||||
pt->attr.stackaddr && //
|
||||
pt->attr.stackaddr != MAP_FAILED) {
|
||||
if (munmap(pt->attr.stackaddr, pt->attr.stacksize)) {
|
||||
if ((pt->flags & PT_OWNSTACK) && //
|
||||
pt->attr.__stackaddr && //
|
||||
pt->attr.__stackaddr != MAP_FAILED) {
|
||||
if (munmap(pt->attr.__stackaddr, pt->attr.__stacksize)) {
|
||||
notpossible;
|
||||
}
|
||||
}
|
||||
|
@ -83,25 +84,19 @@ static int PosixThread(void *arg, int tid) {
|
|||
notpossible;
|
||||
}
|
||||
}
|
||||
if (pt->attr.inheritsched == PTHREAD_EXPLICIT_SCHED) {
|
||||
if (pt->attr.__inheritsched == PTHREAD_EXPLICIT_SCHED) {
|
||||
_pthread_reschedule(pt);
|
||||
}
|
||||
// set long jump handler so pthread_exit can bring control back here
|
||||
if (!setjmp(pt->exiter)) {
|
||||
__get_tls()->tib_pthread = (pthread_t)pt;
|
||||
pt->rc = pt->start_routine(pt->arg);
|
||||
// ensure pthread_cleanup_pop(), and pthread_exit() popped cleanup
|
||||
_npassert(!pt->cleanup);
|
||||
}
|
||||
if (_weaken(_pthread_key_destruct)) {
|
||||
_weaken(_pthread_key_destruct)(0);
|
||||
}
|
||||
_pthread_ungarbage();
|
||||
if (atomic_load_explicit(&pt->status, memory_order_acquire) ==
|
||||
kPosixThreadDetached) {
|
||||
atomic_store_explicit(&pt->status, kPosixThreadZombie,
|
||||
memory_order_release);
|
||||
} else {
|
||||
atomic_store_explicit(&pt->status, kPosixThreadTerminated,
|
||||
memory_order_release);
|
||||
}
|
||||
// run garbage collector, call key destructors, and set change state
|
||||
_pthread_cleanup(pt);
|
||||
// return to clone polyfill which clears tid, wakes futex, and exits
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -113,8 +108,8 @@ static int FixupCustomStackOnOpenbsd(pthread_attr_t *attr) {
|
|||
size_t n;
|
||||
int e, rc;
|
||||
uintptr_t x, y;
|
||||
n = attr->stacksize;
|
||||
x = (uintptr_t)attr->stackaddr;
|
||||
n = attr->__stacksize;
|
||||
x = (uintptr_t)attr->__stackaddr;
|
||||
y = ROUNDUP(x, PAGESIZE);
|
||||
n -= y - x;
|
||||
n = ROUNDDOWN(n, PAGESIZE);
|
||||
|
@ -122,8 +117,8 @@ static int FixupCustomStackOnOpenbsd(pthread_attr_t *attr) {
|
|||
if (__sys_mmap((void *)y, n, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_FIXED | MAP_ANON_OPENBSD | MAP_STACK_OPENBSD,
|
||||
-1, 0, 0) == (void *)y) {
|
||||
attr->stackaddr = (void *)y;
|
||||
attr->stacksize = n;
|
||||
attr->__stackaddr = (void *)y;
|
||||
attr->__stacksize = n;
|
||||
return 0;
|
||||
} else {
|
||||
rc = errno;
|
||||
|
@ -216,7 +211,7 @@ errno_t pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
|||
}
|
||||
|
||||
// setup stack
|
||||
if (pt->attr.stackaddr) {
|
||||
if (pt->attr.__stackaddr) {
|
||||
// caller supplied their own stack
|
||||
// assume they know what they're doing as much as possible
|
||||
if (IsOpenbsd()) {
|
||||
|
@ -229,37 +224,40 @@ errno_t pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
|||
// cosmo is managing the stack
|
||||
// 1. in mono repo optimize for tiniest stack possible
|
||||
// 2. in public world optimize to *work* regardless of memory
|
||||
pt->ownstack = true;
|
||||
pt->attr.stacksize = MAX(pt->attr.stacksize, GetStackSize());
|
||||
pt->attr.stacksize = _roundup2pow(pt->attr.stacksize);
|
||||
pt->attr.guardsize = ROUNDUP(pt->attr.guardsize, PAGESIZE);
|
||||
if (pt->attr.guardsize + PAGESIZE >= pt->attr.stacksize) {
|
||||
pt->flags = PT_OWNSTACK;
|
||||
pt->attr.__stacksize = MAX(pt->attr.__stacksize, GetStackSize());
|
||||
pt->attr.__stacksize = _roundup2pow(pt->attr.__stacksize);
|
||||
pt->attr.__guardsize = ROUNDUP(pt->attr.__guardsize, PAGESIZE);
|
||||
if (pt->attr.__guardsize + PAGESIZE >= pt->attr.__stacksize) {
|
||||
_pthread_free(pt);
|
||||
return EINVAL;
|
||||
}
|
||||
if (pt->attr.guardsize == PAGESIZE) {
|
||||
if (pt->attr.__guardsize == PAGESIZE) {
|
||||
// user is wisely using smaller stacks with default guard size
|
||||
pt->attr.stackaddr = mmap(0, pt->attr.stacksize, PROT_READ | PROT_WRITE,
|
||||
MAP_STACK | MAP_ANONYMOUS, -1, 0);
|
||||
pt->attr.__stackaddr =
|
||||
mmap(0, pt->attr.__stacksize, PROT_READ | PROT_WRITE,
|
||||
MAP_STACK | MAP_ANONYMOUS, -1, 0);
|
||||
} else {
|
||||
// user is tuning things, performance may suffer
|
||||
pt->attr.stackaddr = mmap(0, pt->attr.stacksize, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (pt->attr.stackaddr != MAP_FAILED) {
|
||||
pt->attr.__stackaddr =
|
||||
mmap(0, pt->attr.__stacksize, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (pt->attr.__stackaddr != MAP_FAILED) {
|
||||
if (IsOpenbsd() &&
|
||||
__sys_mmap(
|
||||
pt->attr.stackaddr, pt->attr.stacksize, PROT_READ | PROT_WRITE,
|
||||
pt->attr.__stackaddr, pt->attr.__stacksize,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_FIXED | MAP_ANON_OPENBSD | MAP_STACK_OPENBSD,
|
||||
-1, 0, 0) != pt->attr.stackaddr) {
|
||||
-1, 0, 0) != pt->attr.__stackaddr) {
|
||||
notpossible;
|
||||
}
|
||||
if (pt->attr.guardsize && !IsWindows() &&
|
||||
mprotect(pt->attr.stackaddr, pt->attr.guardsize, PROT_NONE)) {
|
||||
if (pt->attr.__guardsize && !IsWindows() &&
|
||||
mprotect(pt->attr.__stackaddr, pt->attr.__guardsize, PROT_NONE)) {
|
||||
notpossible;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pt->attr.stackaddr == MAP_FAILED) {
|
||||
if (pt->attr.__stackaddr == MAP_FAILED) {
|
||||
rc = errno;
|
||||
_pthread_free(pt);
|
||||
errno = e;
|
||||
|
@ -269,8 +267,9 @@ errno_t pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
|||
return EAGAIN;
|
||||
}
|
||||
}
|
||||
if (IsAsan() && pt->attr.guardsize) {
|
||||
__asan_poison(pt->attr.stackaddr, pt->attr.guardsize, kAsanStackOverflow);
|
||||
if (IsAsan() && pt->attr.__guardsize) {
|
||||
__asan_poison(pt->attr.__stackaddr, pt->attr.__guardsize,
|
||||
kAsanStackOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,7 +279,7 @@ errno_t pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
|||
}
|
||||
|
||||
// set initial status
|
||||
switch (pt->attr.detachstate) {
|
||||
switch (pt->attr.__detachstate) {
|
||||
case PTHREAD_CREATE_JOINABLE:
|
||||
pt->status = kPosixThreadJoinable;
|
||||
break;
|
||||
|
@ -294,8 +293,8 @@ errno_t pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
|||
}
|
||||
|
||||
// launch PosixThread(pt) in new thread
|
||||
if (clone(PosixThread, pt->attr.stackaddr,
|
||||
pt->attr.stacksize - (IsOpenbsd() ? 16 : 0),
|
||||
if (clone(PosixThread, pt->attr.__stackaddr,
|
||||
pt->attr.__stacksize - (IsOpenbsd() ? 16 : 0),
|
||||
CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
|
||||
CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID |
|
||||
CLONE_CHILD_CLEARTID,
|
||||
|
|
|
@ -16,19 +16,45 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "third_party/nsync/futex.internal.h"
|
||||
|
||||
STATIC_YOINK("_pthread_main");
|
||||
|
||||
/**
|
||||
* 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
|
||||
* For example, a thread could terminate early as follows:
|
||||
*
|
||||
* pthread_exit((void *)123);
|
||||
*
|
||||
* The result value could then be obtained when joining the thread:
|
||||
*
|
||||
* void *rc;
|
||||
* pthread_join(id, &rc);
|
||||
* assert((intptr_t)rc == 123);
|
||||
*
|
||||
* Under normal circumstances a thread can exit by simply returning from
|
||||
* the callback function that was supplied to pthread_create(). This may
|
||||
* be used if the thread wishes to exit at any other point in the thread
|
||||
* lifecycle, in which case this function is responsible for ensuring we
|
||||
* invoke _gc(), _defer(), and pthread_cleanup_push() callbacks, as well
|
||||
* as pthread_key_create() destructors.
|
||||
*
|
||||
* If the current thread is an orphaned thread, or is the main thread
|
||||
* when no other threads were created, then this will terminated your
|
||||
* process with an exit code of zero. It's not possible to supply a
|
||||
* non-zero exit status to wait4() via this function.
|
||||
*
|
||||
* Once a thread has exited, access to its stack memory is undefined.
|
||||
* The behavior of calling pthread_exit() from cleanup handlers and key
|
||||
* destructors is also undefined.
|
||||
*
|
||||
* @param rc is reported later to pthread_join()
|
||||
* @threadsafe
|
||||
|
@ -36,11 +62,32 @@
|
|||
*/
|
||||
wontreturn void pthread_exit(void *rc) {
|
||||
struct PosixThread *pt;
|
||||
pt = (struct PosixThread *)pthread_self();
|
||||
if (pt->status != kPosixThreadMain) {
|
||||
pt->rc = rc;
|
||||
_gclongjmp(pt->exiter, 1);
|
||||
struct _pthread_cleanup_buffer *cb;
|
||||
pt = (struct PosixThread *)__get_tls()->tib_pthread;
|
||||
pt->rc = rc;
|
||||
// the memory of pthread cleanup objects lives on the stack
|
||||
// so we need to harvest them before calling longjmp()
|
||||
while ((cb = pt->cleanup)) {
|
||||
pt->cleanup = cb->__prev;
|
||||
cb->__routine(cb->__arg);
|
||||
}
|
||||
if (~pt->flags & PT_MAINTHREAD) {
|
||||
// this thread was created by pthread_create()
|
||||
// garbage collector memory exists on a shadow stack. we don't need
|
||||
// to use _gclongjmp() since _pthread_ungarbage() will collect them
|
||||
// at the setjmp() site.
|
||||
longjmp(pt->exiter, 1);
|
||||
} else {
|
||||
_Exit1((int)(intptr_t)rc);
|
||||
// this is the main thread
|
||||
// release as much resources and possible and mark it terminated
|
||||
_pthread_cleanup(pt);
|
||||
// it's kind of irregular for a child thread to join the main thread
|
||||
// so we don't bother freeing the main thread's stack since it makes
|
||||
// this implementation so much simpler for example we want't to call
|
||||
// set_tid_address() upon every program startup which isn't possible
|
||||
// on non-linux platforms anyway.
|
||||
__get_tls()->tib_tid = 0;
|
||||
nsync_futex_wake_((int *)&__get_tls()->tib_tid, INT_MAX, !IsWindows());
|
||||
_Exit1(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,51 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
* Gets thread attributes.
|
||||
*
|
||||
* These attributes are copied from the ones supplied when
|
||||
* pthread_create() was called. However this function supplies
|
||||
* additional runtime information too:
|
||||
*
|
||||
* 1. The detached state. You can use pthread_attr_getdetachstate() on
|
||||
* the `attr` result to see, for example, if a thread detached itself
|
||||
* or some other thread detached it, after it was spawned.
|
||||
*
|
||||
* 2. The thread's stack. You can use pthread_attr_getstack() to see the
|
||||
* address and size of the stack that was allocated by cosmo for your
|
||||
* thread. This is useful for knowing where the stack is. It can also
|
||||
* be useful If you explicitly configured a stack too, since we might
|
||||
* have needed to slightly tune the address and size to meet platform
|
||||
* requirements. This function returns information that reflects that
|
||||
*
|
||||
* 3. You can view changes pthread_create() may have made to the stack
|
||||
* guard size by calling pthread_attr_getguardsize() on `attr`
|
||||
*
|
||||
* @param attr is output argument that receives attributes, which should
|
||||
* find its way to pthread_attr_destroy() when it's done being used
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ENOMEM is listed as a possible result by LSB 5.0
|
||||
*/
|
||||
int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr) {
|
||||
struct PosixThread *pt = (struct PosixThread *)thread;
|
||||
memcpy(attr, &pt->attr, sizeof(pt->attr));
|
||||
switch (atomic_load_explicit(&pt->status, memory_order_relaxed)) {
|
||||
case kPosixThreadJoinable:
|
||||
case kPosixThreadTerminated:
|
||||
attr->__detachstate = PTHREAD_CREATE_JOINABLE;
|
||||
break;
|
||||
case kPosixThreadDetached:
|
||||
case kPosixThreadZombie:
|
||||
attr->__detachstate = PTHREAD_CREATE_DETACHED;
|
||||
break;
|
||||
default:
|
||||
unreachable;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
int pthread_getschedparam(pthread_t thread, int *policy,
|
||||
struct sched_param *param) {
|
||||
struct PosixThread *pt = (struct PosixThread *)thread;
|
||||
*policy = pt->attr.schedpolicy;
|
||||
*param = (struct sched_param){pt->attr.schedparam};
|
||||
*policy = pt->attr.__schedpolicy;
|
||||
*param = (struct sched_param){pt->attr.__schedparam};
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -24,14 +24,16 @@
|
|||
/**
|
||||
* Waits for thread to terminate.
|
||||
*
|
||||
* @param value_ptr if non-null will receive pthread_exit() argument
|
||||
* @return 0 on success, or errno with error
|
||||
* @raise EDEADLK if thread is detached
|
||||
* @raise EDEADLK if `thread` is the current thread
|
||||
* @raise EINVAL if `thread` is detached
|
||||
* @returnserrno
|
||||
* @threadsafe
|
||||
*/
|
||||
int pthread_join(pthread_t thread, void **value_ptr) {
|
||||
struct PosixThread *pt;
|
||||
if (thread == pthread_self()) {
|
||||
if (thread == __get_tls()->tib_pthread) {
|
||||
return EDEADLK;
|
||||
}
|
||||
if (!(pt = (struct PosixThread *)thread) || //
|
||||
|
|
31
libc/thread/pthread_main.c
Normal file
31
libc/thread/pthread_main.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*-*- 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/thread/posixthread.internal.h"
|
||||
|
||||
// it's only be possible for this memory to be accessed when the user
|
||||
// has linked either pthread_self() or pthread_exit() which yoink it.
|
||||
struct PosixThread _pthread_main;
|
||||
|
||||
__attribute__((__constructor__)) static void pthread_self_init(void) {
|
||||
_pthread_main.tid = gettid();
|
||||
_pthread_main.tib = __get_tls();
|
||||
_pthread_main.flags = PT_MAINTHREAD;
|
||||
__get_tls()->tib_pthread = (pthread_t)&_pthread_main;
|
||||
}
|
|
@ -25,13 +25,14 @@
|
|||
|
||||
int _pthread_reschedule(struct PosixThread *pt) {
|
||||
int rc, e = errno;
|
||||
struct sched_param param = {pt->attr.schedparam};
|
||||
int policy = pt->attr.__schedpolicy;
|
||||
struct sched_param param = {pt->attr.__schedparam};
|
||||
if (IsNetbsd()) {
|
||||
rc = sys_sched_setparam_netbsd(0, pt->tid, pt->attr.schedpolicy, ¶m);
|
||||
rc = sys_sched_setparam_netbsd(0, pt->tid, policy, ¶m);
|
||||
} else if (IsLinux()) {
|
||||
rc = sys_sched_setscheduler(pt->tid, pt->attr.schedpolicy, ¶m);
|
||||
rc = sys_sched_setscheduler(pt->tid, policy, ¶m);
|
||||
} else if (IsFreebsd()) {
|
||||
rc = _pthread_setschedparam_freebsd(pt->tid, pt->attr.schedpolicy, ¶m);
|
||||
rc = _pthread_setschedparam_freebsd(pt->tid, policy, ¶m);
|
||||
} else {
|
||||
rc = enosys();
|
||||
}
|
||||
|
|
|
@ -16,21 +16,14 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
STATIC_YOINK("_pthread_main");
|
||||
|
||||
/**
|
||||
* Returns current POSIX thread.
|
||||
*/
|
||||
pthread_t pthread_self(void) {
|
||||
return __get_tls()->tib_pthread;
|
||||
}
|
||||
|
||||
static struct PosixThread pthread_main;
|
||||
__attribute__((__constructor__)) static void pthread_self_init(void) {
|
||||
pthread_main.tid = gettid();
|
||||
pthread_main.status = kPosixThreadMain;
|
||||
__get_tls()->tib_pthread = (pthread_t)&pthread_main;
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ int pthread_setschedparam(pthread_t thread, int policy,
|
|||
const struct sched_param *param) {
|
||||
struct PosixThread *pt = (struct PosixThread *)thread;
|
||||
if (!param) return EINVAL;
|
||||
pt->attr.schedpolicy = policy;
|
||||
pt->attr.schedparam = param->sched_priority;
|
||||
pt->attr.__schedpolicy = policy;
|
||||
pt->attr.__schedparam = param->sched_priority;
|
||||
return _pthread_reschedule(pt);
|
||||
}
|
||||
|
|
|
@ -16,17 +16,17 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/gc.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
void _pthread_ungarbage(void) {
|
||||
int i;
|
||||
struct Garbages *g;
|
||||
if ((g = __get_tls()->tib_garbages)) {
|
||||
// _pthread_exit() uses _gclongjmp() so if this assertion fails,
|
||||
// then the likely cause is the thread used gc() with longjmp().
|
||||
assert(!g->i);
|
||||
for (i = g->i; i--;) {
|
||||
((void (*)(intptr_t))g->p[i].fn)(g->p[i].arg);
|
||||
}
|
||||
free(g->p);
|
||||
free(g);
|
||||
}
|
||||
|
|
|
@ -77,16 +77,23 @@ typedef struct pthread_barrier_s {
|
|||
} pthread_barrier_t;
|
||||
|
||||
typedef struct pthread_attr_s {
|
||||
char detachstate;
|
||||
char inheritsched;
|
||||
int schedparam;
|
||||
int schedpolicy;
|
||||
int scope;
|
||||
unsigned guardsize;
|
||||
unsigned stacksize;
|
||||
char *stackaddr;
|
||||
char __detachstate;
|
||||
char __inheritsched;
|
||||
int __schedparam;
|
||||
int __schedpolicy;
|
||||
int __scope;
|
||||
unsigned __guardsize;
|
||||
unsigned __stacksize;
|
||||
char *__stackaddr;
|
||||
} pthread_attr_t;
|
||||
|
||||
struct _pthread_cleanup_buffer {
|
||||
void (*__routine)(void *);
|
||||
void *__arg;
|
||||
int __canceltype;
|
||||
struct _pthread_cleanup_buffer *__prev;
|
||||
};
|
||||
|
||||
int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *),
|
||||
void *);
|
||||
|
||||
|
@ -168,6 +175,18 @@ int pthread_barrier_wait(pthread_barrier_t *);
|
|||
int pthread_barrier_destroy(pthread_barrier_t *);
|
||||
int pthread_barrier_init(pthread_barrier_t *, const pthread_barrierattr_t *,
|
||||
unsigned);
|
||||
void _pthread_cleanup_pop(struct _pthread_cleanup_buffer *, int);
|
||||
void _pthread_cleanup_push(struct _pthread_cleanup_buffer *, void (*)(void *),
|
||||
void *);
|
||||
|
||||
#define pthread_cleanup_push(routine, arg) \
|
||||
{ \
|
||||
struct _pthread_cleanup_buffer _buffer; \
|
||||
_pthread_cleanup_push(&_buffer, (routine), (arg));
|
||||
|
||||
#define pthread_cleanup_pop(execute) \
|
||||
_pthread_cleanup_pop(&_buffer, (execute)); \
|
||||
}
|
||||
|
||||
#define pthread_spin_init(pSpin, multiprocess) ((pSpin)->_lock = 0, 0)
|
||||
#define pthread_spin_destroy(pSpin) ((pSpin)->_lock = -1, 0)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_THREAD_TLS_H_
|
||||
#define COSMOPOLITAN_LIBC_THREAD_TLS_H_
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
#define TLS_ALIGNMENT 64
|
||||
|
||||
|
@ -33,7 +32,7 @@ struct CosmoTib {
|
|||
void *tib_reserved5;
|
||||
void *tib_reserved6;
|
||||
void *tib_reserved7;
|
||||
void *tib_keys[PTHREAD_KEYS_MAX];
|
||||
void *tib_keys[128];
|
||||
};
|
||||
|
||||
extern int __threaded;
|
||||
|
|
|
@ -16,71 +16,10 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/consts/futex.h"
|
||||
#include "libc/thread/freebsd.internal.h"
|
||||
#include "libc/thread/wait0.internal.h"
|
||||
|
||||
int _futex(atomic_int *, int, int, const struct timespec *);
|
||||
|
||||
static int _wait0_sleep(struct timespec *ts) {
|
||||
int rc, e = errno;
|
||||
if ((rc = nanosleep(ts, 0))) {
|
||||
_npassert(errno == EINTR);
|
||||
errno = e;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void _wait0_poll(struct timespec *ts) {
|
||||
if (ts->tv_nsec < 1000) {
|
||||
// prefer sched_yield() for small time intervals because nanosleep()
|
||||
// will ceiling round to 1ms on the new technology.
|
||||
sched_yield();
|
||||
ts->tv_nsec <<= 1;
|
||||
} else if (!_wait0_sleep(ts)) {
|
||||
if (ts->tv_nsec < 100 * 1000 * 1000) {
|
||||
ts->tv_nsec <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _wait0_futex(const atomic_int *a, int e) {
|
||||
int rc, op;
|
||||
op = FUTEX_WAIT; // we need a shared mutex
|
||||
if (IsWindows()) {
|
||||
if (WaitOnAddress(a, &e, sizeof(*a), -1)) {
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = -GetLastError();
|
||||
}
|
||||
} else if (IsFreebsd()) {
|
||||
rc = sys_umtx_op(a, UMTX_OP_WAIT_UINT, e, 0, 0);
|
||||
} else {
|
||||
rc = _futex(a, op, e, 0);
|
||||
if (IsOpenbsd() && rc > 0) {
|
||||
rc = -rc;
|
||||
}
|
||||
}
|
||||
STRACE("futex(%t, %s, %d, %s) → %s", a, DescribeFutexOp(op), e, "NULL",
|
||||
DescribeErrnoResult(rc));
|
||||
_npassert(rc == 0 || //
|
||||
rc == -EINTR || //
|
||||
rc == -ETIMEDOUT || //
|
||||
rc == -EWOULDBLOCK);
|
||||
}
|
||||
#include "third_party/nsync/futex.internal.h"
|
||||
|
||||
/**
|
||||
* Blocks until memory location becomes zero.
|
||||
|
@ -88,20 +27,12 @@ static void _wait0_futex(const atomic_int *a, int e) {
|
|||
* This is intended to be used on the child thread id, which is updated
|
||||
* by the clone() system call when a thread terminates. We need this in
|
||||
* order to know when it's safe to free a thread's stack. This function
|
||||
* uses futexes on Linux, OpenBSD, and Windows. On other platforms this
|
||||
* uses polling with exponential backoff.
|
||||
* uses futexes on Linux, FreeBSD, OpenBSD, and Windows. On other
|
||||
* platforms this uses polling with exponential backoff.
|
||||
*/
|
||||
void _wait0(const atomic_int *ctid) {
|
||||
int x;
|
||||
struct timespec ts = {0, 1};
|
||||
while ((x = atomic_load_explicit(ctid, memory_order_acquire))) {
|
||||
if (IsLinux() || IsOpenbsd() || IsWindows()) {
|
||||
_wait0_futex(ctid, x);
|
||||
} else {
|
||||
_wait0_poll(&ts);
|
||||
}
|
||||
}
|
||||
if (IsOpenbsd()) {
|
||||
sched_yield(); // TODO(jart): whhhy?
|
||||
while ((x = atomic_load_explicit(ctid, memory_order_relaxed))) {
|
||||
nsync_futex_wait_((int *)ctid, x, !IsWindows(), 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1714,7 +1714,7 @@ OnError:
|
|||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
ShowCrashReports();
|
||||
// ShowCrashReports();
|
||||
|
||||
// we don't have proper futexes on these platforms
|
||||
// so choose a smaller number of workers
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#include "libc/testlib/subprocess.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
|
|
|
@ -16,13 +16,14 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sched_param.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/nexgen32e.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
@ -48,7 +49,6 @@ void SetUp(void) {
|
|||
|
||||
void TriggerSignal(void) {
|
||||
sched_yield();
|
||||
/* kprintf("raising at %p\n", __builtin_frame_address(0)); */
|
||||
raise(SIGUSR1);
|
||||
sched_yield();
|
||||
}
|
||||
|
@ -183,19 +183,90 @@ TEST(pthread_detach, testCustomStack_withReallySmallSize) {
|
|||
free(stk);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
void *JoinMainWorker(void *arg) {
|
||||
void *rc;
|
||||
pthread_t main_thread = (pthread_t)arg;
|
||||
_gc(malloc(32));
|
||||
_gc(malloc(32));
|
||||
ASSERT_EQ(0, pthread_join(main_thread, &rc));
|
||||
ASSERT_EQ(123, (intptr_t)rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(pthread_join, mainThread) {
|
||||
pthread_t id;
|
||||
_gc(malloc(32));
|
||||
_gc(malloc(32));
|
||||
SPAWN(fork);
|
||||
ASSERT_EQ(0, pthread_create(&id, 0, JoinMainWorker, (void *)pthread_self()));
|
||||
pthread_exit((void *)123);
|
||||
EXITS(0);
|
||||
}
|
||||
|
||||
TEST(pthread_join, mainThreadDelayed) {
|
||||
pthread_t id;
|
||||
_gc(malloc(32));
|
||||
_gc(malloc(32));
|
||||
SPAWN(fork);
|
||||
ASSERT_EQ(0, pthread_create(&id, 0, JoinMainWorker, (void *)pthread_self()));
|
||||
usleep(10000);
|
||||
pthread_exit((void *)123);
|
||||
EXITS(0);
|
||||
}
|
||||
|
||||
TEST(pthread_exit, fromMainThread_whenNoThreadsWereCreated) {
|
||||
SPAWN(fork);
|
||||
pthread_exit((void *)123);
|
||||
EXITS(0);
|
||||
}
|
||||
|
||||
atomic_bool g_cleanup1;
|
||||
atomic_bool g_cleanup2;
|
||||
|
||||
void OnCleanup(void *arg) {
|
||||
*(atomic_bool *)arg = true;
|
||||
}
|
||||
|
||||
void *CleanupExit(void *arg) {
|
||||
pthread_cleanup_push(OnCleanup, &g_cleanup1);
|
||||
pthread_cleanup_push(OnCleanup, &g_cleanup2);
|
||||
pthread_cleanup_pop(false);
|
||||
pthread_exit(0);
|
||||
pthread_cleanup_pop(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(pthread_cleanup, pthread_exit_alwaysCallsCallback) {
|
||||
pthread_t id;
|
||||
g_cleanup1 = false;
|
||||
g_cleanup2 = false;
|
||||
ASSERT_EQ(0, pthread_create(&id, 0, CleanupExit, 0));
|
||||
ASSERT_EQ(0, pthread_join(id, 0));
|
||||
ASSERT_TRUE(g_cleanup1);
|
||||
ASSERT_FALSE(g_cleanup2);
|
||||
}
|
||||
|
||||
void *CleanupNormal(void *arg) {
|
||||
pthread_cleanup_push(OnCleanup, &g_cleanup1);
|
||||
pthread_cleanup_push(OnCleanup, &g_cleanup2);
|
||||
pthread_cleanup_pop(true);
|
||||
pthread_cleanup_pop(true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(pthread_cleanup, pthread_normal) {
|
||||
pthread_t id;
|
||||
g_cleanup1 = false;
|
||||
g_cleanup2 = false;
|
||||
ASSERT_EQ(0, pthread_create(&id, 0, CleanupNormal, 0));
|
||||
ASSERT_EQ(0, pthread_join(id, 0));
|
||||
ASSERT_TRUE(g_cleanup1);
|
||||
ASSERT_TRUE(g_cleanup2);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// BENCHMARKS
|
||||
|
||||
static void CreateJoin(void) {
|
||||
pthread_t id;
|
||||
ASSERT_EQ(0, pthread_create(&id, 0, Increment, 0));
|
||||
|
|
Loading…
Add table
Reference in a new issue