mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
Improve multithreading
This commit is contained in:
parent
d3167126aa
commit
30afd6ddbb
38 changed files with 752 additions and 174 deletions
|
@ -396,9 +396,7 @@ static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
||||||
textwindows int __sig_kill(struct PosixThread *pt, int sig, int sic) {
|
textwindows int __sig_kill(struct PosixThread *pt, int sig, int sic) {
|
||||||
int rc;
|
int rc;
|
||||||
BLOCK_SIGNALS;
|
BLOCK_SIGNALS;
|
||||||
_pthread_ref(pt);
|
|
||||||
rc = __sig_killer(pt, sig, sic);
|
rc = __sig_killer(pt, sig, sic);
|
||||||
_pthread_unref(pt);
|
|
||||||
ALLOW_SIGNALS;
|
ALLOW_SIGNALS;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,7 +146,7 @@ int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) {
|
||||||
} else {
|
} else {
|
||||||
rc = sigaltstack_cosmo(neu, old);
|
rc = sigaltstack_cosmo(neu, old);
|
||||||
}
|
}
|
||||||
STRACE("sigaltstack(%s, [%s]) → %d% m", DescribeSigaltstk(0, neu),
|
STRACE("sigaltstack(%s, [%s]) → %d% m", DescribeSigaltstack(0, neu),
|
||||||
DescribeSigaltstk(0, old), rc);
|
DescribeSigaltstack(0, old), rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
#include "libc/mem/alloca.h"
|
#include "libc/mem/alloca.h"
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
const char *DescribeSigaltstk(char[128], int, const struct sigaltstack *);
|
const char *DescribeSigaltstack(char[128], int, const struct sigaltstack *);
|
||||||
#define DescribeSigaltstk(rc, ss) DescribeSigaltstk(alloca(128), rc, ss)
|
#define DescribeSigaltstack(rc, ss) DescribeSigaltstack(alloca(128), rc, ss)
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_SIGALTSTACK_INTERNAL_H_ */
|
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_SIGALTSTACK_INTERNAL_H_ */
|
||||||
|
|
|
@ -27,6 +27,7 @@ const char *DescribeItimer(char[12], int) libcesque;
|
||||||
const char *DescribeMapFlags(char[64], int) libcesque;
|
const char *DescribeMapFlags(char[64], int) libcesque;
|
||||||
const char *DescribeMapping(char[8], int, int) libcesque;
|
const char *DescribeMapping(char[8], int, int) libcesque;
|
||||||
const char *DescribeMremapFlags(char[30], int) libcesque;
|
const char *DescribeMremapFlags(char[30], int) libcesque;
|
||||||
|
const char *DescribeMsyncFlags(char[48], int) libcesque;
|
||||||
const char *DescribeNtConsoleInFlags(char[256], uint32_t) libcesque;
|
const char *DescribeNtConsoleInFlags(char[256], uint32_t) libcesque;
|
||||||
const char *DescribeNtConsoleOutFlags(char[128], uint32_t) libcesque;
|
const char *DescribeNtConsoleOutFlags(char[128], uint32_t) libcesque;
|
||||||
const char *DescribeNtCreationDisposition(uint32_t) libcesque;
|
const char *DescribeNtCreationDisposition(uint32_t) libcesque;
|
||||||
|
@ -54,6 +55,7 @@ const char *DescribeRlimitName(char[20], int) libcesque;
|
||||||
const char *DescribeSchedPolicy(char[48], int) libcesque;
|
const char *DescribeSchedPolicy(char[48], int) libcesque;
|
||||||
const char *DescribeSeccompOperation(int) libcesque;
|
const char *DescribeSeccompOperation(int) libcesque;
|
||||||
const char *DescribeSiCode(char[20], int, int) libcesque;
|
const char *DescribeSiCode(char[20], int, int) libcesque;
|
||||||
|
const char *DescribeSigaltstackFlags(char[22], int) libcesque;
|
||||||
const char *DescribeSleepFlags(char[16], int) libcesque;
|
const char *DescribeSleepFlags(char[16], int) libcesque;
|
||||||
const char *DescribeSockLevel(char[12], int) libcesque;
|
const char *DescribeSockLevel(char[12], int) libcesque;
|
||||||
const char *DescribeSockOptname(char[32], int, int) libcesque;
|
const char *DescribeSockOptname(char[32], int, int) libcesque;
|
||||||
|
@ -82,6 +84,7 @@ const char *DescribeWhichPrio(char[12], int) libcesque;
|
||||||
#define DescribeMapFlags(x) DescribeMapFlags(alloca(64), x)
|
#define DescribeMapFlags(x) DescribeMapFlags(alloca(64), x)
|
||||||
#define DescribeMapping(x, y) DescribeMapping(alloca(8), x, y)
|
#define DescribeMapping(x, y) DescribeMapping(alloca(8), x, y)
|
||||||
#define DescribeMremapFlags(x) DescribeMremapFlags(alloca(30), x)
|
#define DescribeMremapFlags(x) DescribeMremapFlags(alloca(30), x)
|
||||||
|
#define DescribeMsyncFlags(x) DescribeMsyncFlags(alloca(48), x)
|
||||||
#define DescribeNtConsoleInFlags(x) DescribeNtConsoleInFlags(alloca(256), x)
|
#define DescribeNtConsoleInFlags(x) DescribeNtConsoleInFlags(alloca(256), x)
|
||||||
#define DescribeNtConsoleOutFlags(x) DescribeNtConsoleOutFlags(alloca(128), x)
|
#define DescribeNtConsoleOutFlags(x) DescribeNtConsoleOutFlags(alloca(128), x)
|
||||||
#define DescribeNtFileAccessFlags(x) DescribeNtFileAccessFlags(alloca(512), x)
|
#define DescribeNtFileAccessFlags(x) DescribeNtFileAccessFlags(alloca(512), x)
|
||||||
|
@ -107,6 +110,7 @@ const char *DescribeWhichPrio(char[12], int) libcesque;
|
||||||
#define DescribeRlimitName(rl) DescribeRlimitName(alloca(20), rl)
|
#define DescribeRlimitName(rl) DescribeRlimitName(alloca(20), rl)
|
||||||
#define DescribeSchedPolicy(x) DescribeSchedPolicy(alloca(48), x)
|
#define DescribeSchedPolicy(x) DescribeSchedPolicy(alloca(48), x)
|
||||||
#define DescribeSiCode(x, y) DescribeSiCode(alloca(20), x, y)
|
#define DescribeSiCode(x, y) DescribeSiCode(alloca(20), x, y)
|
||||||
|
#define DescribeSigaltstackFlags(x) DescribeSigaltstackFlags(alloca(22), x)
|
||||||
#define DescribeSleepFlags(x) DescribeSleepFlags(alloca(16), x)
|
#define DescribeSleepFlags(x) DescribeSleepFlags(alloca(16), x)
|
||||||
#define DescribeSockLevel(x) DescribeSockLevel(alloca(12), x)
|
#define DescribeSockLevel(x) DescribeSockLevel(alloca(12), x)
|
||||||
#define DescribeSockOptname(x, y) DescribeSockOptname(alloca(32), x, y)
|
#define DescribeSockOptname(x, y) DescribeSockOptname(alloca(32), x, y)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
│ │
|
│ │
|
||||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
│ any purpose with or without fee is hereby granted, provided that the │
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
@ -16,20 +16,15 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
#include "libc/intrin/describeflags.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/intrin/weaken.h"
|
#include "libc/sysv/consts/msync.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
|
||||||
|
|
||||||
static bool _pthread_deref(struct PosixThread *pt) {
|
const char *(DescribeMsyncFlags)(char buf[48], int x) {
|
||||||
int refs = atomic_load_explicit(&pt->pt_refs, memory_order_acquire);
|
const struct DescribeFlags kMsyncFlags[] = {
|
||||||
return !refs || !atomic_fetch_sub(&pt->pt_refs, 1);
|
{MS_SYNC, "SYNC"}, //
|
||||||
}
|
{MS_ASYNC, "ASYNC"}, //
|
||||||
|
{MS_INVALIDATE, "INVALIDATE"}, //
|
||||||
void _pthread_unref(struct PosixThread *pt) {
|
};
|
||||||
if (_pthread_deref(pt)) {
|
return DescribeFlags(buf, 48, kMsyncFlags, ARRAYLEN(kMsyncFlags), "MS_", x);
|
||||||
unassert(_weaken(_pthread_free));
|
|
||||||
_weaken(_pthread_free)(pt, false);
|
|
||||||
_weaken(_pthread_decimate)();
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -21,8 +21,8 @@
|
||||||
#include "libc/intrin/describeflags.h"
|
#include "libc/intrin/describeflags.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
|
|
||||||
const char *(DescribeSigaltstk)(char buf[128], int rc,
|
const char *(DescribeSigaltstack)(char buf[128], int rc,
|
||||||
const struct sigaltstack *ss) {
|
const struct sigaltstack *ss) {
|
||||||
if (rc == -1)
|
if (rc == -1)
|
||||||
return "n/a";
|
return "n/a";
|
||||||
if (!ss)
|
if (!ss)
|
||||||
|
@ -30,8 +30,8 @@ const char *(DescribeSigaltstk)(char buf[128], int rc,
|
||||||
if (kisdangerous(ss)) {
|
if (kisdangerous(ss)) {
|
||||||
ksnprintf(buf, 128, "%p", ss);
|
ksnprintf(buf, 128, "%p", ss);
|
||||||
} else {
|
} else {
|
||||||
ksnprintf(buf, 128, "{.ss_sp=%p, .ss_flags=%#lx, .ss_size=%'zu}", ss->ss_sp,
|
ksnprintf(buf, 128, "{.ss_sp=%p, .ss_flags=%s, .ss_size=%'zu}", ss->ss_sp,
|
||||||
ss->ss_flags, ss->ss_size);
|
DescribeSigaltstackFlags(ss->ss_flags), ss->ss_size);
|
||||||
}
|
}
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
30
libc/intrin/describesigaltstackflags.c
Normal file
30
libc/intrin/describesigaltstackflags.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 et 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/intrin/describeflags.h"
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
|
#include "libc/sysv/consts/ss.h"
|
||||||
|
|
||||||
|
const char *(DescribeSigaltstackFlags)(char buf[22], int x) {
|
||||||
|
const struct DescribeFlags kSigaltstackFlags[] = {
|
||||||
|
{SS_ONSTACK, "ONSTACK"}, //
|
||||||
|
{SS_DISABLE, "DISABLE"}, //
|
||||||
|
};
|
||||||
|
return DescribeFlags(buf, 48, kSigaltstackFlags, ARRAYLEN(kSigaltstackFlags),
|
||||||
|
"SS_", x);
|
||||||
|
}
|
|
@ -91,12 +91,21 @@ privileged bool __maps_lock(void) {
|
||||||
tib = __get_tls_privileged();
|
tib = __get_tls_privileged();
|
||||||
if (atomic_fetch_add_explicit(&tib->tib_relock_maps, 1, memory_order_relaxed))
|
if (atomic_fetch_add_explicit(&tib->tib_relock_maps, 1, memory_order_relaxed))
|
||||||
return true;
|
return true;
|
||||||
|
int backoff = 0;
|
||||||
while (atomic_exchange_explicit(&__maps.lock, 1, memory_order_acquire)) {
|
while (atomic_exchange_explicit(&__maps.lock, 1, memory_order_acquire)) {
|
||||||
|
if (backoff < 7) {
|
||||||
|
volatile int i;
|
||||||
|
for (i = 0; i != 1 << backoff; i++) {
|
||||||
|
}
|
||||||
|
backoff++;
|
||||||
|
} else {
|
||||||
|
// STRACE("pthread_delay_np(__maps)");
|
||||||
#if defined(__GNUC__) && defined(__aarch64__)
|
#if defined(__GNUC__) && defined(__aarch64__)
|
||||||
__asm__ volatile("yield");
|
__asm__ volatile("yield");
|
||||||
#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
|
#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
|
||||||
__asm__ volatile("pause");
|
__asm__ volatile("pause");
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -834,7 +834,8 @@ void *mremap(void *old_addr, size_t old_size, size_t new_size, int flags, ...) {
|
||||||
*/
|
*/
|
||||||
int munmap(void *addr, size_t size) {
|
int munmap(void *addr, size_t size) {
|
||||||
int rc = __munmap(addr, size);
|
int rc = __munmap(addr, size);
|
||||||
STRACE("munmap(%p, %'zu) → %d% m", addr, size, rc);
|
STRACE("munmap(%p, %'zu) → %d% m (%'zu bytes total)", addr, size, rc,
|
||||||
|
__maps.pages * __pagesize);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "libc/calls/syscall-nt.internal.h"
|
#include "libc/calls/syscall-nt.internal.h"
|
||||||
#include "libc/calls/syscall-sysv.internal.h"
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
#include "libc/intrin/describeflags.h"
|
||||||
#include "libc/intrin/strace.h"
|
#include "libc/intrin/strace.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
@ -93,6 +94,7 @@ int msync(void *addr, size_t size, int flags) {
|
||||||
END_CANCELATION_POINT;
|
END_CANCELATION_POINT;
|
||||||
|
|
||||||
Finished:
|
Finished:
|
||||||
STRACE("msync(%p, %'zu, %#x) → %d% m", addr, size, flags, rc);
|
STRACE("msync(%p, %'zu, %s) → %d% m", addr, size, DescribeMsyncFlags(flags),
|
||||||
|
rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ void CheckForMemoryLeaks(void) {
|
||||||
|
|
||||||
// validate usage of this api
|
// validate usage of this api
|
||||||
if (_weaken(_pthread_decimate))
|
if (_weaken(_pthread_decimate))
|
||||||
_weaken(_pthread_decimate)();
|
_weaken(_pthread_decimate)(false);
|
||||||
if (!pthread_orphan_np())
|
if (!pthread_orphan_np())
|
||||||
kprintf("warning: called CheckForMemoryLeaks() from non-orphaned thread\n");
|
kprintf("warning: called CheckForMemoryLeaks() from non-orphaned thread\n");
|
||||||
|
|
||||||
|
|
|
@ -79,11 +79,22 @@ void __cxa_thread_finalize(void) {
|
||||||
struct Dtor *dtor;
|
struct Dtor *dtor;
|
||||||
struct CosmoTib *tib;
|
struct CosmoTib *tib;
|
||||||
tib = __get_tls();
|
tib = __get_tls();
|
||||||
|
|
||||||
|
// "Any cancellation cleanup handlers that have been pushed and not
|
||||||
|
// yet popped shall be popped in the reverse order that they were
|
||||||
|
// pushed and then executed." ──Quoth POSIX.1-2017
|
||||||
_pthread_unwind(tib);
|
_pthread_unwind(tib);
|
||||||
|
|
||||||
|
// "After all cancellation cleanup handlers have been executed, if the
|
||||||
|
// thread has any thread-specific data, appropriate destructor
|
||||||
|
// functions shall be called in an unspecified order."
|
||||||
|
// ──Quoth POSIX.1-2017
|
||||||
if (tib->tib_nsync)
|
if (tib->tib_nsync)
|
||||||
_weaken(nsync_waiter_destroy)(tib->tib_nsync);
|
_weaken(nsync_waiter_destroy)(tib->tib_nsync);
|
||||||
_pthread_unkey(tib);
|
_pthread_unkey(tib);
|
||||||
|
|
||||||
_pthread_ungarbage(tib);
|
_pthread_ungarbage(tib);
|
||||||
|
|
||||||
while ((dtor = tib->tib_atexit)) {
|
while ((dtor = tib->tib_atexit)) {
|
||||||
STRACE("__cxa_finalize(%t, %p)", dtor->fun, dtor->arg);
|
STRACE("__cxa_finalize(%t, %p)", dtor->fun, dtor->arg);
|
||||||
tib->tib_atexit = dtor->next;
|
tib->tib_atexit = dtor->next;
|
||||||
|
|
|
@ -151,23 +151,20 @@ int main(int argc, char *argv[]) {
|
||||||
a->teardown(0);
|
a->teardown(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_weaken(TearDownOnce)) {
|
if (_weaken(TearDownOnce))
|
||||||
_weaken(TearDownOnce)();
|
_weaken(TearDownOnce)();
|
||||||
}
|
|
||||||
|
|
||||||
// make sure threads are in a good state
|
// make sure threads are in a good state
|
||||||
if (_weaken(_pthread_decimate)) {
|
if (_weaken(_pthread_decimate))
|
||||||
_weaken(_pthread_decimate)();
|
_weaken(_pthread_decimate)(false);
|
||||||
}
|
|
||||||
if (_weaken(pthread_orphan_np) && !_weaken(pthread_orphan_np)()) {
|
if (_weaken(pthread_orphan_np) && !_weaken(pthread_orphan_np)()) {
|
||||||
tinyprint(2, "error: tests ended with threads still active\n", NULL);
|
tinyprint(2, "error: tests ended with threads still active\n", NULL);
|
||||||
_Exit(1);
|
_Exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for memory leaks
|
// check for memory leaks
|
||||||
if (!g_testlib_failed) {
|
if (!g_testlib_failed)
|
||||||
CheckForMemoryLeaks();
|
CheckForMemoryLeaks();
|
||||||
}
|
|
||||||
|
|
||||||
// we're done!
|
// we're done!
|
||||||
int status = MIN(255, g_testlib_failed);
|
int status = MIN(255, g_testlib_failed);
|
||||||
|
|
|
@ -74,7 +74,7 @@ struct PosixThread {
|
||||||
atomic_int pt_canceled; // 0x04: thread has bad beliefs
|
atomic_int pt_canceled; // 0x04: thread has bad beliefs
|
||||||
_Atomic(enum PosixThreadStatus) pt_status;
|
_Atomic(enum PosixThreadStatus) pt_status;
|
||||||
atomic_int ptid; // transitions 0 → tid
|
atomic_int ptid; // transitions 0 → tid
|
||||||
atomic_int pt_refs; // negative means free
|
atomic_int pt_refs; // prevents decimation
|
||||||
void *(*pt_start)(void *); // creation callback
|
void *(*pt_start)(void *); // creation callback
|
||||||
void *pt_arg; // start's parameter
|
void *pt_arg; // start's parameter
|
||||||
void *pt_rc; // start's return value
|
void *pt_rc; // start's return value
|
||||||
|
@ -103,14 +103,13 @@ int _pthread_setschedparam_freebsd(int, int, const struct sched_param *);
|
||||||
int _pthread_tid(struct PosixThread *) libcesque;
|
int _pthread_tid(struct PosixThread *) libcesque;
|
||||||
intptr_t _pthread_syshand(struct PosixThread *) libcesque;
|
intptr_t _pthread_syshand(struct PosixThread *) libcesque;
|
||||||
long _pthread_cancel_ack(void) libcesque;
|
long _pthread_cancel_ack(void) libcesque;
|
||||||
void _pthread_decimate(void) libcesque;
|
void _pthread_decimate(bool) libcesque;
|
||||||
void _pthread_free(struct PosixThread *, bool) libcesque;
|
void _pthread_free(struct PosixThread *) libcesque;
|
||||||
void _pthread_lock(void) libcesque;
|
void _pthread_lock(void) libcesque;
|
||||||
void _pthread_onfork_child(void) libcesque;
|
void _pthread_onfork_child(void) libcesque;
|
||||||
void _pthread_onfork_parent(void) libcesque;
|
void _pthread_onfork_parent(void) libcesque;
|
||||||
void _pthread_onfork_prepare(void) libcesque;
|
void _pthread_onfork_prepare(void) libcesque;
|
||||||
void _pthread_unlock(void) libcesque;
|
void _pthread_unlock(void) libcesque;
|
||||||
void _pthread_unref(struct PosixThread *) libcesque;
|
|
||||||
void _pthread_zombify(struct PosixThread *) libcesque;
|
void _pthread_zombify(struct PosixThread *) libcesque;
|
||||||
|
|
||||||
forceinline pureconst struct PosixThread *_pthread_self(void) {
|
forceinline pureconst struct PosixThread *_pthread_self(void) {
|
||||||
|
@ -118,7 +117,11 @@ forceinline pureconst struct PosixThread *_pthread_self(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
forceinline void _pthread_ref(struct PosixThread *pt) {
|
forceinline void _pthread_ref(struct PosixThread *pt) {
|
||||||
atomic_fetch_add_explicit(&pt->pt_refs, 1, memory_order_relaxed);
|
atomic_fetch_add_explicit(&pt->pt_refs, 1, memory_order_acq_rel);
|
||||||
|
}
|
||||||
|
|
||||||
|
forceinline void _pthread_unref(struct PosixThread *pt) {
|
||||||
|
atomic_fetch_sub_explicit(&pt->pt_refs, 1, memory_order_acq_rel);
|
||||||
}
|
}
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
|
|
|
@ -8,5 +8,7 @@
|
||||||
#define PT_MASKED 16
|
#define PT_MASKED 16
|
||||||
#define PT_INCANCEL 32
|
#define PT_INCANCEL 32
|
||||||
#define PT_OPENBSD_KLUDGE 64
|
#define PT_OPENBSD_KLUDGE 64
|
||||||
|
#define PT_EXITING 128
|
||||||
|
#define PT_OWNSIGALTSTACK 256
|
||||||
|
|
||||||
#endif /* COSMOPOLITAN_LIBC_THREAD_PT_H_ */
|
#endif /* COSMOPOLITAN_LIBC_THREAD_PT_H_ */
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns size of unmapped pages at bottom of stack.
|
* Returns size of protected region at bottom of thread stack.
|
||||||
*
|
*
|
||||||
* @param guardsize will be set to guard size in bytes
|
* @param guardsize will be set to guard size in bytes
|
||||||
* @return 0 on success, or errno on error
|
* @return 0 on success, or errno on error
|
||||||
|
|
34
libc/thread/pthread_attr_getsigaltstack_np.c
Normal file
34
libc/thread/pthread_attr_getsigaltstack_np.c
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ 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/runtime/stack.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns configuration for thread signal stack.
|
||||||
|
*
|
||||||
|
* @param stackaddr will be set to signal stack address
|
||||||
|
* @return 0 on success, or errno on error
|
||||||
|
* @see pthread_attr_setsigaltstacksize_np()
|
||||||
|
*/
|
||||||
|
errno_t pthread_attr_getsigaltstack_np(const pthread_attr_t *attr,
|
||||||
|
void **stackaddr, size_t *stacksize) {
|
||||||
|
*stackaddr = attr->__sigaltstackaddr;
|
||||||
|
*stacksize = attr->__sigaltstacksize;
|
||||||
|
return 0;
|
||||||
|
}
|
36
libc/thread/pthread_attr_getsigaltstacksize_np.c
Normal file
36
libc/thread/pthread_attr_getsigaltstacksize_np.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 et 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/runtime/stack.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns size of thread signal stack.
|
||||||
|
*
|
||||||
|
* This defaults to zero, which means that cosmo won't allocate a
|
||||||
|
* managed signal stack for newly created threads.
|
||||||
|
*
|
||||||
|
* @param x will be set to stack size in bytes
|
||||||
|
* @return 0 on success, or errno on error
|
||||||
|
* @see pthread_attr_setsigaltstacksize_np()
|
||||||
|
*/
|
||||||
|
errno_t pthread_attr_getsigaltstacksize_np(const pthread_attr_t *a,
|
||||||
|
size_t *stacksize) {
|
||||||
|
*stacksize = a->__sigaltstacksize;
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -19,18 +19,16 @@
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets size of unmapped pages at bottom of stack.
|
* Sets size of protected region at bottom of thread stack.
|
||||||
*
|
*
|
||||||
* This value will be rounded up to the host microprocessor page size,
|
* Cosmopolitan sets this value to `sysconf(_SC_PAGESIZE)` by default.
|
||||||
* which is usually 4096 or 16384. It's important to write code in such
|
|
||||||
* a way that that code can't skip over the guard area. GCC has warnings
|
|
||||||
* like `-Wframe-larger-than=4096 -Walloca-larger-than=4096` which help
|
|
||||||
* guarantee your code is safe in this regard. It should be assumed the
|
|
||||||
* guard pages exist beneath the stack pointer, rather than the bottom
|
|
||||||
* of the stack, since guard pages are also used to grow down commit,
|
|
||||||
* which can be poked using CheckLargeStackAllocation().
|
|
||||||
*
|
*
|
||||||
* @param guardsize contains guard size in bytes
|
* You may set `guardsize` to disable the stack guard feature and gain a
|
||||||
|
* slight performance advantage by avoiding mprotect() calls. Note that
|
||||||
|
* it could make your code more prone to silent unreported corruption.
|
||||||
|
*
|
||||||
|
* @param guardsize contains guard size in bytes, which is implicitly
|
||||||
|
* rounded up to `sysconf(_SC_PAGESIZE)`, or zero to disable
|
||||||
* @return 0 on success, or errno on error
|
* @return 0 on success, or errno on error
|
||||||
*/
|
*/
|
||||||
errno_t pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) {
|
errno_t pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) {
|
||||||
|
|
42
libc/thread/pthread_attr_setsigaltstack_np.c
Normal file
42
libc/thread/pthread_attr_setsigaltstack_np.c
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/dce.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines user-owned signal stack for thread.
|
||||||
|
*/
|
||||||
|
errno_t pthread_attr_setsigaltstack_np(pthread_attr_t *attr, void *stackaddr,
|
||||||
|
size_t stacksize) {
|
||||||
|
if (!stackaddr) {
|
||||||
|
attr->__sigaltstackaddr = 0;
|
||||||
|
attr->__sigaltstacksize = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (stacksize > INT_MAX)
|
||||||
|
return EINVAL;
|
||||||
|
if (stacksize < __get_minsigstksz())
|
||||||
|
return EINVAL;
|
||||||
|
attr->__sigaltstackaddr = stackaddr;
|
||||||
|
attr->__sigaltstacksize = stacksize;
|
||||||
|
return 0;
|
||||||
|
}
|
78
libc/thread/pthread_attr_setsigaltstacksize_np.c
Normal file
78
libc/thread/pthread_attr_setsigaltstacksize_np.c
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ 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/errno.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines size of cosmo-owned signal stack for thread.
|
||||||
|
*
|
||||||
|
* The sigaltstack() function is useful for writing robust programs that
|
||||||
|
* can recover from the occasional thread having a stack overflow rather
|
||||||
|
* than having the entire process crash. To use it normally, sigaltstack
|
||||||
|
* needs to be called at the start of each thread with a unique piece of
|
||||||
|
* memory. However this is challenging to do *correctly* without support
|
||||||
|
* from the POSIX threads runtime, since canceled or crashed threads may
|
||||||
|
* need to execute on the signal stack during pthread_exit() which would
|
||||||
|
* prevent a thread-local storage key destructor from free()'ing it.
|
||||||
|
*
|
||||||
|
* By default pthread_create() will not install a sigaltstack() on newly
|
||||||
|
* created threads. If this function is called, on the attributes object
|
||||||
|
* that gets passed to pthread_create(), then it'll use malloc() to make
|
||||||
|
* a stack for the thread using the size you specify here. The threading
|
||||||
|
* runtime will also free that memory safely after complete termination.
|
||||||
|
*
|
||||||
|
* pthread_t id;
|
||||||
|
* pthread_attr_t attr;
|
||||||
|
* pthread_attr_init(&attr);
|
||||||
|
* pthread_attr_setguardsize(&attr, getpagesize());
|
||||||
|
* pthread_attr_setsigaltstacksize_np(&attr, stacksize);
|
||||||
|
* pthread_create(&id, &attr, func, 0);
|
||||||
|
* pthread_attr_destroy(&attr);
|
||||||
|
* pthread_join(id, 0);
|
||||||
|
*
|
||||||
|
* Try using a size of `sysconf(_SC_SIGSTKSZ)`. If you want the smallest
|
||||||
|
* size possible, then `sysconf(_SC_MINSIGSTKSZ) + 2048` is probably the
|
||||||
|
* smallest value that can reasonably expected to work with pthread_exit
|
||||||
|
*
|
||||||
|
* struct sigaction sa;
|
||||||
|
* sigemptyset(&sa.sa_mask);
|
||||||
|
* sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||||
|
* sa.sa_sigaction = on_crash_signal;
|
||||||
|
* sigaction(SIGSEGV, &sa, 0);
|
||||||
|
*
|
||||||
|
* Please note that in order for this to work, your handlers for signals
|
||||||
|
* such as SIGSEGV and SIGBUS need to use SA_ONSTACK in your sa_flags.
|
||||||
|
*
|
||||||
|
* @param stacksize contains stack size in bytes, or 0 to disable
|
||||||
|
* @return 0 on success, or errno on error
|
||||||
|
* @raise EINVAL if `stacksize` is less than `sysconf(_SC_MINSIGSTKSZ)`
|
||||||
|
*/
|
||||||
|
errno_t pthread_attr_setsigaltstacksize_np(pthread_attr_t *a,
|
||||||
|
size_t stacksize) {
|
||||||
|
if (stacksize) {
|
||||||
|
if (stacksize > INT_MAX)
|
||||||
|
return EINVAL;
|
||||||
|
if (stacksize < __get_minsigstksz())
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
a->__sigaltstacksize = stacksize;
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,6 +72,8 @@ errno_t pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr,
|
||||||
attr->__stacksize = 0;
|
attr->__stacksize = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (stacksize > INT_MAX)
|
||||||
|
return EINVAL;
|
||||||
if (stacksize < PTHREAD_STACK_MIN)
|
if (stacksize < PTHREAD_STACK_MIN)
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
attr->__stackaddr = stackaddr;
|
attr->__stackaddr = stackaddr;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,6 +28,8 @@
|
||||||
* @raise EINVAL if `stacksize` is less than `PTHREAD_STACK_MIN`
|
* @raise EINVAL if `stacksize` is less than `PTHREAD_STACK_MIN`
|
||||||
*/
|
*/
|
||||||
errno_t pthread_attr_setstacksize(pthread_attr_t *a, size_t stacksize) {
|
errno_t pthread_attr_setstacksize(pthread_attr_t *a, size_t stacksize) {
|
||||||
|
if (stacksize > INT_MAX)
|
||||||
|
return EINVAL;
|
||||||
if (stacksize < PTHREAD_STACK_MIN)
|
if (stacksize < PTHREAD_STACK_MIN)
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
a->__stacksize = stacksize;
|
a->__stacksize = stacksize;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/struct/sigaltstack.h"
|
||||||
#include "libc/calls/struct/sigset.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
#include "libc/calls/struct/sigset.internal.h"
|
#include "libc/calls/struct/sigset.internal.h"
|
||||||
#include "libc/calls/syscall-sysv.internal.h"
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
|
@ -67,54 +68,104 @@ __static_yoink("_pthread_onfork_child");
|
||||||
#define MAP_ANON_OPENBSD 0x1000
|
#define MAP_ANON_OPENBSD 0x1000
|
||||||
#define MAP_STACK_OPENBSD 0x4000
|
#define MAP_STACK_OPENBSD 0x4000
|
||||||
|
|
||||||
void _pthread_free(struct PosixThread *pt, bool isfork) {
|
void _pthread_free(struct PosixThread *pt) {
|
||||||
|
|
||||||
|
// thread must be removed from _pthread_list before calling
|
||||||
unassert(dll_is_alone(&pt->list) && &pt->list != _pthread_list);
|
unassert(dll_is_alone(&pt->list) && &pt->list != _pthread_list);
|
||||||
|
|
||||||
|
// do nothing for the one and only magical statical posix thread
|
||||||
if (pt->pt_flags & PT_STATIC)
|
if (pt->pt_flags & PT_STATIC)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// unmap stack if the cosmo runtime was responsible for mapping it
|
||||||
if (pt->pt_flags & PT_OWNSTACK)
|
if (pt->pt_flags & PT_OWNSTACK)
|
||||||
unassert(!munmap(pt->pt_attr.__stackaddr, pt->pt_attr.__stacksize));
|
unassert(!munmap(pt->pt_attr.__stackaddr, pt->pt_attr.__stacksize));
|
||||||
if (!isfork) {
|
|
||||||
uint64_t syshand =
|
// free any additional upstream system resources
|
||||||
atomic_load_explicit(&pt->tib->tib_syshand, memory_order_acquire);
|
// our fork implementation wipes this handle in child automatically
|
||||||
if (syshand) {
|
uint64_t syshand =
|
||||||
if (IsWindows())
|
atomic_load_explicit(&pt->tib->tib_syshand, memory_order_acquire);
|
||||||
unassert(CloseHandle(syshand));
|
if (syshand) {
|
||||||
else if (IsXnuSilicon())
|
if (IsWindows())
|
||||||
__syslib->__pthread_join(syshand, 0);
|
unassert(CloseHandle(syshand)); // non-inheritable
|
||||||
}
|
else if (IsXnuSilicon())
|
||||||
|
unassert(!__syslib->__pthread_join(syshand, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// free heap memory associated with thread
|
||||||
|
if (pt->pt_flags & PT_OWNSIGALTSTACK)
|
||||||
|
free(pt->pt_attr.__sigaltstackaddr);
|
||||||
free(pt->pt_tls);
|
free(pt->pt_tls);
|
||||||
free(pt);
|
free(pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _pthread_decimate(void) {
|
void _pthread_decimate(bool annihilation_only) {
|
||||||
struct Dll *e;
|
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
|
struct Dll *e, *e2, *list = 0;
|
||||||
enum PosixThreadStatus status;
|
enum PosixThreadStatus status;
|
||||||
StartOver:
|
|
||||||
|
// acquire posix threads gil
|
||||||
_pthread_lock();
|
_pthread_lock();
|
||||||
for (e = dll_last(_pthread_list); e; e = dll_prev(_pthread_list, e)) {
|
|
||||||
|
// swiftly remove every single zombie
|
||||||
|
// that isn't being held by a killing thread
|
||||||
|
for (e = dll_last(_pthread_list); e; e = e2) {
|
||||||
|
e2 = dll_prev(_pthread_list, e);
|
||||||
pt = POSIXTHREAD_CONTAINER(e);
|
pt = POSIXTHREAD_CONTAINER(e);
|
||||||
|
if (atomic_load_explicit(&pt->pt_refs, memory_order_acquire) > 0)
|
||||||
|
continue; // pthread_kill() has a lease on this thread
|
||||||
status = atomic_load_explicit(&pt->pt_status, memory_order_acquire);
|
status = atomic_load_explicit(&pt->pt_status, memory_order_acquire);
|
||||||
if (status != kPosixThreadZombie)
|
if (status != kPosixThreadZombie)
|
||||||
break;
|
break; // zombies only exist at the end of the linked list
|
||||||
if (!atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire)) {
|
if (atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire))
|
||||||
dll_remove(&_pthread_list, e);
|
continue; // undead thread should that'll stop existing soon
|
||||||
_pthread_unlock();
|
dll_remove(&_pthread_list, e);
|
||||||
_pthread_unref(pt);
|
dll_make_first(&list, e);
|
||||||
goto StartOver;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// code like pthread_exit() needs to call this in order to know if
|
||||||
|
// it's appropriate to run exit() handlers however we really don't
|
||||||
|
// want to have a thread exiting block on a bunch of __maps locks!
|
||||||
|
// therefore we only take action if we'll destroy all but the self
|
||||||
|
if (annihilation_only)
|
||||||
|
if (!(_pthread_list == _pthread_list->prev &&
|
||||||
|
_pthread_list == _pthread_list->next)) {
|
||||||
|
dll_make_last(&_pthread_list, list);
|
||||||
|
list = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// release posix threads gil
|
||||||
_pthread_unlock();
|
_pthread_unlock();
|
||||||
|
|
||||||
|
// now free our thread local batch of zombies
|
||||||
|
// because death is a release and not a punishment
|
||||||
|
// this is advantaged by not holding locks over munmap
|
||||||
|
while ((e = dll_first(list))) {
|
||||||
|
pt = POSIXTHREAD_CONTAINER(e);
|
||||||
|
dll_remove(&list, e);
|
||||||
|
_pthread_free(pt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int PosixThread(void *arg, int tid) {
|
static int PosixThread(void *arg, int tid) {
|
||||||
void *rc;
|
void *rc;
|
||||||
struct PosixThread *pt = arg;
|
struct PosixThread *pt = arg;
|
||||||
|
|
||||||
|
// setup scheduling
|
||||||
if (pt->pt_attr.__inheritsched == PTHREAD_EXPLICIT_SCHED) {
|
if (pt->pt_attr.__inheritsched == PTHREAD_EXPLICIT_SCHED) {
|
||||||
unassert(_weaken(_pthread_reschedule));
|
unassert(_weaken(_pthread_reschedule));
|
||||||
_weaken(_pthread_reschedule)(pt); // yoinked by attribute builder
|
_weaken(_pthread_reschedule)(pt); // yoinked by attribute builder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setup signal stack
|
||||||
|
if (pt->pt_attr.__sigaltstacksize) {
|
||||||
|
struct sigaltstack ss;
|
||||||
|
ss.ss_sp = pt->pt_attr.__sigaltstackaddr;
|
||||||
|
ss.ss_size = pt->pt_attr.__sigaltstacksize;
|
||||||
|
ss.ss_flags = 0;
|
||||||
|
unassert(!sigaltstack(&ss, 0));
|
||||||
|
}
|
||||||
|
|
||||||
// set long jump handler so pthread_exit can bring control back here
|
// set long jump handler so pthread_exit can bring control back here
|
||||||
if (!setjmp(pt->pt_exiter)) {
|
if (!setjmp(pt->pt_exiter)) {
|
||||||
sigdelset(&pt->pt_attr.__sigmask, SIGTHR);
|
sigdelset(&pt->pt_attr.__sigmask, SIGTHR);
|
||||||
|
@ -130,41 +181,45 @@ static int PosixThread(void *arg, int tid) {
|
||||||
// calling pthread_exit() will either jump back here, or call exit
|
// calling pthread_exit() will either jump back here, or call exit
|
||||||
pthread_exit(rc);
|
pthread_exit(rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// avoid signal handler being triggered after we trash our own stack
|
// avoid signal handler being triggered after we trash our own stack
|
||||||
__sig_block();
|
__sig_block();
|
||||||
|
|
||||||
// return to clone polyfill which clears tid, wakes futex, and exits
|
// return to clone polyfill which clears tid, wakes futex, and exits
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int FixupCustomStackOnOpenbsd(pthread_attr_t *attr) {
|
static bool TellOpenbsdThisIsStackMemory(void *addr, size_t size) {
|
||||||
// OpenBSD: Only permits RSP to occupy memory that's been explicitly
|
return __sys_mmap(
|
||||||
// defined as stack memory. We need to squeeze the provided interval
|
addr, size, PROT_READ | PROT_WRITE,
|
||||||
// in order to successfully call mmap(), which will return EINVAL if
|
MAP_PRIVATE | MAP_FIXED | MAP_ANON_OPENBSD | MAP_STACK_OPENBSD, -1,
|
||||||
// these calculations should overflow.
|
0, 0) == addr;
|
||||||
size_t n;
|
}
|
||||||
uintptr_t x, y;
|
|
||||||
int e, rc, pagesz;
|
// OpenBSD only permits RSP to occupy memory that's been explicitly
|
||||||
pagesz = __pagesize;
|
// defined as stack memory, i.e. `lo <= %rsp < hi` must be the case
|
||||||
n = attr->__stacksize;
|
static errno_t FixupCustomStackOnOpenbsd(pthread_attr_t *attr) {
|
||||||
x = (uintptr_t)attr->__stackaddr;
|
|
||||||
y = ROUNDUP(x, pagesz);
|
// get interval
|
||||||
n -= y - x;
|
uintptr_t lo = (uintptr_t)attr->__stackaddr;
|
||||||
n = ROUNDDOWN(n, pagesz);
|
uintptr_t hi = lo + attr->__stacksize;
|
||||||
e = errno;
|
|
||||||
if (__sys_mmap((void *)y, n, PROT_READ | PROT_WRITE,
|
// squeeze interval
|
||||||
MAP_PRIVATE | MAP_FIXED | MAP_ANON_OPENBSD | MAP_STACK_OPENBSD,
|
lo = (lo + __pagesize - 1) & -__pagesize;
|
||||||
-1, 0, 0) == (void *)y) {
|
hi = hi & -__pagesize;
|
||||||
attr->__stackaddr = (void *)y;
|
|
||||||
attr->__stacksize = n;
|
// tell os it's stack memory
|
||||||
return 0;
|
errno_t olderr = errno;
|
||||||
} else {
|
if (!TellOpenbsdThisIsStackMemory((void *)lo, hi - lo)) {
|
||||||
rc = errno;
|
errno_t err = errno;
|
||||||
errno = e;
|
errno = olderr;
|
||||||
if (rc == EOVERFLOW) {
|
return err;
|
||||||
rc = EINVAL;
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update attributes with usable stack address
|
||||||
|
attr->__stackaddr = (void *)lo;
|
||||||
|
attr->__stacksize = hi - lo;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static errno_t pthread_create_impl(pthread_t *thread,
|
static errno_t pthread_create_impl(pthread_t *thread,
|
||||||
|
@ -204,7 +259,7 @@ static errno_t pthread_create_impl(pthread_t *thread,
|
||||||
// assume they know what they're doing as much as possible
|
// assume they know what they're doing as much as possible
|
||||||
if (IsOpenbsd()) {
|
if (IsOpenbsd()) {
|
||||||
if ((rc = FixupCustomStackOnOpenbsd(&pt->pt_attr))) {
|
if ((rc = FixupCustomStackOnOpenbsd(&pt->pt_attr))) {
|
||||||
_pthread_free(pt, false);
|
_pthread_free(pt);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,21 +269,17 @@ static errno_t pthread_create_impl(pthread_t *thread,
|
||||||
pt->pt_attr.__guardsize = ROUNDUP(pt->pt_attr.__guardsize, pagesize);
|
pt->pt_attr.__guardsize = ROUNDUP(pt->pt_attr.__guardsize, pagesize);
|
||||||
pt->pt_attr.__stacksize = pt->pt_attr.__stacksize;
|
pt->pt_attr.__stacksize = pt->pt_attr.__stacksize;
|
||||||
if (pt->pt_attr.__guardsize + pagesize > pt->pt_attr.__stacksize) {
|
if (pt->pt_attr.__guardsize + pagesize > pt->pt_attr.__stacksize) {
|
||||||
_pthread_free(pt, false);
|
_pthread_free(pt);
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
pt->pt_attr.__stackaddr =
|
pt->pt_attr.__stackaddr =
|
||||||
mmap(0, pt->pt_attr.__stacksize, PROT_READ | PROT_WRITE,
|
mmap(0, pt->pt_attr.__stacksize, PROT_READ | PROT_WRITE,
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
if (pt->pt_attr.__stackaddr != MAP_FAILED) {
|
if (pt->pt_attr.__stackaddr != MAP_FAILED) {
|
||||||
if (IsOpenbsd() &&
|
if (IsOpenbsd())
|
||||||
__sys_mmap(
|
if (!TellOpenbsdThisIsStackMemory(pt->pt_attr.__stackaddr,
|
||||||
pt->pt_attr.__stackaddr, pt->pt_attr.__stacksize,
|
pt->pt_attr.__stacksize))
|
||||||
PROT_READ | PROT_WRITE,
|
notpossible;
|
||||||
MAP_PRIVATE | MAP_FIXED | MAP_ANON_OPENBSD | MAP_STACK_OPENBSD,
|
|
||||||
-1, 0, 0) != pt->pt_attr.__stackaddr) {
|
|
||||||
notpossible;
|
|
||||||
}
|
|
||||||
if (pt->pt_attr.__guardsize)
|
if (pt->pt_attr.__guardsize)
|
||||||
if (mprotect(pt->pt_attr.__stackaddr, pt->pt_attr.__guardsize,
|
if (mprotect(pt->pt_attr.__stackaddr, pt->pt_attr.__guardsize,
|
||||||
PROT_NONE | PROT_GUARD))
|
PROT_NONE | PROT_GUARD))
|
||||||
|
@ -236,7 +287,7 @@ static errno_t pthread_create_impl(pthread_t *thread,
|
||||||
}
|
}
|
||||||
if (!pt->pt_attr.__stackaddr || pt->pt_attr.__stackaddr == MAP_FAILED) {
|
if (!pt->pt_attr.__stackaddr || pt->pt_attr.__stackaddr == MAP_FAILED) {
|
||||||
rc = errno;
|
rc = errno;
|
||||||
_pthread_free(pt, false);
|
_pthread_free(pt);
|
||||||
errno = e;
|
errno = e;
|
||||||
if (rc == EINVAL || rc == EOVERFLOW) {
|
if (rc == EINVAL || rc == EOVERFLOW) {
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
|
@ -247,6 +298,18 @@ static errno_t pthread_create_impl(pthread_t *thread,
|
||||||
pt->pt_flags |= PT_OWNSTACK;
|
pt->pt_flags |= PT_OWNSTACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setup signal stack
|
||||||
|
if (pt->pt_attr.__sigaltstacksize) {
|
||||||
|
if (!pt->pt_attr.__sigaltstackaddr) {
|
||||||
|
if (!(pt->pt_attr.__sigaltstackaddr =
|
||||||
|
malloc(pt->pt_attr.__sigaltstacksize))) {
|
||||||
|
_pthread_free(pt);
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
pt->pt_flags |= PT_OWNSIGALTSTACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set initial status
|
// set initial status
|
||||||
pt->tib->tib_pthread = (pthread_t)pt;
|
pt->tib->tib_pthread = (pthread_t)pt;
|
||||||
atomic_store_explicit(&pt->tib->tib_sigmask, -1, memory_order_relaxed);
|
atomic_store_explicit(&pt->tib->tib_sigmask, -1, memory_order_relaxed);
|
||||||
|
@ -264,7 +327,7 @@ static errno_t pthread_create_impl(pthread_t *thread,
|
||||||
memory_order_relaxed);
|
memory_order_relaxed);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_pthread_free(pt, false);
|
_pthread_free(pt);
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,7 +347,7 @@ static errno_t pthread_create_impl(pthread_t *thread,
|
||||||
_pthread_lock();
|
_pthread_lock();
|
||||||
dll_remove(&_pthread_list, &pt->list);
|
dll_remove(&_pthread_list, &pt->list);
|
||||||
_pthread_unlock();
|
_pthread_unlock();
|
||||||
_pthread_free(pt, false);
|
_pthread_free(pt);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,7 +416,7 @@ static const char *DescribeHandle(char buf[12], errno_t err, pthread_t *th) {
|
||||||
errno_t pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
errno_t pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||||
void *(*start_routine)(void *), void *arg) {
|
void *(*start_routine)(void *), void *arg) {
|
||||||
errno_t err;
|
errno_t err;
|
||||||
_pthread_decimate();
|
_pthread_decimate(false);
|
||||||
BLOCK_SIGNALS;
|
BLOCK_SIGNALS;
|
||||||
err = pthread_create_impl(thread, attr, start_routine, arg, _SigMask);
|
err = pthread_create_impl(thread, attr, start_routine, arg, _SigMask);
|
||||||
ALLOW_SIGNALS;
|
ALLOW_SIGNALS;
|
||||||
|
|
|
@ -32,6 +32,6 @@
|
||||||
* @return 0 on success, or errno on error
|
* @return 0 on success, or errno on error
|
||||||
*/
|
*/
|
||||||
int pthread_decimate_np(void) {
|
int pthread_decimate_np(void) {
|
||||||
_pthread_decimate();
|
_pthread_decimate(false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,10 +40,8 @@ static errno_t pthread_detach_impl(struct PosixThread *pt) {
|
||||||
if (atomic_compare_exchange_weak_explicit(&pt->pt_status, &status,
|
if (atomic_compare_exchange_weak_explicit(&pt->pt_status, &status,
|
||||||
transition, memory_order_release,
|
transition, memory_order_release,
|
||||||
memory_order_relaxed)) {
|
memory_order_relaxed)) {
|
||||||
if (transition == kPosixThreadZombie) {
|
if (transition == kPosixThreadZombie)
|
||||||
_pthread_zombify(pt);
|
_pthread_zombify(pt);
|
||||||
}
|
|
||||||
_pthread_decimate();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,23 +69,33 @@
|
||||||
* @noreturn
|
* @noreturn
|
||||||
*/
|
*/
|
||||||
wontreturn void pthread_exit(void *rc) {
|
wontreturn void pthread_exit(void *rc) {
|
||||||
|
int orphan;
|
||||||
struct CosmoTib *tib;
|
struct CosmoTib *tib;
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
enum PosixThreadStatus status, transition;
|
enum PosixThreadStatus status, transition;
|
||||||
|
|
||||||
|
STRACE("pthread_exit(%p)", rc);
|
||||||
|
|
||||||
|
// get posix thread object
|
||||||
tib = __get_tls();
|
tib = __get_tls();
|
||||||
pt = (struct PosixThread *)tib->tib_pthread;
|
pt = (struct PosixThread *)tib->tib_pthread;
|
||||||
pt->pt_flags |= PT_NOCANCEL;
|
|
||||||
pt->pt_rc = rc;
|
|
||||||
|
|
||||||
STRACE("pthread_exit(%p)", rc);
|
// "The behavior of pthread_exit() is undefined if called from a
|
||||||
|
// cancellation cleanup handler or destructor function that was
|
||||||
|
// invoked as a result of either an implicit or explicit call to
|
||||||
|
// pthread_exit()." ──Quoth POSIX.1-2017
|
||||||
|
unassert(!(pt->pt_flags & PT_EXITING));
|
||||||
|
|
||||||
|
// set state
|
||||||
|
pt->pt_flags |= PT_NOCANCEL | PT_EXITING;
|
||||||
|
pt->pt_rc = rc;
|
||||||
|
|
||||||
// free resources
|
// free resources
|
||||||
__cxa_thread_finalize();
|
__cxa_thread_finalize();
|
||||||
_pthread_decimate();
|
|
||||||
|
|
||||||
// run atexit handlers if orphaned thread
|
// run atexit handlers if orphaned thread
|
||||||
if (pthread_orphan_np())
|
_pthread_decimate(true);
|
||||||
|
if ((orphan = pthread_orphan_np()))
|
||||||
if (_weaken(__cxa_finalize))
|
if (_weaken(__cxa_finalize))
|
||||||
_weaken(__cxa_finalize)(NULL);
|
_weaken(__cxa_finalize)(NULL);
|
||||||
|
|
||||||
|
@ -113,8 +123,11 @@ wontreturn void pthread_exit(void *rc) {
|
||||||
if (transition == kPosixThreadZombie)
|
if (transition == kPosixThreadZombie)
|
||||||
_pthread_zombify(pt);
|
_pthread_zombify(pt);
|
||||||
|
|
||||||
// check if this is the last survivor
|
// "The process shall exit with an exit status of 0 after the last
|
||||||
if (pthread_orphan_np()) {
|
// thread has been terminated. The behavior shall be as if the
|
||||||
|
// implementation called exit() with a zero argument at thread
|
||||||
|
// termination time." ──Quoth POSIX.1-2017
|
||||||
|
if (orphan) {
|
||||||
for (const uintptr_t *p = __fini_array_end; p > __fini_array_start;)
|
for (const uintptr_t *p = __fini_array_end; p > __fini_array_start;)
|
||||||
((void (*)(void))(*--p))();
|
((void (*)(void))(*--p))();
|
||||||
_Exit(0);
|
_Exit(0);
|
||||||
|
|
|
@ -55,29 +55,38 @@ static const char *DescribeReturnValue(char buf[30], int err, void **value) {
|
||||||
*
|
*
|
||||||
* @return 0 on success, or errno on error
|
* @return 0 on success, or errno on error
|
||||||
* @raise ECANCELED if calling thread was cancelled in masked mode
|
* @raise ECANCELED if calling thread was cancelled in masked mode
|
||||||
|
* @raise EDEADLK if `ctid` refers calling thread's own ctid futex
|
||||||
* @raise EBUSY if `abstime` was specified and deadline expired
|
* @raise EBUSY if `abstime` was specified and deadline expired
|
||||||
* @cancelationpoint
|
* @cancelationpoint
|
||||||
*/
|
*/
|
||||||
static errno_t _pthread_wait(atomic_int *ctid, struct timespec *abstime) {
|
static errno_t _pthread_wait(atomic_int *ctid, struct timespec *abstime) {
|
||||||
int x, e, rc = 0;
|
int x, e;
|
||||||
unassert(ctid != &__get_tls()->tib_tid);
|
errno_t err = 0;
|
||||||
// "If the thread calling pthread_join() is canceled, then the target
|
if (ctid == &__get_tls()->tib_tid) {
|
||||||
// thread shall not be detached." ──Quoth POSIX.1-2017
|
// "If an implementation detects that the value specified by the
|
||||||
if (!(rc = pthread_testcancel_np())) {
|
// thread argument to pthread_join() refers to the calling thread,
|
||||||
BEGIN_CANCELATION_POINT;
|
// it is recommended that the function should fail and report an
|
||||||
while ((x = atomic_load_explicit(ctid, memory_order_acquire))) {
|
// [EDEADLK] error." ──Quoth POSIX.1-2017
|
||||||
e = nsync_futex_wait_(ctid, x, !IsWindows() && !IsXnu(), abstime);
|
err = EDEADLK;
|
||||||
if (e == -ECANCELED) {
|
} else {
|
||||||
rc = ECANCELED;
|
// "If the thread calling pthread_join() is canceled, then the target
|
||||||
break;
|
// thread shall not be detached." ──Quoth POSIX.1-2017
|
||||||
} else if (e == -ETIMEDOUT) {
|
if (!(err = pthread_testcancel_np())) {
|
||||||
rc = EBUSY;
|
BEGIN_CANCELATION_POINT;
|
||||||
break;
|
while ((x = atomic_load_explicit(ctid, memory_order_acquire))) {
|
||||||
|
e = nsync_futex_wait_(ctid, x, !IsWindows() && !IsXnu(), abstime);
|
||||||
|
if (e == -ECANCELED) {
|
||||||
|
err = ECANCELED;
|
||||||
|
break;
|
||||||
|
} else if (e == -ETIMEDOUT) {
|
||||||
|
err = EBUSY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
END_CANCELATION_POINT;
|
||||||
}
|
}
|
||||||
END_CANCELATION_POINT;
|
|
||||||
}
|
}
|
||||||
return rc;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -97,6 +106,7 @@ static errno_t _pthread_wait(atomic_int *ctid, struct timespec *abstime) {
|
||||||
* when we'll stop waiting; if this is null we will wait forever
|
* when we'll stop waiting; if this is null we will wait forever
|
||||||
* @return 0 on success, or errno on error
|
* @return 0 on success, or errno on error
|
||||||
* @raise ECANCELED if calling thread was cancelled in masked mode
|
* @raise ECANCELED if calling thread was cancelled in masked mode
|
||||||
|
* @raise EDEADLK if `thread` refers to calling thread
|
||||||
* @raise EBUSY if `abstime` deadline elapsed
|
* @raise EBUSY if `abstime` deadline elapsed
|
||||||
* @cancelationpoint
|
* @cancelationpoint
|
||||||
* @returnserrno
|
* @returnserrno
|
||||||
|
@ -104,26 +114,29 @@ static errno_t _pthread_wait(atomic_int *ctid, struct timespec *abstime) {
|
||||||
errno_t pthread_timedjoin_np(pthread_t thread, void **value_ptr,
|
errno_t pthread_timedjoin_np(pthread_t thread, void **value_ptr,
|
||||||
struct timespec *abstime) {
|
struct timespec *abstime) {
|
||||||
int tid;
|
int tid;
|
||||||
errno_t err;
|
errno_t err = 0;
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
enum PosixThreadStatus status;
|
enum PosixThreadStatus status;
|
||||||
pt = (struct PosixThread *)thread;
|
pt = (struct PosixThread *)thread;
|
||||||
tid = _pthread_tid(pt);
|
|
||||||
unassert(_pthread_tid(pt));
|
|
||||||
status = atomic_load_explicit(&pt->pt_status, memory_order_acquire);
|
|
||||||
// "The behavior is undefined if the value specified by the thread
|
// "The behavior is undefined if the value specified by the thread
|
||||||
// argument to pthread_join() does not refer to a joinable thread."
|
// argument to pthread_join() does not refer to a joinable thread."
|
||||||
// ──Quoth POSIX.1-2017
|
// ──Quoth POSIX.1-2017
|
||||||
|
unassert((tid = _pthread_tid(pt)));
|
||||||
|
status = atomic_load_explicit(&pt->pt_status, memory_order_acquire);
|
||||||
unassert(status == kPosixThreadJoinable || status == kPosixThreadTerminated);
|
unassert(status == kPosixThreadJoinable || status == kPosixThreadTerminated);
|
||||||
|
|
||||||
|
// "The results of multiple simultaneous calls to pthread_join()
|
||||||
|
// specifying the same target thread are undefined."
|
||||||
|
// ──Quoth POSIX.1-2017
|
||||||
if (!(err = _pthread_wait(&pt->tib->tib_tid, abstime))) {
|
if (!(err = _pthread_wait(&pt->tib->tib_tid, abstime))) {
|
||||||
_pthread_lock();
|
atomic_store_explicit(&pt->pt_status, kPosixThreadZombie,
|
||||||
dll_remove(&_pthread_list, &pt->list);
|
memory_order_release);
|
||||||
_pthread_unlock();
|
_pthread_zombify(pt);
|
||||||
if (value_ptr) {
|
if (value_ptr)
|
||||||
*value_ptr = pt->pt_rc;
|
*value_ptr = pt->pt_rc;
|
||||||
}
|
|
||||||
_pthread_unref(pt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
STRACE("pthread_timedjoin_np(%d, %s, %s) → %s", tid,
|
STRACE("pthread_timedjoin_np(%d, %s, %s) → %s", tid,
|
||||||
DescribeReturnValue(alloca(30), err, value_ptr),
|
DescribeReturnValue(alloca(30), err, value_ptr),
|
||||||
DescribeTimespec(err ? -1 : 0, abstime), DescribeErrno(err));
|
DescribeTimespec(err ? -1 : 0, abstime), DescribeErrno(err));
|
||||||
|
|
|
@ -94,9 +94,11 @@ typedef struct pthread_attr_s {
|
||||||
int __schedpolicy;
|
int __schedpolicy;
|
||||||
int __contentionscope;
|
int __contentionscope;
|
||||||
int __guardsize;
|
int __guardsize;
|
||||||
size_t __stacksize;
|
int __stacksize;
|
||||||
|
int __sigaltstacksize;
|
||||||
uint64_t __sigmask;
|
uint64_t __sigmask;
|
||||||
void *__stackaddr;
|
void *__stackaddr;
|
||||||
|
void *__sigaltstackaddr;
|
||||||
} pthread_attr_t;
|
} pthread_attr_t;
|
||||||
|
|
||||||
struct _pthread_cleanup_buffer {
|
struct _pthread_cleanup_buffer {
|
||||||
|
@ -117,6 +119,8 @@ int pthread_attr_getschedpolicy(const pthread_attr_t *, int *) libcesque paramsn
|
||||||
int pthread_attr_getscope(const pthread_attr_t *, int *) libcesque paramsnonnull();
|
int pthread_attr_getscope(const pthread_attr_t *, int *) libcesque paramsnonnull();
|
||||||
int pthread_attr_getstack(const pthread_attr_t *, void **, size_t *) libcesque paramsnonnull();
|
int pthread_attr_getstack(const pthread_attr_t *, void **, size_t *) libcesque paramsnonnull();
|
||||||
int pthread_attr_getstacksize(const pthread_attr_t *, size_t *) libcesque paramsnonnull();
|
int pthread_attr_getstacksize(const pthread_attr_t *, size_t *) libcesque paramsnonnull();
|
||||||
|
int pthread_attr_getsigaltstack_np(const pthread_attr_t *, void **, size_t *) libcesque paramsnonnull();
|
||||||
|
int pthread_attr_getsigaltstacksize_np(const pthread_attr_t *, size_t *) libcesque paramsnonnull();
|
||||||
int pthread_attr_init(pthread_attr_t *) libcesque paramsnonnull();
|
int pthread_attr_init(pthread_attr_t *) libcesque paramsnonnull();
|
||||||
int pthread_attr_setdetachstate(pthread_attr_t *, int) libcesque paramsnonnull();
|
int pthread_attr_setdetachstate(pthread_attr_t *, int) libcesque paramsnonnull();
|
||||||
int pthread_attr_setguardsize(pthread_attr_t *, size_t) libcesque paramsnonnull();
|
int pthread_attr_setguardsize(pthread_attr_t *, size_t) libcesque paramsnonnull();
|
||||||
|
@ -125,6 +129,8 @@ int pthread_attr_setschedpolicy(pthread_attr_t *, int) libcesque paramsnonnull()
|
||||||
int pthread_attr_setscope(pthread_attr_t *, int) libcesque paramsnonnull();
|
int pthread_attr_setscope(pthread_attr_t *, int) libcesque paramsnonnull();
|
||||||
int pthread_attr_setstack(pthread_attr_t *, void *, size_t) libcesque paramsnonnull((1));
|
int pthread_attr_setstack(pthread_attr_t *, void *, size_t) libcesque paramsnonnull((1));
|
||||||
int pthread_attr_setstacksize(pthread_attr_t *, size_t) libcesque paramsnonnull();
|
int pthread_attr_setstacksize(pthread_attr_t *, size_t) libcesque paramsnonnull();
|
||||||
|
int pthread_attr_setsigaltstack_np(pthread_attr_t *, void *, size_t) libcesque paramsnonnull((1));
|
||||||
|
int pthread_attr_setsigaltstacksize_np(pthread_attr_t *, size_t);
|
||||||
int pthread_barrier_destroy(pthread_barrier_t *) libcesque paramsnonnull();
|
int pthread_barrier_destroy(pthread_barrier_t *) libcesque paramsnonnull();
|
||||||
int pthread_barrier_init(pthread_barrier_t *, const pthread_barrierattr_t *, unsigned) libcesque paramsnonnull((1));
|
int pthread_barrier_init(pthread_barrier_t *, const pthread_barrierattr_t *, unsigned) libcesque paramsnonnull((1));
|
||||||
int pthread_barrier_wait(pthread_barrier_t *) libcesque paramsnonnull();
|
int pthread_barrier_wait(pthread_barrier_t *) libcesque paramsnonnull();
|
||||||
|
|
86
test/libc/calls/stackoverflow5_test.c
Normal file
86
test/libc/calls/stackoverflow5_test.c
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2023 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 <cosmo.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stack overflow recovery technique #5
|
||||||
|
* use the cosmo posix threads extensions
|
||||||
|
*/
|
||||||
|
|
||||||
|
sig_atomic_t smashed_stack;
|
||||||
|
|
||||||
|
void CrashHandler(int sig) {
|
||||||
|
smashed_stack = true;
|
||||||
|
pthread_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int StackOverflow(int f(), int n) {
|
||||||
|
if (n < INT_MAX) {
|
||||||
|
return f(f, n + 1) - 1;
|
||||||
|
} else {
|
||||||
|
return INT_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int (*pStackOverflow)(int (*)(), int) = StackOverflow;
|
||||||
|
|
||||||
|
void *MyPosixThread(void *arg) {
|
||||||
|
exit(pStackOverflow(pStackOverflow, 0));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
// choose the most dangerously small size possible
|
||||||
|
size_t sigstacksize = sysconf(_SC_MINSIGSTKSZ) + 2048;
|
||||||
|
|
||||||
|
// setup signal handler
|
||||||
|
struct sigaction sa;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_flags = SA_ONSTACK;
|
||||||
|
sa.sa_handler = CrashHandler;
|
||||||
|
if (sigaction(SIGBUS, &sa, 0))
|
||||||
|
return 1;
|
||||||
|
if (sigaction(SIGSEGV, &sa, 0))
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
// create thread with signal stack
|
||||||
|
pthread_t id;
|
||||||
|
pthread_attr_t attr;
|
||||||
|
if (pthread_attr_init(&attr))
|
||||||
|
return 3;
|
||||||
|
if (pthread_attr_setguardsize(&attr, getpagesize()))
|
||||||
|
return 4;
|
||||||
|
if (pthread_attr_setsigaltstacksize_np(&attr, sigstacksize))
|
||||||
|
return 5;
|
||||||
|
if (pthread_create(&id, &attr, MyPosixThread, 0))
|
||||||
|
return 6;
|
||||||
|
if (pthread_attr_destroy(&attr))
|
||||||
|
return 7;
|
||||||
|
if (pthread_join(id, 0))
|
||||||
|
return 8;
|
||||||
|
if (!smashed_stack)
|
||||||
|
return 9;
|
||||||
|
|
||||||
|
CheckForMemoryLeaks();
|
||||||
|
}
|
|
@ -50,12 +50,10 @@ TEST(cosmo_once, test) {
|
||||||
pthread_t th[N];
|
pthread_t th[N];
|
||||||
x = y = 0;
|
x = y = 0;
|
||||||
ASSERT_EQ(0, pthread_barrier_init(&b, 0, N));
|
ASSERT_EQ(0, pthread_barrier_init(&b, 0, N));
|
||||||
for (i = 0; i < N; ++i) {
|
for (i = 0; i < N; ++i)
|
||||||
ASSERT_EQ(0, pthread_create(th + i, 0, Worker, 0));
|
ASSERT_EQ(0, pthread_create(th + i, 0, Worker, 0));
|
||||||
}
|
for (i = 0; i < N; ++i)
|
||||||
for (i = 0; i < N; ++i) {
|
|
||||||
ASSERT_EQ(0, pthread_join(th[i], 0));
|
ASSERT_EQ(0, pthread_join(th[i], 0));
|
||||||
}
|
|
||||||
ASSERT_EQ(N, atomic_load(&x));
|
ASSERT_EQ(N, atomic_load(&x));
|
||||||
ASSERT_EQ(1, atomic_load(&y));
|
ASSERT_EQ(1, atomic_load(&y));
|
||||||
ASSERT_EQ(0, pthread_barrier_destroy(&b));
|
ASSERT_EQ(0, pthread_barrier_destroy(&b));
|
||||||
|
|
|
@ -528,18 +528,21 @@ TEST(mmap, sharedFileMapFork) {
|
||||||
|
|
||||||
int count;
|
int count;
|
||||||
void *ptrs[N];
|
void *ptrs[N];
|
||||||
|
size_t sizes[N];
|
||||||
|
|
||||||
void BenchMmapPrivate(void) {
|
void BenchMmapPrivate(void) {
|
||||||
void *p;
|
void *p;
|
||||||
p = mmap(0, gransz * 10, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE,
|
p = mmap(0, (sizes[count] = rand() % (pagesz * 500)), PROT_READ | PROT_WRITE,
|
||||||
-1, 0);
|
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||||
if (p == MAP_FAILED)
|
if (p == MAP_FAILED)
|
||||||
__builtin_trap();
|
__builtin_trap();
|
||||||
ptrs[count++] = p;
|
ptrs[count] = p;
|
||||||
|
++count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BenchUnmap(void) {
|
void BenchUnmap(void) {
|
||||||
if (munmap(ptrs[--count], gransz * 10))
|
--count;
|
||||||
|
if (munmap(ptrs[count], sizes[count]))
|
||||||
__builtin_trap();
|
__builtin_trap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,7 +560,7 @@ void BenchBigMunmap(void) {
|
||||||
__builtin_trap();
|
__builtin_trap();
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCH(mmap, bench) {
|
TEST(mmap, bench) {
|
||||||
EZBENCH2("mmap", donothing, BenchMmapPrivate());
|
EZBENCH2("mmap", donothing, BenchMmapPrivate());
|
||||||
EZBENCH2("munmap", donothing, BenchUnmap());
|
EZBENCH2("munmap", donothing, BenchUnmap());
|
||||||
// EZBENCH2("big mmap", donothing, BenchBigMmap());
|
// EZBENCH2("big mmap", donothing, BenchBigMmap());
|
||||||
|
|
40
test/libc/thread/pthread_cancel_deferred_cond_test.c
Normal file
40
test/libc/thread/pthread_cancel_deferred_cond_test.c
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int got_cleanup;
|
||||||
|
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
|
||||||
|
pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
void cleanup(void* arg) {
|
||||||
|
got_cleanup = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* worker(void* arg) {
|
||||||
|
pthread_cleanup_push(cleanup, 0);
|
||||||
|
if (pthread_mutex_lock(&mu))
|
||||||
|
_Exit(11);
|
||||||
|
pthread_cond_wait(&cv, &mu);
|
||||||
|
_Exit(12);
|
||||||
|
pthread_cleanup_pop(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
void* rc;
|
||||||
|
pthread_t th;
|
||||||
|
if (pthread_create(&th, 0, worker, 0))
|
||||||
|
return 2;
|
||||||
|
if (pthread_cancel(th))
|
||||||
|
return 3;
|
||||||
|
if (pthread_join(th, &rc))
|
||||||
|
return 4;
|
||||||
|
if (rc != PTHREAD_CANCELED)
|
||||||
|
return 5;
|
||||||
|
if (!got_cleanup)
|
||||||
|
return 6;
|
||||||
|
if (pthread_mutex_trylock(&mu) != EBUSY)
|
||||||
|
return 7;
|
||||||
|
if (pthread_mutex_unlock(&mu))
|
||||||
|
return 8;
|
||||||
|
}
|
43
test/libc/thread/pthread_cancel_masked_cond_test.c
Normal file
43
test/libc/thread/pthread_cancel_masked_cond_test.c
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int got_cleanup;
|
||||||
|
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
|
||||||
|
pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
void cleanup(void* arg) {
|
||||||
|
got_cleanup = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* worker(void* arg) {
|
||||||
|
if (pthread_setcancelstate(PTHREAD_CANCEL_MASKED, 0))
|
||||||
|
_Exit(10);
|
||||||
|
pthread_cleanup_push(cleanup, 0);
|
||||||
|
if (pthread_mutex_lock(&mu))
|
||||||
|
_Exit(11);
|
||||||
|
if (pthread_cond_wait(&cv, &mu) != ECANCELED)
|
||||||
|
_Exit(12);
|
||||||
|
if (pthread_mutex_trylock(&mu) != EBUSY)
|
||||||
|
_Exit(13);
|
||||||
|
if (pthread_mutex_unlock(&mu))
|
||||||
|
_Exit(14);
|
||||||
|
pthread_cleanup_pop(0);
|
||||||
|
return (void*)123;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
void* rc;
|
||||||
|
pthread_t th;
|
||||||
|
if (pthread_create(&th, 0, worker, 0))
|
||||||
|
return 2;
|
||||||
|
if (pthread_cancel(th))
|
||||||
|
return 3;
|
||||||
|
if (pthread_join(th, &rc))
|
||||||
|
return 4;
|
||||||
|
if (rc != (void*)123)
|
||||||
|
return 5;
|
||||||
|
if (got_cleanup)
|
||||||
|
return 6;
|
||||||
|
}
|
41
test/libc/thread/pthread_cancel_masked_read_test.c
Normal file
41
test/libc/thread/pthread_cancel_masked_read_test.c
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int pfds[2];
|
||||||
|
int got_cleanup;
|
||||||
|
|
||||||
|
void cleanup(void* arg) {
|
||||||
|
got_cleanup = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* worker(void* arg) {
|
||||||
|
char buf[8];
|
||||||
|
if (pthread_setcancelstate(PTHREAD_CANCEL_MASKED, 0))
|
||||||
|
_Exit(10);
|
||||||
|
pthread_cleanup_push(cleanup, 0);
|
||||||
|
if (read(pfds[0], buf, sizeof(buf)) != -1)
|
||||||
|
_Exit(11);
|
||||||
|
if (errno != ECANCELED)
|
||||||
|
_Exit(12);
|
||||||
|
pthread_cleanup_pop(0);
|
||||||
|
return (void*)123;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
void* rc;
|
||||||
|
pthread_t th;
|
||||||
|
if (pipe(pfds))
|
||||||
|
return 1;
|
||||||
|
if (pthread_create(&th, 0, worker, 0))
|
||||||
|
return 2;
|
||||||
|
if (pthread_cancel(th))
|
||||||
|
return 3;
|
||||||
|
if (pthread_join(th, &rc))
|
||||||
|
return 4;
|
||||||
|
if (rc != (void*)123)
|
||||||
|
return 5;
|
||||||
|
if (got_cleanup)
|
||||||
|
return 7;
|
||||||
|
}
|
30
test/libc/thread/pthread_create_inherit_mask_test.c
Normal file
30
test/libc/thread/pthread_create_inherit_mask_test.c
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#include <limits.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
sigset_t parent_mask;
|
||||||
|
sigset_t child_mask;
|
||||||
|
|
||||||
|
void* worker(void* arg) {
|
||||||
|
if (pthread_sigmask(SIG_SETMASK, 0, &child_mask))
|
||||||
|
_Exit(1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
pthread_t th;
|
||||||
|
sigemptyset(&parent_mask);
|
||||||
|
sigaddset(&parent_mask, SIGSYS);
|
||||||
|
sigaddset(&parent_mask, SIGUSR2);
|
||||||
|
sigaddset(&parent_mask, SIGWINCH);
|
||||||
|
if (pthread_sigmask(SIG_SETMASK, &parent_mask, 0))
|
||||||
|
return 1;
|
||||||
|
if (pthread_create(&th, 0, worker, 0))
|
||||||
|
return 2;
|
||||||
|
if (pthread_join(th, 0))
|
||||||
|
return 3;
|
||||||
|
for (int i = 1; i <= _NSIG; ++i)
|
||||||
|
if (sigismember(&parent_mask, i) != sigismember(&child_mask, i))
|
||||||
|
return 4;
|
||||||
|
}
|
|
@ -34,6 +34,7 @@
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/runtime/stack.h"
|
#include "libc/runtime/stack.h"
|
||||||
#include "libc/runtime/sysconf.h"
|
#include "libc/runtime/sysconf.h"
|
||||||
|
#include "libc/stdio/rand.h"
|
||||||
#include "libc/sysv/consts/prot.h"
|
#include "libc/sysv/consts/prot.h"
|
||||||
#include "libc/sysv/consts/sa.h"
|
#include "libc/sysv/consts/sa.h"
|
||||||
#include "libc/sysv/consts/sched.h"
|
#include "libc/sysv/consts/sched.h"
|
||||||
|
@ -279,11 +280,10 @@ static void CreateDetached(void) {
|
||||||
ASSERT_EQ(0, pthread_attr_destroy(&attr));
|
ASSERT_EQ(0, pthread_attr_destroy(&attr));
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCH(pthread_create, bench) {
|
TEST(pthread_create, bench) {
|
||||||
EZBENCH2("CreateJoin", donothing, CreateJoin());
|
EZBENCH2("CreateJoin", donothing, CreateJoin());
|
||||||
EZBENCH2("CreateDetach", donothing, CreateDetach());
|
EZBENCH2("CreateDetach", donothing, CreateDetach());
|
||||||
EZBENCH2("CreateDetached", donothing, CreateDetached());
|
EZBENCH2("CreateDetached", donothing, CreateDetached());
|
||||||
while (!pthread_orphan_np()) {
|
while (!pthread_orphan_np())
|
||||||
_pthread_decimate();
|
pthread_decimate_np();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,9 +50,8 @@ TEST(pthread_detach, testCreateReturn) {
|
||||||
pthread_t id;
|
pthread_t id;
|
||||||
ASSERT_EQ(0, pthread_create(&id, 0, Increment, 0));
|
ASSERT_EQ(0, pthread_create(&id, 0, Increment, 0));
|
||||||
ASSERT_EQ(0, pthread_detach(id));
|
ASSERT_EQ(0, pthread_detach(id));
|
||||||
while (!pthread_orphan_np()) {
|
while (!pthread_orphan_np())
|
||||||
_pthread_decimate();
|
pthread_decimate_np();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(pthread_detach, testDetachUponCreation) {
|
TEST(pthread_detach, testDetachUponCreation) {
|
||||||
|
@ -62,7 +61,6 @@ TEST(pthread_detach, testDetachUponCreation) {
|
||||||
ASSERT_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
|
ASSERT_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
|
||||||
ASSERT_EQ(0, pthread_create(&th, &attr, Increment, 0));
|
ASSERT_EQ(0, pthread_create(&th, &attr, Increment, 0));
|
||||||
ASSERT_EQ(0, pthread_attr_destroy(&attr));
|
ASSERT_EQ(0, pthread_attr_destroy(&attr));
|
||||||
while (!pthread_orphan_np()) {
|
while (!pthread_orphan_np())
|
||||||
_pthread_decimate();
|
pthread_decimate_np();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue