Make improvements to locking

This change makes pthread_mutex_lock() as fast as _spinlock() by
default. Thread instability issues on NetBSD have been resolved.
Improvements made to gdtoa thread code. Crash reporting will now
synchronize between threads in a slightly better way.
This commit is contained in:
Justine Tunney 2022-06-19 01:13:03 -07:00
parent 25041b8026
commit d5312b60f7
60 changed files with 890 additions and 629 deletions

View file

@ -16,9 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/struct/timespec.h"
#include "libc/str/str.h"
#include "libc/time/time.h"
/**

View file

@ -25,9 +25,11 @@ unsigned __sighandflags[NSIG];
static pthread_mutex_t __sig_lock_obj;
void(__sig_lock)(void) {
__sig_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_lock(&__sig_lock_obj);
}
void(__sig_unlock)(void) {
__sig_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_unlock(&__sig_lock_obj);
}

View file

@ -18,34 +18,44 @@
*/
#include "libc/assert.h"
#include "libc/bits/weaken.h"
#include "libc/calls/strace.internal.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/calls/calls.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/log/log.h"
#include "libc/intrin/lockcmpxchgp.h"
#include "libc/log/backtrace.internal.h"
#include "libc/log/internal.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.h"
/**
* Handles failure of assert() macro.
*/
relegated wontreturn void __assert_fail(const char *expr, const char *file,
int line) {
int rc;
static bool noreentry;
int me, owner;
static int sync;
--__strace;
--__ftrace;
kprintf("%s:%d: assert(%s) failed\n", file, line, expr);
if (_lockcmpxchg(&noreentry, false, true)) {
if (weaken(__die)) {
weaken(__die)();
owner = 0;
me = gettid();
kprintf("%s:%d: assert(%s) failed (tid %d)\n", file, line, expr, me);
if (_lockcmpxchgp(&sync, &owner, me)) {
__restore_tty();
if (weaken(ShowBacktrace)) {
weaken(ShowBacktrace)(2, __builtin_frame_address(0));
} else if (weaken(PrintBacktraceUsingSymbols) && weaken(GetSymbolTable)) {
weaken(PrintBacktraceUsingSymbols)(2, __builtin_frame_address(0),
weaken(GetSymbolTable)());
} else {
kprintf("can't backtrace b/c `__die` not linked\n");
kprintf("can't backtrace b/c `ShowCrashReports` not linked\n");
}
rc = 23;
__restorewintty();
_Exit(23);
} else if (owner == me) {
kprintf("assert failed while failing\n");
__restorewintty();
_Exit(24);
} else {
rc = 24;
_Exit1(25);
}
__restorewintty();
_Exit(rc);
}

View file

@ -31,7 +31,6 @@
* @noreturn
*/
privileged wontreturn void _Exit1(int rc) {
jmp_buf *jb;
struct WinThread *wt;
STRACE("_Exit1(%d)", rc);
if (!IsWindows() && !IsMetal()) {

View file

@ -30,10 +30,12 @@ struct Fds g_fds;
static pthread_mutex_t __fds_lock_obj;
void(__fds_lock)(void) {
__fds_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_lock(&__fds_lock_obj);
}
void(__fds_unlock)(void) {
__fds_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_unlock(&__fds_lock_obj);
}

View file

@ -22,7 +22,7 @@
STATIC_YOINK("_init__mmi");
struct MemoryIntervals _mmi;
static pthread_mutex_t __mmi_lock_obj;
pthread_mutex_t __mmi_lock_obj; // recursive :'(
void(__mmi_lock)(void) {
pthread_mutex_lock(&__mmi_lock_obj);

View file

@ -16,9 +16,11 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pthread.h"
#include "libc/macros.internal.h"
.init.start 200,_init__mmi
movb $OPEN_MAX,_mmi+8
movl $_mmi+24,_mmi+16
movb $PTHREAD_MUTEX_RECURSIVE,__mmi_lock_obj(%rip)
.init.end 200,_init__mmi

View file

@ -1,19 +1,21 @@
#ifndef COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_
#define COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_
#include "libc/bits/atomic.h"
#include "libc/calls/struct/timespec.h"
#include "libc/intrin/kprintf.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#include "libc/dce.h"
#define PTHREAD_ONCE_INIT 0
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
#define PTHREAD_MUTEX_RECURSIVE 0
#define PTHREAD_MUTEX_NORMAL 1
#define PTHREAD_MUTEX_NORMAL 0
#define PTHREAD_MUTEX_RECURSIVE 1
#define PTHREAD_MUTEX_ERRORCHECK 2
#define PTHREAD_MUTEX_STALLED 0
#define PTHREAD_MUTEX_ROBUST 1
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/* clang-format off */
#define PTHREAD_MUTEX_INITIALIZER {PTHREAD_MUTEX_DEFAULT}
#define PTHREAD_RWLOCK_INITIALIZER {{{0}}}
@ -66,8 +68,8 @@ typedef struct {
} __u;
} pthread_rwlock_t;
wontreturn void pthread_exit(void *);
pureconst pthread_t pthread_self(void);
void pthread_exit(void *) wontreturn;
pthread_t pthread_self(void) pureconst;
int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *),
void *);
int pthread_yield(void);
@ -104,6 +106,30 @@ int pthread_rwlock_trywrlock(pthread_rwlock_t *);
int pthread_rwlock_timedwrlock(pthread_rwlock_t *, const struct timespec *);
int pthread_rwlock_unlock(pthread_rwlock_t *);
#define pthread_mutexattr_init(pAttr) ((pAttr)->attr = PTHREAD_MUTEX_DEFAULT, 0)
#define pthread_mutexattr_destroy(pAttr) ((pAttr)->attr = 0)
#define pthread_mutexattr_gettype(pAttr, pType) (*(pType) = (pAttr)->attr, 0)
#define pthread_mutexattr_settype(pAttr, type) ((pAttr)->attr = type, 0)
#ifdef __GNUC__
#define pthread_mutex_lock(mutex) \
(((mutex)->attr == PTHREAD_MUTEX_NORMAL && \
!atomic_load_explicit(&(mutex)->lock, memory_order_relaxed) && \
!atomic_exchange(&(mutex)->lock, 1)) \
? 0 \
: pthread_mutex_lock(mutex))
#define pthread_mutex_unlock(mutex) \
((mutex)->attr == PTHREAD_MUTEX_NORMAL \
? (atomic_store_explicit(&(mutex)->lock, 0, memory_order_relaxed), \
(IsLinux() && \
atomic_load_explicit(&(mutex)->waits, memory_order_relaxed) && \
_pthread_mutex_wake(mutex)), \
0) \
: pthread_mutex_unlock(mutex))
#endif
int _pthread_mutex_wake(pthread_mutex_t *) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_ */

View file

@ -21,41 +21,68 @@
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/pthread.h"
#include "libc/intrin/spinlock.h"
#include "libc/linux/futex.h"
#include "libc/nexgen32e/threaded.h"
static int pthread_mutex_lock_spin(pthread_mutex_t *mutex, int tries) {
volatile int i;
if (tries < 7) {
for (i = 0; i != 1 << tries; i++) {
}
tries++;
} else if (IsLinux()) {
atomic_fetch_add(&mutex->waits, 1);
LinuxFutexWait(&mutex->lock, 1, 0);
atomic_fetch_sub(&mutex->waits, 1);
} else {
sched_yield();
}
return tries;
}
/**
* Locks mutex.
*
* _spinlock() l: 181,570c 58,646ns
* mutex normal l: 297,965c 96,241ns
* mutex recursive l: 1,112,166c 359,223ns
* mutex errorcheck l: 1,449,723c 468,252ns
*
* @return 0 on success, or error number on failure
*/
int pthread_mutex_lock(pthread_mutex_t *mutex) {
volatile int i;
int(pthread_mutex_lock)(pthread_mutex_t *mutex) {
int me, owner, tries;
for (tries = 0, me = gettid();;) {
owner = atomic_load_explicit(&mutex->lock, memory_order_relaxed);
if (!owner && atomic_compare_exchange_weak_explicit(
&mutex->lock, &owner, me, memory_order_acquire,
memory_order_relaxed)) {
break;
} else if (owner == me) {
if (mutex->attr != PTHREAD_MUTEX_ERRORCHECK) {
break;
} else {
return EDEADLK;
switch (mutex->attr) {
case PTHREAD_MUTEX_NORMAL:
for (tries = 0;;) {
if (!atomic_load_explicit(&mutex->lock, memory_order_relaxed) &&
!atomic_exchange_explicit(&mutex->lock, 1, memory_order_acquire)) {
break;
}
tries = pthread_mutex_lock_spin(mutex, tries);
}
}
if (tries < 7) {
for (i = 0; i != 1 << tries; i++) {
return 0;
case PTHREAD_MUTEX_RECURSIVE:
case PTHREAD_MUTEX_ERRORCHECK:
for (tries = 0, me = gettid();;) {
owner = atomic_load_explicit(&mutex->lock, memory_order_relaxed);
if (!owner && atomic_compare_exchange_weak_explicit(
&mutex->lock, &owner, me, memory_order_acquire,
memory_order_relaxed)) {
break;
} else if (owner == me) {
if (mutex->attr != PTHREAD_MUTEX_ERRORCHECK) {
break;
} else {
return EDEADLK;
}
}
tries = pthread_mutex_lock_spin(mutex, tries);
}
tries++;
} else if (IsLinux()) {
atomic_fetch_add(&mutex->waits, 1);
LinuxFutexWait(&mutex->lock, owner, 0);
atomic_fetch_sub(&mutex->waits, 1);
} else {
sched_yield();
}
++mutex->reent;
return 0;
default:
return EINVAL;
}
++mutex->reent;
return 0;
}

View file

@ -18,28 +18,34 @@
*/
#include "libc/bits/atomic.h"
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/pthread.h"
#include "libc/linux/futex.h"
#include "libc/nexgen32e/threaded.h"
/**
* Releases mutex.
* @return 0 on success or error number on failure
* @raises EPERM if in error check mode and not owned by caller
*/
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
int owner;
if (mutex->attr == PTHREAD_MUTEX_ERRORCHECK && mutex->lock != gettid()) {
return EPERM;
int(pthread_mutex_unlock)(pthread_mutex_t *mutex) {
int me, owner;
switch (mutex->attr) {
case PTHREAD_MUTEX_ERRORCHECK:
me = gettid();
owner = atomic_load_explicit(&mutex->lock, memory_order_relaxed);
if (owner != me) return EPERM;
// fallthrough
case PTHREAD_MUTEX_RECURSIVE:
if (--mutex->reent) return 0;
// fallthrough
case PTHREAD_MUTEX_NORMAL:
atomic_store_explicit(&mutex->lock, 0, memory_order_relaxed);
if (IsLinux() &&
atomic_load_explicit(&mutex->waits, memory_order_relaxed)) {
_pthread_mutex_wake(mutex);
}
return 0;
default:
return EINVAL;
}
if (!--mutex->reent) {
atomic_store_explicit(&mutex->lock, 0, memory_order_relaxed);
if (IsLinux() &&
atomic_load_explicit(&mutex->waits, memory_order_acquire)) {
LinuxFutexWake(&mutex->lock, 1);
}
}
return 0;
}

View file

@ -0,0 +1,26 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/atomic.h"
#include "libc/dce.h"
#include "libc/intrin/pthread.h"
#include "libc/linux/futex.h"
int _pthread_mutex_wake(pthread_mutex_t *mutex) {
return LinuxFutexWake(&mutex->lock, 1);
}

View file

@ -17,13 +17,12 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pthread.h"
#include "libc/str/str.h"
/**
* Destroys mutex attr.
* @return 0 on success, or error number on failure
*/
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) {
bzero(attr, sizeof(*attr));
int(pthread_mutexattr_destroy)(pthread_mutexattr_t *attr) {
attr->attr = 0;
return 0;
}

View file

@ -29,7 +29,7 @@
* - `PTHREAD_MUTEX_ERRORCHECK`
* @return 0 on success, or error on failure
*/
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) {
int(pthread_mutexattr_gettype)(const pthread_mutexattr_t *attr, int *type) {
*type = attr->attr;
return 0;
}

View file

@ -17,14 +17,12 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pthread.h"
#include "libc/str/str.h"
/**
* Initializes mutex attr.
* @return 0 on success, or error number on failure
*/
int pthread_mutexattr_init(pthread_mutexattr_t *attr) {
bzero(attr, sizeof(*attr));
int(pthread_mutexattr_init)(pthread_mutexattr_t *attr) {
attr->attr = PTHREAD_MUTEX_DEFAULT;
return 0;
}

View file

@ -30,7 +30,7 @@
* @return 0 on success, or error on failure
* @raises EINVAL if `type` is invalid
*/
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) {
int(pthread_mutexattr_settype)(pthread_mutexattr_t *attr, int type) {
switch (type) {
case PTHREAD_MUTEX_NORMAL:
case PTHREAD_MUTEX_RECURSIVE:

View file

@ -1,5 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_
#define COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § spinlocks
@ -11,7 +13,7 @@
#define _spinlock(lock) _spinlock_cooperative(lock)
#endif
#define _spunlock(lock) __atomic_store_n(lock, 0, __ATOMIC_RELAXED)
#define _spunlock(lock) (__atomic_store_n(lock, 0, __ATOMIC_RELAXED), 0)
#define _seizelock(lock, value) \
({ \
@ -69,6 +71,8 @@
#define _trylock(lock) __atomic_test_and_set(lock, __ATOMIC_SEQ_CST)
void _spinlock_yield(void);
int _spinlock_yield(void);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ */

View file

@ -103,12 +103,6 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
garbage = weaken(__garbage);
gi = garbage ? garbage->i : 0;
for (frame = bp; frame && i < kBacktraceMaxFrames - 1; frame = frame->next) {
__mmi_lock();
ok = IsValidStackFramePointer(frame);
__mmi_unlock();
if (!ok) {
return -1;
}
addr = frame->addr;
if (addr == weakaddr("__gc")) {
do {

View file

@ -57,13 +57,6 @@ noinstrument noasan int PrintBacktraceUsingSymbols(int fd,
garbage = weaken(__garbage);
gi = garbage ? garbage->i : 0;
for (i = 0, frame = bp; frame; frame = frame->next) {
__mmi_lock();
ok = IsValidStackFramePointer(frame);
__mmi_unlock();
if (!ok) {
kprintf("%p corrupt frame pointer\n", frame);
break;
}
if (++i == LIMIT) {
kprintf("<truncated backtrace>\n");
break;

View file

@ -16,8 +16,10 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/lockcmpxchgp.h"
#include "libc/log/backtrace.internal.h"
#include "libc/log/internal.h"
#include "libc/log/libfatal.internal.h"
@ -32,18 +34,30 @@
*/
relegated wontreturn void __die(void) {
/* asan runtime depends on this function */
int rc;
static bool once;
if (_lockcmpxchg(&once, false, true)) {
int me, owner;
static int sync;
owner = 0;
me = gettid();
if (_lockcmpxchgp(&sync, &owner, me)) {
__restore_tty();
if (IsDebuggerPresent(false)) {
DebugBreak();
}
ShowBacktrace(2, NULL);
rc = 77;
if (weaken(ShowBacktrace)) {
weaken(ShowBacktrace)(2, __builtin_frame_address(0));
} else if (weaken(PrintBacktraceUsingSymbols) && weaken(GetSymbolTable)) {
weaken(PrintBacktraceUsingSymbols)(2, __builtin_frame_address(0),
weaken(GetSymbolTable)());
} else {
kprintf("can't backtrace b/c `ShowCrashReports` not linked\n");
}
__restorewintty();
_Exit(77);
} else if (owner == me) {
kprintf("die failed while dying\n");
__restorewintty();
_Exit(78);
} else {
rc = 78;
_Exit1(79);
}
__restorewintty();
_Exit(rc);
}

View file

@ -26,6 +26,7 @@
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/intrin/lockcmpxchgp.h"
#include "libc/log/backtrace.internal.h"
#include "libc/log/gdb.h"
#include "libc/log/internal.h"
@ -262,15 +263,22 @@ static wontreturn relegated noinstrument void __minicrash(int sig,
* simply print addresses which may be cross-referenced using objdump.
*
* This function never returns, except for traps w/ human supervision.
*
* @threadsafe
* @vforksafe
*/
relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
intptr_t rip;
int me, owner;
int gdbpid, err;
static bool noreentry, notpossible;
static int sync;
static bool notpossible;
STRACE("__oncrash rip %x", ctx->uc_mcontext.rip);
--__ftrace;
--__strace;
if (_lockcmpxchg(&noreentry, false, true)) {
owner = 0;
me = gettid();
if (_lockcmpxchgp(&sync, &owner, me)) {
if (!__vforked) {
rip = ctx ? ctx->uc_mcontext.rip : 0;
err = errno;
@ -292,20 +300,30 @@ relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
__restorewintty();
_Exit(128 + sig);
}
sync = 0;
} else {
sync = 0;
__minicrash(sig, si, ctx, "WHILE VFORKED");
}
} else if (sig == SIGTRAP) {
/* chances are IsDebuggerPresent() confused strace w/ gdb */
// chances are IsDebuggerPresent() confused strace w/ gdb
goto ItsATrap;
} else if (_lockcmpxchg(&notpossible, false, true)) {
__minicrash(sig, si, ctx, "WHILE CRASHING");
} else {
for (;;) {
asm("ud2");
} else if (owner == me) {
// we crashed while generating a crash report
if (_lockcmpxchg(&notpossible, false, true)) {
__minicrash(sig, si, ctx, "WHILE CRASHING");
} else {
// somehow __minicrash() crashed not possible
for (;;) {
asm("ud2");
}
}
} else {
// multiple threads have crashed
// kill current thread assuming process dies soon
// TODO(jart): It'd be nice to report on all threads.
_Exit1(8);
}
noreentry = false;
ItsATrap:
++__strace;
++__ftrace;

View file

@ -30,9 +30,12 @@
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/ss.h"
STATIC_YOINK("__die"); /* for backtracing */
STATIC_YOINK("malloc_inspect_all"); /* for asan memory origin */
STATIC_YOINK("__get_symbol_by_addr"); /* for asan memory origin */
STATIC_YOINK("__die"); // for backtracing
STATIC_YOINK("ShowBacktrace"); // for backtracing
STATIC_YOINK("GetSymbolTable"); // for backtracing
STATIC_YOINK("PrintBacktraceUsingSymbols"); // for backtracing
STATIC_YOINK("malloc_inspect_all"); // for asan memory origin
STATIC_YOINK("__get_symbol_by_addr"); // for asan memory origin
extern const unsigned char __oncrash_thunks[8][11];
static struct sigaltstack g_oldsigaltstack;

View file

@ -355,16 +355,15 @@ static wontreturn void NetbsdThreadMain(void *arg, int (*func)(void *arg),
static int CloneNetbsd(int (*func)(void *), char *stk, size_t stksz, int flags,
void *arg, void *tls, size_t tlssz, int *ctid) {
// NetBSD has its own clone() and it works, but it's technically a
// second-class API, intended to help Linux folks migrate to this!
// We put it on the thread's stack, to avoid locking this function
// so its stack doesn't scope. The ucontext struct needs 784 bytes
// second-class API, intended to help Linux folks migrate to this.
bool failed;
int ax, *tid;
intptr_t dx, sp;
static bool once;
static int broken;
struct ucontext_netbsd ctx;
struct ucontext_netbsd *ctx;
static struct ucontext_netbsd netbsd_clone_template;
_Static_assert(sizeof(struct ucontext_netbsd) == 784, "fix assembly");
// memoize arbitrary valid processor state structure
if (!once) {
@ -382,35 +381,48 @@ static int CloneNetbsd(int (*func)(void *), char *stk, size_t stksz, int flags,
return -1;
}
sp = (intptr_t)(stk + stksz);
// allocate memory for child tid
sp -= sizeof(int);
sp = sp & -alignof(int);
tid = (int *)sp;
// align the stack
sp = sp & -16;
// simulate call to misalign stack and ensure backtrace looks good
sp -= 8;
*(intptr_t *)sp = (intptr_t)CloneNetbsd + 1;
// place the giant 784 byte ucontext structure in the red zone!
// it only has to live long enough for the thread to come alive
ctx = (struct ucontext_netbsd *)((sp - sizeof(struct ucontext_netbsd)) &
-alignof(struct ucontext_netbsd));
// pass parameters in process state
memcpy(&ctx, &netbsd_clone_template, sizeof(ctx));
ctx.uc_link = 0;
ctx.uc_mcontext.rbp = 0;
ctx.uc_mcontext.rsp = sp;
ctx.uc_mcontext.rip = (intptr_t)NetbsdThreadMain;
ctx.uc_mcontext.rdi = (intptr_t)arg;
ctx.uc_mcontext.rsi = (intptr_t)func;
ctx.uc_mcontext.rdx = (intptr_t)tid;
ctx.uc_mcontext.rcx = (intptr_t)(flags & CLONE_CHILD_SETTID ? ctid : tid);
ctx.uc_mcontext.r8 = (intptr_t)(flags & CLONE_CHILD_CLEARTID ? ctid : tid);
ctx.uc_flags |= _UC_STACK;
ctx.uc_stack.ss_sp = stk;
ctx.uc_stack.ss_size = stksz;
ctx.uc_stack.ss_flags = 0;
memcpy(ctx, &netbsd_clone_template, sizeof(*ctx));
ctx->uc_link = 0;
ctx->uc_mcontext.rbp = 0;
ctx->uc_mcontext.rsp = sp;
ctx->uc_mcontext.rip = (intptr_t)NetbsdThreadMain;
ctx->uc_mcontext.rdi = (intptr_t)arg;
ctx->uc_mcontext.rsi = (intptr_t)func;
ctx->uc_mcontext.rdx = (intptr_t)tid;
ctx->uc_mcontext.rcx = (intptr_t)(flags & CLONE_CHILD_SETTID ? ctid : tid);
ctx->uc_mcontext.r8 = (intptr_t)(flags & CLONE_CHILD_CLEARTID ? ctid : tid);
ctx->uc_flags |= _UC_STACK;
ctx->uc_stack.ss_sp = stk;
ctx->uc_stack.ss_size = stksz;
ctx->uc_stack.ss_flags = 0;
if (flags & CLONE_SETTLS) {
ctx.uc_flags |= _UC_TLSBASE;
ctx.uc_mcontext._mc_tlsbase = (intptr_t)tls;
ctx->uc_flags |= _UC_TLSBASE;
ctx->uc_mcontext._mc_tlsbase = (intptr_t)tls;
}
// perform the system call
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(failed), "=a"(ax), "=d"(dx)
: "1"(__NR__lwp_create), "D"(&ctx), "S"(LWP_DETACHED), "2"(tid)
: "1"(__NR__lwp_create), "D"(ctx), "S"(LWP_DETACHED), "2"(tid)
: "rcx", "r11", "memory");
if (!failed) {
return *tid;

View file

@ -34,10 +34,12 @@
static pthread_mutex_t __fflush_lock_obj;
void(__fflush_lock)(void) {
__fflush_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_lock(&__fflush_lock_obj);
}
void(__fflush_unlock)(void) {
__fflush_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_unlock(&__fflush_lock_obj);
}

View file

@ -22,5 +22,6 @@
* Acquires reentrant lock on stdio object, blocking if needed.
*/
void(flockfile)(FILE *f) {
f->lock.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_lock(&f->lock);
}

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pthread.h"
#include "libc/mem/mem.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"

View file

@ -22,5 +22,6 @@
* Releases lock on stdio object.
*/
void(funlockfile)(FILE *f) {
f->lock.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_unlock(&f->lock);
}

View file

@ -25,13 +25,13 @@
.init.start 400,_init_stderr
ezlea __stderr,ax
push $_IOLBF
pop (%rax) # f.fd
pop (%rax) # f.fd
push STDERR_FILENO
pop 12(%rax)
mov O_WRONLY,%edx
mov %edx,4(%rax) # f.iomode
mov %edx,4(%rax) # f.iomode
ezlea __stderr_buf,cx
mov %rcx,24(%rax) # f.buf
movl $BUFSIZ,32(%rax) # f.size
mov %rcx,0x18(%rax) # f.buf
movl $BUFSIZ,0x20(%rax) # f.size
mov %rax,stderr(%rip)
.init.end 400,_init_stderr,globl,hidden

View file

@ -25,9 +25,9 @@
.init.start 400,_init_stdin
ezlea __stdin,ax
mov O_RDONLY,%edx
mov %edx,4(%rax) # f.iomode
mov %edx,4(%rax) # f.iomode
ezlea __stdin_buf,cx
mov %rcx,24(%rax) # f.buf
movl $BUFSIZ,32(%rax) # f.size
mov %rcx,0x18(%rax) # f.buf
movl $BUFSIZ,0x20(%rax) # f.size
mov %rax,stdin(%rip)
.init.end 400,_init_stdin,globl,hidden

View file

@ -16,19 +16,19 @@ COSMOPOLITAN_C_START_
*/
typedef struct FILE {
uint8_t bufmode; /* 0x00 _IOFBF, etc. (ignored if fd=-1) */
bool noclose; /* 0x01 for fake dup() todo delete! */
uint32_t iomode; /* 0x04 O_RDONLY, etc. (ignored if fd=-1) */
int32_t state; /* 0x08 0=OK, -1=EOF, >0=errno */
int fd; /* 0x0c ≥0=fd, -1=closed|buffer */
uint32_t beg; /* 0x10 */
uint32_t end; /* 0x14 */
char *buf; /* 0x18 */
uint32_t size; /* 0x20 */
uint32_t nofree; /* 0x24 */
int pid; /* 0x28 */
char *getln;
pthread_mutex_t lock;
uint8_t bufmode; /* 0x00 _IOFBF, etc. (ignored if fd=-1) */
bool noclose; /* 0x01 for fake dup() todo delete! */
uint32_t iomode; /* 0x04 O_RDONLY, etc. (ignored if fd=-1) */
int32_t state; /* 0x08 0=OK, -1=EOF, >0=errno */
int fd; /* 0x0c ≥0=fd, -1=closed|buffer */
uint32_t beg; /* 0x10 */
uint32_t end; /* 0x14 */
char *buf; /* 0x18 */
uint32_t size; /* 0x20 */
uint32_t nofree; /* 0x24 */
int pid; /* 0x28 */
char *getln; /* 0x30 */
pthread_mutex_t lock; /* 0x38 */
} FILE;
extern FILE *stdin;

View file

@ -25,11 +25,11 @@
.init.start 400,_init_stdout
ezlea __stdout,ax
push STDOUT_FILENO
pop 12(%rax) # f.fd
pop 0x0c(%rax) # f.fd
mov O_WRONLY,%edx
mov %edx,4(%rax) # f.iomode
mov %edx,4(%rax) # f.iomode
ezlea __stdout_buf,cx
mov %rcx,24(%rax) # f.buf
movl $BUFSIZ,32(%rax) # f.size
mov %rcx,0x18(%rax) # f.buf
movl $BUFSIZ,0x20(%rax) # f.size
mov %rax,stdout(%rip)
.init.end 400,_init_stdout,globl,hidden