Get rid of kmalloc()

This changes *NSYNC to allocate waiters on the stack so our locks don't
need to depend on dynamic memory. This make our runtiem simpler, and it
also fixes bugs with thread cancellation support.
This commit is contained in:
Justine Tunney 2023-09-11 21:34:53 -07:00
parent 77a7873057
commit a359de7893
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
57 changed files with 405 additions and 472 deletions

View file

@ -33,7 +33,7 @@
#define pagesz 16384 #define pagesz 16384
#define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24) #define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24)
#define SYSLIB_VERSION 1 #define SYSLIB_VERSION 2
struct Syslib { struct Syslib {
int magic; int magic;
@ -56,6 +56,9 @@ struct Syslib {
long (*dispatch_semaphore_signal)(dispatch_semaphore_t); long (*dispatch_semaphore_signal)(dispatch_semaphore_t);
long (*dispatch_semaphore_wait)(dispatch_semaphore_t, dispatch_time_t); long (*dispatch_semaphore_wait)(dispatch_semaphore_t, dispatch_time_t);
dispatch_time_t (*dispatch_walltime)(const struct timespec *, int64_t); dispatch_time_t (*dispatch_walltime)(const struct timespec *, int64_t);
/* v2 (2023-09-10) */
pthread_t (*pthread_self)(void);
void (*dispatch_release)(dispatch_semaphore_t);
}; };
#define ELFCLASS32 1 #define ELFCLASS32 1
@ -829,6 +832,8 @@ int main(int argc, char **argv, char **envp) {
M->lib.dispatch_semaphore_signal = dispatch_semaphore_signal; M->lib.dispatch_semaphore_signal = dispatch_semaphore_signal;
M->lib.dispatch_semaphore_wait = dispatch_semaphore_wait; M->lib.dispatch_semaphore_wait = dispatch_semaphore_wait;
M->lib.dispatch_walltime = dispatch_walltime; M->lib.dispatch_walltime = dispatch_walltime;
M->lib.pthread_self = pthread_self;
M->lib.dispatch_release = dispatch_release;
/* getenv("_") is close enough to at_execfn */ /* getenv("_") is close enough to at_execfn */
execfn = argc > 0 ? argv[0] : 0; execfn = argc > 0 ? argv[0] : 0;

View file

@ -2,7 +2,7 @@
#define COSMOPOLITAN_APE_APE_H_ #define COSMOPOLITAN_APE_APE_H_
#define APE_VERSION_MAJOR 1 #define APE_VERSION_MAJOR 1
#define APE_VERSION_MINOR 7 #define APE_VERSION_MINOR 8
#define APE_VERSION_STR APE_VERSION_STR_(APE_VERSION_MAJOR, APE_VERSION_MINOR) #define APE_VERSION_STR APE_VERSION_STR_(APE_VERSION_MAJOR, APE_VERSION_MINOR)
#define APE_VERSION_NOTE APE_VERSION_NOTE_(APE_VERSION_MAJOR, APE_VERSION_MINOR) #define APE_VERSION_NOTE APE_VERSION_NOTE_(APE_VERSION_MAJOR, APE_VERSION_MINOR)

View file

@ -71,7 +71,7 @@ int execve(const char *prog, char *const argv[], char *const envp[]) {
rc = _weaken(sys_pledge_linux)(__execpromises, __pledge_mode); rc = _weaken(sys_pledge_linux)(__execpromises, __pledge_mode);
} }
if (!rc) { if (!rc) {
if (_weaken(__zipos_parseuri) && if (0 && _weaken(__zipos_parseuri) &&
(_weaken(__zipos_parseuri)(prog, &uri) != -1)) { (_weaken(__zipos_parseuri)(prog, &uri) != -1)) {
rc = _weaken(__zipos_open)(&uri, O_RDONLY | O_CLOEXEC); rc = _weaken(__zipos_open)(&uri, O_RDONLY | O_CLOEXEC);
if (rc != -1) { if (rc != -1) {

View file

@ -25,12 +25,13 @@
#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h"
#include "libc/calls/wincrash.internal.h" #include "libc/calls/wincrash.internal.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/kmalloc.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/intrin/leaky.internal.h"
#include "libc/intrin/weaken.h" #include "libc/intrin/weaken.h"
#include "libc/limits.h" #include "libc/limits.h"
#include "libc/log/backtrace.internal.h" #include "libc/log/backtrace.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/nt/createfile.h" #include "libc/nt/createfile.h"
#include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/enum/filelockflags.h" #include "libc/nt/enum/filelockflags.h"
@ -72,7 +73,7 @@ static textwindows struct FileLock *NewFileLock(void) {
fl = g_locks.free; fl = g_locks.free;
g_locks.free = fl->next; g_locks.free = fl->next;
} else { } else {
fl = kmalloc(sizeof(*fl)); unassert((fl = _weaken(malloc)(sizeof(*fl))));
} }
bzero(fl, sizeof(*fl)); bzero(fl, sizeof(*fl));
fl->next = g_locks.list; fl->next = g_locks.list;
@ -80,6 +81,8 @@ static textwindows struct FileLock *NewFileLock(void) {
return fl; return fl;
} }
IGNORE_LEAKS(NewFileLock)
static textwindows void FreeFileLock(struct FileLock *fl) { static textwindows void FreeFileLock(struct FileLock *fl) {
fl->next = g_locks.free; fl->next = g_locks.free;
g_locks.free = fl; g_locks.free = fl;
@ -129,6 +132,10 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int fd, int cmd,
int64_t pos, off, len, end; int64_t pos, off, len, end;
struct FileLock *fl, *ft, **flp; struct FileLock *fl, *ft, **flp;
if (!_weaken(malloc)) {
return enomem();
}
l = (struct flock *)arg; l = (struct flock *)arg;
len = l->l_len; len = l->l_len;
off = l->l_start; off = l->l_start;

View file

@ -87,8 +87,6 @@ textwindows int ntspawn(
block = NULL; block = NULL;
_init_sigchld(); _init_sigchld();
if (__mkntpath(prog, prog16) == -1) return -1; if (__mkntpath(prog, prog16) == -1) return -1;
// we can't call malloc() because we're higher in the topological order
// we can't call kmalloc() because fork() calls this when kmalloc is locked
if ((handle = CreateFileMapping(-1, 0, pushpop(kNtPageReadwrite), 0, if ((handle = CreateFileMapping(-1, 0, pushpop(kNtPageReadwrite), 0,
sizeof(*block), 0)) && sizeof(*block), 0)) &&
(block = MapViewOfFileEx(handle, kNtFileMapRead | kNtFileMapWrite, 0, 0, (block = MapViewOfFileEx(handle, kNtFileMapRead | kNtFileMapWrite, 0, 0,

View file

@ -87,6 +87,7 @@ i32 sys_sem_close(i64);
i32 sys_sem_destroy(i64); i32 sys_sem_destroy(i64);
i32 sys_sem_getvalue(i64, u32 *); i32 sys_sem_getvalue(i64, u32 *);
i32 sys_sem_init(u32, i64 *); i32 sys_sem_init(u32, i64 *);
i32 sys_sem_destroy(i64);
i32 sys_sem_open(const char *, int, u32, i64 *); i32 sys_sem_open(const char *, int, u32, i64 *);
i32 sys_sem_post(i64); i32 sys_sem_post(i64);
i32 sys_sem_trywait(i64); i32 sys_sem_trywait(i64);

View file

@ -24,6 +24,7 @@
#include "libc/calls/termios.internal.h" #include "libc/calls/termios.internal.h"
#include "libc/calls/ttydefaults.h" #include "libc/calls/ttydefaults.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/nomultics.internal.h" #include "libc/intrin/nomultics.internal.h"
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
@ -261,6 +262,7 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
__attribute__((__constructor__)) static void tcsetattr_nt_init(void) { __attribute__((__constructor__)) static void tcsetattr_nt_init(void) {
if (!getenv("TERM")) { if (!getenv("TERM")) {
setenv("TERM", "xterm-256color", true); setenv("TERM", "xterm-256color", true);
errno = 0; // ignore malloc not linked
} }
} }

View file

@ -98,16 +98,43 @@ static dontinline textwindows int __tkill_nt(int tid, int sig,
} }
} }
static int __tkill_m1(int tid, int sig, struct CosmoTib *tib) { static int __tkill_posix(int tid, int sig, struct CosmoTib *tib) {
struct PosixThread *pt = (struct PosixThread *)__get_tls()->tib_pthread;
return __syslib->pthread_kill(pt->next, sig); // avoid lock when killing self
int me;
struct PosixThread *pt;
pt = (struct PosixThread *)__get_tls()->tib_pthread;
me = atomic_load_explicit(&__get_tls()->tib_tid, memory_order_relaxed);
if (tid == me && (!tib || tib == __get_tls())) {
return __syslib->pthread_kill(pt->next, sig);
}
// otherwise look for matching thread
struct Dll *e;
pthread_spin_lock(&_pthread_lock);
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
enum PosixThreadStatus status;
struct PosixThread *pt = POSIXTHREAD_CONTAINER(e);
int rhs = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire);
if (rhs <= 0 || tid != rhs) continue;
if (tib && tib != pt->tib) continue;
status = atomic_load_explicit(&pt->status, memory_order_acquire);
pthread_spin_unlock(&_pthread_lock);
if (status < kPosixThreadTerminated) {
return __syslib->pthread_kill(pt->next, sig);
} else {
return 0;
}
}
pthread_spin_unlock(&_pthread_lock);
return esrch();
} }
// OpenBSD has an optional `tib` parameter for extra safety. // OpenBSD has an optional `tib` parameter for extra safety.
int __tkill(int tid, int sig, void *tib) { int __tkill(int tid, int sig, void *tib) {
int rc; int rc;
if (IsXnuSilicon()) { if (IsXnuSilicon()) {
return __tkill_m1(tid, sig, tib); return __tkill_posix(tid, sig, tib);
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) { } else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) {
rc = sys_tkill(tid, sig, tib); rc = sys_tkill(tid, sig, tib);
} else if (IsWindows()) { } else if (IsWindows()) {

View file

@ -27,7 +27,6 @@
#include "libc/intrin/bits.h" #include "libc/intrin/bits.h"
#include "libc/intrin/cmpxchg.h" #include "libc/intrin/cmpxchg.h"
#include "libc/intrin/directmap.internal.h" #include "libc/intrin/directmap.internal.h"
#include "libc/intrin/kmalloc.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/intrin/leaky.internal.h" #include "libc/intrin/leaky.internal.h"
#include "libc/intrin/likely.h" #include "libc/intrin/likely.h"
@ -835,10 +834,9 @@ static __wur __asan_die_f *__asan_report(const void *addr, int size,
wint_t c; wint_t c;
signed char t; signed char t;
uint64_t x, y, z; uint64_t x, y, z;
char *p, *q, *buf, *base;
struct MemoryIntervals *m; struct MemoryIntervals *m;
char buf[8192], *base, *q, *p = buf;
ftrace_enabled(-1); ftrace_enabled(-1);
p = buf = kmalloc(1024 * 1024);
kprintf("\n\e[J\e[1;31masan error\e[0m: %s %d-byte %s at %p shadow %p\n", kprintf("\n\e[J\e[1;31masan error\e[0m: %s %d-byte %s at %p shadow %p\n",
__asan_describe_access_poison(kind), size, message, addr, __asan_describe_access_poison(kind), size, message, addr,
SHADOW(addr)); SHADOW(addr));

View file

@ -43,8 +43,6 @@ static const char *GetFrameName(int x) {
return "g_fds"; return "g_fds";
} else if (IsZiposFrame(x)) { } else if (IsZiposFrame(x)) {
return "zipos"; return "zipos";
} else if (IsKmallocFrame(x)) {
return "kmalloc";
} else if (IsMemtrackFrame(x)) { } else if (IsMemtrackFrame(x)) {
return "memtrack"; return "memtrack";
} else if (IsOldStackFrame(x)) { } else if (IsOldStackFrame(x)) {

View file

@ -1,95 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/kmalloc.h"
#include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/extend.internal.h"
#include "libc/log/libfatal.internal.h"
#include "libc/macros.internal.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/sysv/consts/map.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#define KMALLOC_ALIGN sizeof(intptr_t)
static struct {
char *endptr;
size_t total;
pthread_spinlock_t lock;
} g_kmalloc;
void __kmalloc_lock(void) {
pthread_spin_lock(&g_kmalloc.lock);
}
void __kmalloc_unlock(void) {
pthread_spin_unlock(&g_kmalloc.lock);
}
#ifdef _NOPL0
#define __kmalloc_lock() _NOPL0("__threadcalls", __kmalloc_lock)
#define __kmalloc_unlock() _NOPL0("__threadcalls", __kmalloc_unlock)
#endif
/**
* Allocates permanent memory.
*
* The code malloc() depends upon uses this function to allocate memory.
* The returned memory can't be freed, and leak detection is impossible.
* This function panics when memory isn't available.
*
* Memory returned by this function is aligned on the word size, and as
* such, kmalloc() shouldn't be used for vector operations.
*
* @return zero-initialized memory on success, or null w/ errno
* @raise ENOMEM if we require more vespene gas
*/
dontasan void *kmalloc(size_t size) {
char *p, *e;
size_t i, n, t;
n = ROUNDUP(size + (IsAsan() * 8), KMALLOC_ALIGN);
__kmalloc_lock();
t = g_kmalloc.total;
e = g_kmalloc.endptr;
i = t;
t += n;
p = (char *)kMemtrackKmallocStart;
if (!e) e = p;
if ((e = _extend(p, t, e, MAP_PRIVATE,
kMemtrackKmallocStart + kMemtrackKmallocSize))) {
g_kmalloc.endptr = e;
g_kmalloc.total = t;
} else {
p = 0;
}
__kmalloc_unlock();
if (p) {
unassert(!((intptr_t)(p + i) & (KMALLOC_ALIGN - 1)));
if (IsAsan()) __asan_poison(p + i + size, n - size, kAsanHeapOverrun);
return p + i;
} else {
return 0;
}
}

View file

@ -1,17 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_KMALLOC_H_
#define COSMOPOLITAN_LIBC_INTRIN_KMALLOC_H_
#ifdef _COSMO_SOURCE
#define kmalloc __kmalloc
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
void *kmalloc(size_t)
mallocesque attributeallocsize((1)) returnsaligned((8));
void __kmalloc_lock(void);
void __kmalloc_unlock(void);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* _COSMO_SOURCE */
#endif /* COSMOPOLITAN_LIBC_INTRIN_KMALLOC_H_ */

View file

@ -1,12 +1,17 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_ #ifndef COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_
#define COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_ #define COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_
#include "libc/dce.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
#if IsAsan()
#define IGNORE_LEAKS(FUNC) \ #define IGNORE_LEAKS(FUNC) \
__static_yoink("_leaky_start"); \ __static_yoink("_leaky_start"); \
void *_leaky_##FUNC[] _Section(".piro.relo.sort.leaky.2." #FUNC \ void *_leaky_##FUNC[] _Section(".piro.relo.sort.leaky.2." #FUNC \
",\"aw\",@init_array #") = {FUNC} ",\"aw\",@init_array #") = {FUNC};
#else
#define IGNORE_LEAKS(FUNC)
#endif
extern intptr_t _leaky_end[] __attribute__((__weak__)); extern intptr_t _leaky_end[] __attribute__((__weak__));
extern intptr_t _leaky_start[] __attribute__((__weak__)); extern intptr_t _leaky_start[] __attribute__((__weak__));

View file

@ -19,6 +19,7 @@
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/atomic.h" #include "libc/intrin/atomic.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/weaken.h" #include "libc/intrin/weaken.h"
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
#include "libc/thread/tls.h" #include "libc/thread/tls.h"

View file

@ -17,11 +17,14 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/intrin/getenv.internal.h" #include "libc/intrin/getenv.internal.h"
#include "libc/intrin/kmalloc.h" #include "libc/intrin/leaky.internal.h"
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/mem/internal.h" #include "libc/mem/internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/sysv/errfuns.h"
#define ToUpper(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c)) #define ToUpper(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c))
@ -40,7 +43,7 @@ static char **GrowEnviron(char **a) {
if (!a) a = environ; if (!a) a = environ;
n = a ? GetEnvironLen(a) : 0; n = a ? GetEnvironLen(a) : 0;
c = MAX(16ul, n) << 1; c = MAX(16ul, n) << 1;
if ((b = kmalloc(c * sizeof(char *)))) { if (_weaken(malloc) && (b = _weaken(malloc)(c * sizeof(char *)))) {
if (a) { if (a) {
for (p = b; *a;) { for (p = b; *a;) {
*p++ = *a++; *p++ = *a++;
@ -51,10 +54,13 @@ static char **GrowEnviron(char **a) {
capacity = c; capacity = c;
return b; return b;
} else { } else {
enomem();
return 0; return 0;
} }
} }
IGNORE_LEAKS(GrowEnviron)
int PutEnvImpl(char *s, bool overwrite) { int PutEnvImpl(char *s, bool overwrite) {
char **p; char **p;
struct Env e; struct Env e;

View file

@ -16,9 +16,11 @@
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/intrin/kmalloc.h" #include "libc/intrin/leaky.internal.h"
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/mem/internal.h" #include "libc/mem/internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
@ -43,9 +45,13 @@ int setenv(const char *name, const char *value, int overwrite) {
} }
n = strlen(name); n = strlen(name);
m = strlen(value); m = strlen(value);
if (!(s = kmalloc(n + 1 + m + 1))) return -1; if (!_weaken(malloc) || !(s = _weaken(malloc)(n + 1 + m + 1))) {
return enomem();
}
memcpy(mempcpy(mempcpy(s, name, n), "=", 1), value, m + 1); memcpy(mempcpy(mempcpy(s, name, n), "=", 1), value, m + 1);
rc = PutEnvImpl(s, overwrite); rc = PutEnvImpl(s, overwrite);
STRACE("setenv(%#s, %#s, %d) → %d% m", name, value, overwrite, rc); STRACE("setenv(%#s, %#s, %d) → %d% m", name, value, overwrite, rc);
return rc; return rc;
} }
IGNORE_LEAKS(setenv)

View file

@ -32,7 +32,6 @@
#include "libc/intrin/atomic.h" #include "libc/intrin/atomic.h"
#include "libc/intrin/describebacktrace.internal.h" #include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/kmalloc.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h" #include "libc/intrin/weaken.h"
@ -207,9 +206,9 @@ void ShowCrashReportHook(int, int, int, struct siginfo *, ucontext_t *);
relegated void ShowCrashReport(int err, int sig, struct siginfo *si, relegated void ShowCrashReport(int err, int sig, struct siginfo *si,
ucontext_t *ctx) { ucontext_t *ctx) {
int i; int i;
size_t n; char *p;
char host[64]; char host[64];
char *p, *buf; char buf[3000];
struct utsname names; struct utsname names;
if (_weaken(ShowCrashReportHook)) { if (_weaken(ShowCrashReportHook)) {
ShowCrashReportHook(2, err, sig, si, ctx); ShowCrashReportHook(2, err, sig, si, ctx);
@ -223,9 +222,9 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si,
uname(&names); uname(&names);
errno = err; errno = err;
// TODO(jart): Buffer the WHOLE crash report with backtrace for atomic write. // TODO(jart): Buffer the WHOLE crash report with backtrace for atomic write.
npassert((p = buf = kmalloc((n = 1024 * 1024)))); p = buf;
p += ksnprintf( p += ksnprintf(
p, n, p, 10000,
"\n%serror%s: Uncaught %G (%s) on %s pid %d tid %d\n" "\n%serror%s: Uncaught %G (%s) on %s pid %d tid %d\n"
" %s\n" " %s\n"
" %s\n" " %s\n"

View file

@ -68,7 +68,6 @@
extern int64_t __wincrashearly; extern int64_t __wincrashearly;
bool32 __onntconsoleevent(uint32_t); bool32 __onntconsoleevent(uint32_t);
void sys_setitimer_nt_reset(void); void sys_setitimer_nt_reset(void);
void kmalloc_unlock(void);
static textwindows wontreturn void AbortFork(const char *func) { static textwindows wontreturn void AbortFork(const char *func) {
#ifdef SYSDEBUG #ifdef SYSDEBUG

View file

@ -12,19 +12,17 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
#define kAutomapStart 0x100080040000 #define kAutomapStart 0x100080040000
#define kAutomapSize (kMemtrackStart - kAutomapStart) #define kAutomapSize (kMemtrackStart - kAutomapStart)
#define kMemtrackStart 0x1fe7fffc0000 #define kMemtrackStart 0x1fe7fffc0000
#define kMemtrackSize (0x1ffffffc0000 - kMemtrackStart) #define kMemtrackSize (0x1ffffffc0000 - kMemtrackStart)
#define kFixedmapStart 0x300000040000 #define kFixedmapStart 0x300000040000
#define kFixedmapSize (0x400000040000 - kFixedmapStart) #define kFixedmapSize (0x400000040000 - kFixedmapStart)
#define kMemtrackFdsStart 0x6fe000040000 #define kMemtrackFdsStart 0x6fe000040000
#define kMemtrackFdsSize (0x6feffffc0000 - kMemtrackFdsStart) #define kMemtrackFdsSize (0x6feffffc0000 - kMemtrackFdsStart)
#define kMemtrackZiposStart 0x6fd000040000 #define kMemtrackZiposStart 0x6fd000040000
#define kMemtrackZiposSize (0x6fdffffc0000 - kMemtrackZiposStart) #define kMemtrackZiposSize (0x6fdffffc0000 - kMemtrackZiposStart)
#define kMemtrackKmallocStart 0x6fc000040000 #define kMemtrackGran (!IsAsan() ? FRAMESIZE : FRAMESIZE * 8)
#define kMemtrackKmallocSize (0x6fcffffc0000 - kMemtrackKmallocStart)
#define kMemtrackGran (!IsAsan() ? FRAMESIZE : FRAMESIZE * 8)
struct MemoryInterval { struct MemoryInterval {
int x; int x;
@ -132,11 +130,6 @@ forceinline pureconst bool IsZiposFrame(int x) {
x <= (int)((kMemtrackZiposStart + kMemtrackZiposSize - 1) >> 16); x <= (int)((kMemtrackZiposStart + kMemtrackZiposSize - 1) >> 16);
} }
forceinline pureconst bool IsKmallocFrame(int x) {
return (int)(kMemtrackKmallocStart >> 16) <= x &&
x <= (int)((kMemtrackKmallocStart + kMemtrackKmallocSize - 1) >> 16);
}
forceinline pureconst bool IsShadowFrame(int x) { forceinline pureconst bool IsShadowFrame(int x) {
return 0x7fff <= x && x < 0x10008000; return 0x7fff <= x && x < 0x10008000;
} }

View file

@ -1808,7 +1808,7 @@
6f900000-6f9fffff 64gb free 6f900000-6f9fffff 64gb free
6fa00000-6fafffff 64gb free 6fa00000-6fafffff 64gb free
6fb00000-6fbfffff 64gb free 6fb00000-6fbfffff 64gb free
6fc00004-6fcffffb 64gb kmalloc 6fc00004-6fcffffb 64gb free
6fd00004-6fdffffb 64gb zipos 6fd00004-6fdffffb 64gb zipos
6fe00004-6feffffb 64gb g_fds 6fe00004-6feffffb 64gb g_fds
6ff00004-70000003 64gb free 6ff00004-70000003 64gb free

View file

@ -44,6 +44,7 @@ struct Syslib {
dispatch_time_t (*dispatch_walltime)(const struct timespec *, int64_t); dispatch_time_t (*dispatch_walltime)(const struct timespec *, int64_t);
/* v2 (2023-09-10) */ /* v2 (2023-09-10) */
long (*pthread_self)(void); long (*pthread_self)(void);
void (*dispatch_release)(dispatch_semaphore_t);
}; };
extern struct Syslib *__syslib; extern struct Syslib *__syslib;

View file

@ -23,7 +23,6 @@
#include "libc/cosmo.h" #include "libc/cosmo.h"
#include "libc/fmt/conv.h" #include "libc/fmt/conv.h"
#include "libc/intrin/cmpxchg.h" #include "libc/intrin/cmpxchg.h"
#include "libc/intrin/kmalloc.h"
#include "libc/intrin/promises.internal.h" #include "libc/intrin/promises.internal.h"
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
@ -91,7 +90,7 @@ static int __zipos_compare_names(const void *a, const void *b, void *c) {
static void __zipos_generate_index(struct Zipos *zipos) { static void __zipos_generate_index(struct Zipos *zipos) {
size_t c, i; size_t c, i;
zipos->records = GetZipCdirRecords(zipos->cdir); zipos->records = GetZipCdirRecords(zipos->cdir);
zipos->index = kmalloc(zipos->records * sizeof(size_t)); zipos->index = _mapanon(zipos->records * sizeof(size_t));
for (i = 0, c = GetZipCdirOffset(zipos->cdir); i < zipos->records; for (i = 0, c = GetZipCdirOffset(zipos->cdir); i < zipos->records;
++i, c += ZIP_CFILE_HDRSIZE(zipos->map + c)) { ++i, c += ZIP_CFILE_HDRSIZE(zipos->map + c)) {
zipos->index[i] = c; zipos->index[i] = c;

View file

@ -16,49 +16,26 @@
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/intrin/atomic.h" #include "libc/mem/mem.h"
#include "libc/intrin/kmalloc.h"
#include "libc/stdio/internal.h" #include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
static _Atomic(FILE *) __stdio_freelist;
FILE *__stdio_alloc(void) { FILE *__stdio_alloc(void) {
FILE *f; FILE *f;
f = atomic_load_explicit(&__stdio_freelist, memory_order_acquire); if ((f = calloc(1, sizeof(FILE)))) {
while (f) { pthread_mutexattr_t attr;
if (atomic_compare_exchange_weak_explicit( pthread_mutexattr_init(&attr);
&__stdio_freelist, &f, pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
atomic_load_explicit((_Atomic(struct FILE *) *)&f->next, pthread_mutex_init(&f->lock, &attr);
memory_order_acquire), pthread_mutexattr_destroy(&attr);
memory_order_release, memory_order_relaxed)) { f->dynamic = 1;
atomic_store_explicit((_Atomic(struct FILE *) *)&f->next, 0,
memory_order_release);
break;
}
}
if (!f) {
f = kmalloc(sizeof(FILE));
}
if (f) {
f->lock._type = PTHREAD_MUTEX_RECURSIVE;
} }
return f; return f;
} }
void __stdio_free(FILE *f) { void __stdio_free(FILE *f) {
FILE *g; pthread_mutex_destroy(&f->lock);
bzero(f, sizeof(*f)); if (f->dynamic) {
g = atomic_load_explicit(&__stdio_freelist, memory_order_acquire); free(f);
for (;;) {
atomic_store_explicit((_Atomic(struct FILE *) *)&f->next, g,
memory_order_release);
if (atomic_compare_exchange_weak_explicit(&__stdio_freelist, &g, f,
memory_order_release,
memory_order_relaxed)) {
break;
}
} }
} }

View file

@ -9,21 +9,22 @@
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
struct FILE { struct FILE {
uint8_t bufmode; /* 0x00 _IOFBF, etc. (ignored if fd=-1) */ uint8_t bufmode; /* _IOFBF, etc. (ignored if fd=-1) */
char noclose; /* 0x01 for fake dup() todo delete! */ char noclose; /* for fake dup() todo delete! */
uint32_t iomode; /* 0x04 O_RDONLY, etc. (ignored if fd=-1) */ char dynamic; /* did malloc() create this object? */
int32_t state; /* 0x08 0=OK, -1=EOF, >0=errno */ uint32_t iomode; /* O_RDONLY, etc. (ignored if fd=-1) */
int fd; /* 0x0c ≥0=fd, -1=closed|buffer */ int32_t state; /* 0=OK, -1=EOF, >0=errno */
uint32_t beg; /* 0x10 */ int fd; /* ≥0=fd, -1=closed|buffer */
uint32_t end; /* 0x14 */ uint32_t beg;
char *buf; /* 0x18 */ uint32_t end;
uint32_t size; /* 0x20 */ char *buf;
uint32_t nofree; /* 0x24 */ uint32_t size;
int pid; /* 0x28 */ uint32_t nofree;
char *getln; /* 0x30 */ int pid;
pthread_mutex_t lock; /* 0x38 */ char *getln;
struct FILE *next; /* 0x48 */ pthread_mutex_t lock;
char mem[BUFSIZ]; /* 0x50 */ struct FILE *next;
char mem[BUFSIZ];
}; };
extern uint64_t g_rando; extern uint64_t g_rando;

View file

@ -17,13 +17,17 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/calls/state.internal.h" #include "libc/calls/state.internal.h"
#include "libc/cosmo.h"
#include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/atomic.h" #include "libc/intrin/atomic.h"
#include "libc/intrin/dll.h" #include "libc/intrin/dll.h"
#include "libc/intrin/kmalloc.h" #include "libc/intrin/leaky.internal.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/runtime/memtrack.internal.h" #include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/thread/posixthread.internal.h" #include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
@ -61,11 +65,9 @@ void _pthread_onfork_prepare(void) {
pthread_spin_lock(&_pthread_lock); pthread_spin_lock(&_pthread_lock);
__fds_lock(); __fds_lock();
__mmi_lock(); __mmi_lock();
__kmalloc_lock();
} }
void _pthread_onfork_parent(void) { void _pthread_onfork_parent(void) {
__kmalloc_unlock();
__mmi_unlock(); __mmi_unlock();
__fds_unlock(); __fds_unlock();
pthread_spin_unlock(&_pthread_lock); pthread_spin_unlock(&_pthread_lock);
@ -85,7 +87,6 @@ void _pthread_onfork_child(void) {
atomic_store_explicit(&pt->cancelled, false, memory_order_relaxed); atomic_store_explicit(&pt->cancelled, false, memory_order_relaxed);
// wipe core runtime locks // wipe core runtime locks
__kmalloc_unlock();
pthread_mutexattr_init(&attr); pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&__mmi_lock_obj, &attr); pthread_mutex_init(&__mmi_lock_obj, &attr);
@ -105,7 +106,7 @@ void _pthread_onfork_child(void) {
int _pthread_atfork(atfork_f prepare, atfork_f parent, atfork_f child) { int _pthread_atfork(atfork_f prepare, atfork_f parent, atfork_f child) {
int rc; int rc;
struct AtFork *a; struct AtFork *a;
if (!(a = kmalloc(sizeof(struct AtFork)))) return ENOMEM; if (!(a = malloc(sizeof(struct AtFork)))) return ENOMEM;
a->f[0] = prepare; a->f[0] = prepare;
a->f[1] = parent; a->f[1] = parent;
a->f[2] = child; a->f[2] = child;
@ -118,3 +119,5 @@ int _pthread_atfork(atfork_f prepare, atfork_f parent, atfork_f child) {
rc = 0; rc = 0;
return rc; return rc;
} }
IGNORE_LEAKS(_pthread_atfork)

View file

@ -27,7 +27,7 @@ struct CosmoTib {
_Atomic(int32_t) tib_tid; /* 0x38 transitions -1 → tid → 0 */ _Atomic(int32_t) tib_tid; /* 0x38 transitions -1 → tid → 0 */
int32_t tib_errno; /* 0x3c */ int32_t tib_errno; /* 0x3c */
uint64_t tib_flags; /* 0x40 */ uint64_t tib_flags; /* 0x40 */
void *tib_nsync; long __padding;
int tib_ftrace; /* inherited */ int tib_ftrace; /* inherited */
int tib_strace; /* inherited */ int tib_strace; /* inherited */
uint64_t tib_sigmask; /* inherited */ uint64_t tib_sigmask; /* inherited */

View file

@ -17,7 +17,7 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/intrin/atomic.h" #include "libc/intrin/atomic.h"
#include "libc/intrin/kmalloc.h" #include "libc/mem/mem.h"
#include "libc/runtime/internal.h" #include "libc/runtime/internal.h"
#include "libc/x/x.h" #include "libc/x/x.h"
#include "third_party/zlib/zlib.h" #include "third_party/zlib/zlib.h"
@ -44,7 +44,7 @@
void *xload(_Atomic(void *) *a, const void *p, size_t n, size_t m) { void *xload(_Atomic(void *) *a, const void *p, size_t n, size_t m) {
void *r, *z; void *r, *z;
if ((r = atomic_load_explicit(a, memory_order_acquire))) return r; if ((r = atomic_load_explicit(a, memory_order_acquire))) return r;
if (!(r = kmalloc(m))) return 0; if (!(r = malloc(m))) return 0;
if (__inflate(r, m, p, n)) return 0; if (__inflate(r, m, p, n)) return 0;
z = 0; z = 0;
if (!atomic_compare_exchange_strong_explicit(a, &z, r, memory_order_release, if (!atomic_compare_exchange_strong_explicit(a, &z, r, memory_order_release,

View file

@ -89,6 +89,7 @@ __attribute__((__constructor__)) static void init(void) {
} }
} }
#ifdef __x86_64__
TEST(sched_setaffinity, isInheritedAcrossExecve) { TEST(sched_setaffinity, isInheritedAcrossExecve) {
cpu_set_t x; cpu_set_t x;
CPU_ZERO(&x); CPU_ZERO(&x);
@ -103,6 +104,7 @@ TEST(sched_setaffinity, isInheritedAcrossExecve) {
EXPECT_TRUE(WIFEXITED(ws)); EXPECT_TRUE(WIFEXITED(ws));
EXPECT_EQ(42, WEXITSTATUS(ws)); EXPECT_EQ(42, WEXITSTATUS(ws));
} }
#endif /* __x86_64__ */
TEST(sched_getaffinity, getpid) { TEST(sched_getaffinity, getpid) {
cpu_set_t x, y; cpu_set_t x, y;

View file

@ -28,7 +28,6 @@
#include "libc/mem/gc.h" #include "libc/mem/gc.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/stdio/lock.internal.h"
#include "libc/stdio/rand.h" #include "libc/stdio/rand.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
#include "libc/str/str.h" #include "libc/str/str.h"

View file

@ -251,7 +251,7 @@ void PosixSpawnWait(const char *prog) {
ASSERT_EQ(42, WEXITSTATUS(ws)); ASSERT_EQ(42, WEXITSTATUS(ws));
} }
TEST(posix_spawn, bench) { BENCH(posix_spawn, bench) {
long n = 128L * 1000 * 1000; long n = 128L * 1000 * 1000;
memset(gc(malloc(n)), -1, n); memset(gc(malloc(n)), -1, n);
creat("tiny64", 0755); creat("tiny64", 0755);

View file

@ -169,18 +169,17 @@ void *CondWaitDeferredWorker(void *arg) {
pthread_setcancelstate(PTHREAD_CANCEL_DEFERRED, 0); pthread_setcancelstate(PTHREAD_CANCEL_DEFERRED, 0);
ASSERT_EQ(0, pthread_mutex_lock(&mu)); ASSERT_EQ(0, pthread_mutex_lock(&mu));
ASSERT_EQ(ECANCELED, pthread_cond_timedwait(&cv, &mu, 0)); ASSERT_EQ(ECANCELED, pthread_cond_timedwait(&cv, &mu, 0));
ASSERT_EQ(0, pthread_mutex_unlock(&mu)); __builtin_trap();
return 0;
} }
TEST(pthread_cancel, condDeferredWait) { TEST(pthread_cancel, condDeferredWait_reacquiresMutex) {
void *rc; void *rc;
pthread_t th; pthread_t th;
ASSERT_EQ(0, pthread_create(&th, 0, CondWaitDeferredWorker, 0)); ASSERT_EQ(0, pthread_create(&th, 0, CondWaitDeferredWorker, 0));
pthread_cancel(th); pthread_cancel(th);
ASSERT_EQ(0, pthread_join(th, &rc)); ASSERT_EQ(0, pthread_join(th, &rc));
ASSERT_EQ(PTHREAD_CANCELED, rc); ASSERT_EQ(PTHREAD_CANCELED, rc);
ASSERT_EQ(0, pthread_mutex_trylock(&mu)); ASSERT_EQ(EBUSY, pthread_mutex_trylock(&mu));
ASSERT_EQ(0, pthread_mutex_unlock(&mu)); ASSERT_EQ(0, pthread_mutex_unlock(&mu));
} }
@ -192,7 +191,7 @@ TEST(pthread_cancel, condDeferredWaitDelayed) {
pthread_cancel(th); pthread_cancel(th);
ASSERT_EQ(0, pthread_join(th, &rc)); ASSERT_EQ(0, pthread_join(th, &rc));
ASSERT_EQ(PTHREAD_CANCELED, rc); ASSERT_EQ(PTHREAD_CANCELED, rc);
ASSERT_EQ(0, pthread_mutex_trylock(&mu)); ASSERT_EQ(EBUSY, pthread_mutex_trylock(&mu));
ASSERT_EQ(0, pthread_mutex_unlock(&mu)); ASSERT_EQ(0, pthread_mutex_unlock(&mu));
} }

View file

@ -93,7 +93,7 @@ static void userinfo(PERSON *pn, struct passwd *pw) {
pn->shell = strdup(pw->pw_shell); pn->shell = strdup(pw->pw_shell);
/* make a private copy of gecos to munge */ /* make a private copy of gecos to munge */
strncpy(tbuf, pw->pw_gecos, TBUFLEN); strlcpy(tbuf, pw->pw_gecos, TBUFLEN);
tbuf[TBUFLEN-1] = 0; /* ensure null termination */ tbuf[TBUFLEN-1] = 0; /* ensure null termination */
bp = tbuf; bp = tbuf;
@ -176,7 +176,7 @@ match(struct passwd *pw, const char *user)
int i, j, ct, rv=0; int i, j, ct, rv=0;
char *rname; char *rname;
strncpy(tbuf, pw->pw_gecos, TBUFLEN); strlcpy(tbuf, pw->pw_gecos, TBUFLEN);
tbuf[TBUFLEN-1] = 0; /* guarantee null termination */ tbuf[TBUFLEN-1] = 0; /* guarantee null termination */
p = tbuf; p = tbuf;

View file

@ -15,9 +15,22 @@ ORIGIN
LOCAL CHANGES LOCAL CHANGES
- nsync_malloc_() is implemented as kmalloc() - Time APIs were so good that they're now part of our libc
- nsync_mu_semaphore uses Cosmopolitan Futexes
- block pthread cancellations in nsync_mu_lock_slow_ - Double linked list API was so good that it's now part of our libc
- support posix thread cancellations in nsync_cv_wait
- timespec api was so good that it's now part of libc - Modified *NSYNC to allocate waiter objects on the stack. We need it
- linked list api was so good that it's now part of libc because we use *NSYNC mutexes to implement POSIX mutexes, which are
too low-level to safely depend on malloc, or even mmap in our case.
- Rewrote most of the semaphore and futex system call support code so
it works well with Cosmopolitan's fat runtime portability. *NSYNC's
unit test suite passes on all supported platforms. However the BSDs
currently appear to be overutilizing CPU time compared with others.
- Support POSIX thread cancellation. APIs that wait on condition vars
are now cancellation points. In PTHREAD_CANCEL_MASKED mode they may
return ECANCELED. In PTHREAD_CANCEL_DEFERRED mode the POSIX threads
library will unwind the stack to unlock any locks and free waiters.
On the other hand the *NSYNC APIs for mutexes will now safely block
thread cancellation, but you can still use *NSYNC notes to do that.

View file

@ -16,10 +16,6 @@
limitations under the License. limitations under the License.
*/ */
#include "libc/intrin/dll.h" #include "libc/intrin/dll.h"
#include "libc/intrin/kmalloc.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h" #include "libc/thread/tls.h"
#include "third_party/nsync/atomic.h" #include "third_party/nsync/atomic.h"
#include "third_party/nsync/atomic.internal.h" #include "third_party/nsync/atomic.internal.h"
@ -147,78 +143,25 @@ waiter *nsync_dll_waiter_samecond_ (struct Dll *e) {
/* -------------------------------- */ /* -------------------------------- */
static struct Dll *free_waiters = NULL; /* Initializes waiter struct. */
void nsync_waiter_init_ (waiter *w) {
/* free_waiters points to a doubly-linked list of free waiter structs. */ w->tag = WAITER_TAG;
static nsync_atomic_uint32_ free_waiters_mu; /* spinlock; protects free_waiters */ w->nw.tag = NSYNC_WAITER_TAG;
nsync_mu_semaphore_init (&w->sem);
#define waiter_for_thread __get_tls()->tib_nsync w->nw.sem = &w->sem;
dll_init (&w->nw.q);
static void waiter_destroy (void *v) { NSYNC_ATOMIC_UINT32_STORE_ (&w->nw.waiting, 0);
waiter *w = (waiter *) v; w->nw.flags = NSYNC_WAITER_FLAG_MUCV;
/* Reset waiter_for_thread in case another thread-local variable reuses ATM_STORE (&w->remove_count, 0);
the waiter in its destructor while the waiter is taken by the other dll_init (&w->same_condition);
thread from free_waiters. This can happen as the destruction order w->flags = WAITER_IN_USE;
of thread-local variables can be arbitrary in some platform e.g.
POSIX. */
waiter_for_thread = NULL;
IGNORE_RACES_START ();
ASSERT ((w->flags & (WAITER_RESERVED|WAITER_IN_USE)) == WAITER_RESERVED);
w->flags &= ~WAITER_RESERVED;
nsync_spin_test_and_set_ (&free_waiters_mu, 1, 1, 0);
dll_make_first (&free_waiters, &w->nw.q);
ATM_STORE_REL (&free_waiters_mu, 0); /* release store */
IGNORE_RACES_END ();
} }
/* Return a pointer to an unused waiter struct. /* Destroys waiter struct. */
Ensures that the enclosed timer is stopped and its channel drained. */ void nsync_waiter_destroy_ (waiter *w) {
waiter *nsync_waiter_new_ (void) { if (w->tag) {
struct Dll *q; nsync_mu_semaphore_destroy (&w->sem);
waiter *tw; w->tag = 0;
waiter *w;
tw = waiter_for_thread;
w = tw;
if (w == NULL || (w->flags & (WAITER_RESERVED|WAITER_IN_USE)) != WAITER_RESERVED) {
w = NULL;
nsync_spin_test_and_set_ (&free_waiters_mu, 1, 1, 0);
q = dll_first (free_waiters);
if (q != NULL) { /* If free list is non-empty, dequeue an item. */
dll_remove (&free_waiters, q);
w = DLL_WAITER (q);
}
ATM_STORE_REL (&free_waiters_mu, 0); /* release store */
if (w == NULL) { /* If free list was empty, allocate an item. */
w = (waiter *) kmalloc (sizeof (*w));
w->tag = WAITER_TAG;
w->nw.tag = NSYNC_WAITER_TAG;
nsync_mu_semaphore_init (&w->sem);
w->nw.sem = &w->sem;
dll_init (&w->nw.q);
NSYNC_ATOMIC_UINT32_STORE_ (&w->nw.waiting, 0);
w->nw.flags = NSYNC_WAITER_FLAG_MUCV;
ATM_STORE (&w->remove_count, 0);
dll_init (&w->same_condition);
w->flags = 0;
}
if (tw == NULL) {
w->flags |= WAITER_RESERVED;
nsync_set_per_thread_waiter_ (w, &waiter_destroy);
waiter_for_thread = w;
}
}
w->flags |= WAITER_IN_USE;
return (w);
}
/* Return an unused waiter struct *w to the free pool. */
void nsync_waiter_free_ (waiter *w) {
ASSERT ((w->flags & WAITER_IN_USE) != 0);
w->flags &= ~WAITER_IN_USE;
if ((w->flags & WAITER_RESERVED) == 0) {
nsync_spin_test_and_set_ (&free_waiters_mu, 1, 1, 0);
dll_make_first (&free_waiters, &w->nw.q);
ATM_STORE_REL (&free_waiters_mu, 0); /* release store */
} }
} }

View file

@ -244,12 +244,8 @@ waiter *nsync_dll_waiter_(struct Dll *e);
: DLL_CONTAINER(struct waiter_s, same_condition, e)) : DLL_CONTAINER(struct waiter_s, same_condition, e))
waiter *nsync_dll_waiter_samecond_(struct Dll *e); waiter *nsync_dll_waiter_samecond_(struct Dll *e);
/* Return a pointer to an unused waiter struct. void nsync_waiter_init_(waiter *);
Ensures that the enclosed timer is stopped and its channel drained. */ void nsync_waiter_destroy_(waiter *);
waiter *nsync_waiter_new_(void);
/* Return an unused waiter struct *w to the free pool. */
void nsync_waiter_free_(waiter *w);
/* ---------- */ /* ---------- */

View file

@ -16,6 +16,7 @@
limitations under the License. limitations under the License.
*/ */
#include "libc/calls/cp.internal.h" #include "libc/calls/cp.internal.h"
#include "libc/errno.h"
#include "libc/intrin/dll.h" #include "libc/intrin/dll.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
@ -169,6 +170,95 @@ static void void_mu_unlock (void *mu) {
nsync_mu_unlock ((nsync_mu *) mu); nsync_mu_unlock ((nsync_mu *) mu);
} }
/* Memory needed for a single nsync_cv_wait_with_deadline_generic() call. */
struct nsync_cv_wait_with_deadline_s {
nsync_cv *pcv;
int sem_outcome;
int is_reader_mu;
uint32_t old_word;
uint32_t remove_count;
void *pmu;
void (*lock) (void *);
nsync_mu *cv_mu;
nsync_time abs_deadline;
nsync_note cancel_note;
waiter w;
};
/* Wait until awoken or timeout, or back out of wait if the thread is being cancelled. */
static int nsync_cv_wait_with_deadline_impl_ (struct nsync_cv_wait_with_deadline_s *c) {
int outcome = 0;
int attempts = 0;
IGNORE_RACES_START ();
while (ATM_LOAD_ACQ (&c->w.nw.waiting) != 0) { /* acquire load */
if (c->sem_outcome == 0) {
c->sem_outcome = nsync_sem_wait_with_cancel_ (&c->w, c->abs_deadline, c->cancel_note);
}
if (c->sem_outcome != 0 && ATM_LOAD (&c->w.nw.waiting) != 0) {
/* A timeout or cancellation occurred, and no wakeup.
Acquire *pcv's spinlock, and confirm. */
c->old_word = nsync_spin_test_and_set_ (&c->pcv->word, CV_SPINLOCK,
CV_SPINLOCK, 0);
/* Check that w wasn't removed from the queue after we
checked above, but before we acquired the spinlock.
The test of remove_count confirms that the waiter *w
is still governed by *pcv's spinlock; otherwise, some
other thread is about to set w.waiting==0. */
if (ATM_LOAD (&c->w.nw.waiting) != 0) {
if (c->remove_count == ATM_LOAD (&c->w.remove_count)) {
uint32_t old_value;
/* still in cv waiter queue */
/* Not woken, so remove *w from cv
queue, and declare a
timeout/cancellation. */
outcome = c->sem_outcome;
dll_remove (&c->pcv->waiters, &c->w.nw.q);
do {
old_value = ATM_LOAD (&c->w.remove_count);
} while (!ATM_CAS (&c->w.remove_count, old_value, old_value+1));
if (dll_is_empty (c->pcv->waiters)) {
c->old_word &= ~(CV_NON_EMPTY);
}
ATM_STORE_REL (&c->w.nw.waiting, 0); /* release store */
}
}
/* Release spinlock. */
ATM_STORE_REL (&c->pcv->word, c->old_word); /* release store */
}
if (ATM_LOAD (&c->w.nw.waiting) != 0) {
/* The delay here causes this thread ultimately to
yield to another that has dequeued this thread, but
has not yet set the waiting field to zero; a
cancellation or timeout may prevent this thread
from blocking above on the semaphore. */
attempts = nsync_spin_delay_ (attempts);
}
}
if (c->cv_mu != NULL && c->w.cv_mu == NULL) { /* waiter was moved to *pmu's queue, and woken. */
/* Requeue on *pmu using existing waiter struct; current thread
is the designated waker. */
nsync_mu_lock_slow_ (c->cv_mu, &c->w, MU_DESIG_WAKER, c->w.l_type);
} else {
/* Traditional case: We've woken from the cv, and need to reacquire *pmu. */
if (c->is_reader_mu) {
nsync_mu_rlock (c->cv_mu);
} else {
(*c->lock) (c->pmu);
}
}
nsync_waiter_destroy_ (&c->w);
IGNORE_RACES_END ();
return (outcome);
}
/* Handle POSIX thread DEFERRED mode cancellation. */
static void nsync_cv_wait_with_deadline_unwind_ (void *arg) {
struct nsync_cv_wait_with_deadline_s *c;
c = (struct nsync_cv_wait_with_deadline_s *)arg;
c->sem_outcome = ECANCELED;
nsync_cv_wait_with_deadline_impl_ (c);
}
/* Atomically release *pmu (which must be held on entry) /* Atomically release *pmu (which must be held on entry)
and block the calling thread on *pcv. Then wait until awakened by a and block the calling thread on *pcv. Then wait until awakened by a
call to nsync_cv_signal() or nsync_cv_broadcast() (or a spurious wakeup), or by the time call to nsync_cv_signal() or nsync_cv_broadcast() (or a spurious wakeup), or by the time
@ -194,33 +284,33 @@ int nsync_cv_wait_with_deadline_generic (nsync_cv *pcv, void *pmu,
void (*lock) (void *), void (*unlock) (void *), void (*lock) (void *), void (*unlock) (void *),
nsync_time abs_deadline, nsync_time abs_deadline,
nsync_note cancel_note) { nsync_note cancel_note) {
nsync_mu *cv_mu = NULL; int outcome;
int is_reader_mu; struct nsync_cv_wait_with_deadline_s c;
uint32_t old_word;
uint32_t remove_count;
int sem_outcome;
unsigned attempts;
int outcome = 0;
waiter *w;
IGNORE_RACES_START (); IGNORE_RACES_START ();
BEGIN_CANCELLATION_POINT;
w = nsync_waiter_new_ (); nsync_waiter_init_ (&c.w);
pthread_cleanup_push((void *)nsync_waiter_free_, w); c.abs_deadline = abs_deadline;
ATM_STORE (&w->nw.waiting, 1); c.cancel_note = cancel_note;
w->cond.f = NULL; /* Not using a conditional critical section. */ c.cv_mu = NULL;
w->cond.v = NULL; c.lock = lock;
w->cond.eq = NULL; c.pcv = pcv;
c.pmu = pmu;
ATM_STORE (&c.w.nw.waiting, 1);
c.w.cond.f = NULL; /* Not using a conditional critical section. */
c.w.cond.v = NULL;
c.w.cond.eq = NULL;
if (lock == &void_mu_lock || if (lock == &void_mu_lock ||
lock == (void (*) (void *)) &nsync_mu_lock || lock == (void (*) (void *)) &nsync_mu_lock ||
lock == (void (*) (void *)) &nsync_mu_rlock) { lock == (void (*) (void *)) &nsync_mu_rlock) {
cv_mu = (nsync_mu *) pmu; c.cv_mu = (nsync_mu *) pmu;
} }
w->cv_mu = cv_mu; /* If *pmu is an nsync_mu, record its address, else record NULL. */ c.w.cv_mu = c.cv_mu; /* If *pmu is an nsync_mu, record its address, else record NULL. */
is_reader_mu = 0; /* If true, an nsync_mu in reader mode. */ c.is_reader_mu = 0; /* If true, an nsync_mu in reader mode. */
if (cv_mu == NULL) { if (c.cv_mu == NULL) {
w->l_type = NULL; c.w.l_type = NULL;
} else { } else {
uint32_t old_mu_word = ATM_LOAD (&cv_mu->word); uint32_t old_mu_word = ATM_LOAD (&c.cv_mu->word);
int is_writer = (old_mu_word & MU_WHELD_IF_NON_ZERO) != 0; int is_writer = (old_mu_word & MU_WHELD_IF_NON_ZERO) != 0;
int is_reader = (old_mu_word & MU_RHELD_IF_NON_ZERO) != 0; int is_reader = (old_mu_word & MU_RHELD_IF_NON_ZERO) != 0;
if (is_writer) { if (is_writer) {
@ -228,95 +318,34 @@ int nsync_cv_wait_with_deadline_generic (nsync_cv *pcv, void *pmu,
nsync_panic_ ("mu held in reader and writer mode simultaneously " nsync_panic_ ("mu held in reader and writer mode simultaneously "
"on entry to nsync_cv_wait_with_deadline()\n"); "on entry to nsync_cv_wait_with_deadline()\n");
} }
w->l_type = nsync_writer_type_; c.w.l_type = nsync_writer_type_;
} else if (is_reader) { } else if (is_reader) {
w->l_type = nsync_reader_type_; c.w.l_type = nsync_reader_type_;
is_reader_mu = 1; c.is_reader_mu = 1;
} else { } else {
nsync_panic_ ("mu not held on entry to nsync_cv_wait_with_deadline()\n"); nsync_panic_ ("mu not held on entry to nsync_cv_wait_with_deadline()\n");
} }
} }
/* acquire spinlock, set non-empty */ /* acquire spinlock, set non-empty */
old_word = nsync_spin_test_and_set_ (&pcv->word, CV_SPINLOCK, CV_SPINLOCK|CV_NON_EMPTY, 0); c.old_word = nsync_spin_test_and_set_ (&pcv->word, CV_SPINLOCK, CV_SPINLOCK|CV_NON_EMPTY, 0);
dll_make_last (&pcv->waiters, &w->nw.q); dll_make_last (&pcv->waiters, &c.w.nw.q);
remove_count = ATM_LOAD (&w->remove_count); c.remove_count = ATM_LOAD (&c.w.remove_count);
/* Release the spin lock. */ /* Release the spin lock. */
ATM_STORE_REL (&pcv->word, old_word|CV_NON_EMPTY); /* release store */ ATM_STORE_REL (&pcv->word, c.old_word|CV_NON_EMPTY); /* release store */
/* Release *pmu. */ /* Release *pmu. */
if (is_reader_mu) { if (c.is_reader_mu) {
nsync_mu_runlock (cv_mu); nsync_mu_runlock (c.cv_mu);
} else { } else {
(*unlock) (pmu); (*unlock) (pmu);
} }
/* wait until awoken or a timeout. */ /* Wait until awoken or a timeout. */
sem_outcome = 0; c.sem_outcome = 0;
attempts = 0; pthread_cleanup_push (nsync_cv_wait_with_deadline_unwind_, &c);
while (ATM_LOAD_ACQ (&w->nw.waiting) != 0) { /* acquire load */ outcome = nsync_cv_wait_with_deadline_impl_ (&c);
if (sem_outcome == 0) { pthread_cleanup_pop (0);
sem_outcome = nsync_sem_wait_with_cancel_ (w, abs_deadline, cancel_note);
}
if (sem_outcome != 0 && ATM_LOAD (&w->nw.waiting) != 0) {
/* A timeout or cancellation occurred, and no wakeup.
Acquire *pcv's spinlock, and confirm. */
old_word = nsync_spin_test_and_set_ (&pcv->word, CV_SPINLOCK,
CV_SPINLOCK, 0);
/* Check that w wasn't removed from the queue after we
checked above, but before we acquired the spinlock.
The test of remove_count confirms that the waiter *w
is still governed by *pcv's spinlock; otherwise, some
other thread is about to set w.waiting==0. */
if (ATM_LOAD (&w->nw.waiting) != 0) {
if (remove_count == ATM_LOAD (&w->remove_count)) {
uint32_t old_value;
/* still in cv waiter queue */
/* Not woken, so remove *w from cv
queue, and declare a
timeout/cancellation. */
outcome = sem_outcome;
dll_remove (&pcv->waiters, &w->nw.q);
do {
old_value = ATM_LOAD (&w->remove_count);
} while (!ATM_CAS (&w->remove_count, old_value, old_value+1));
if (dll_is_empty (pcv->waiters)) {
old_word &= ~(CV_NON_EMPTY);
}
ATM_STORE_REL (&w->nw.waiting, 0); /* release store */
}
}
/* Release spinlock. */
ATM_STORE_REL (&pcv->word, old_word); /* release store */
}
if (ATM_LOAD (&w->nw.waiting) != 0) {
/* The delay here causes this thread ultimately to
yield to another that has dequeued this thread, but
has not yet set the waiting field to zero; a
cancellation or timeout may prevent this thread
from blocking above on the semaphore. */
attempts = nsync_spin_delay_ (attempts);
}
}
if (cv_mu != NULL && w->cv_mu == NULL) { /* waiter was moved to *pmu's queue, and woken. */
/* Requeue on *pmu using existing waiter struct; current thread
is the designated waker. */
nsync_mu_lock_slow_ (cv_mu, w, MU_DESIG_WAKER, w->l_type);
nsync_waiter_free_ (w);
} else {
/* Traditional case: We've woken from the cv, and need to reacquire *pmu. */
nsync_waiter_free_ (w);
if (is_reader_mu) {
nsync_mu_rlock (cv_mu);
} else {
(*lock) (pmu);
}
}
pthread_cleanup_pop(0);
END_CANCELLATION_POINT;
IGNORE_RACES_END (); IGNORE_RACES_END ();
return (outcome); return (outcome);
} }
@ -341,7 +370,7 @@ void nsync_cv_signal (nsync_cv *pcv) {
first_nw = DLL_NSYNC_WAITER (first); first_nw = DLL_NSYNC_WAITER (first);
if ((first_nw->flags & NSYNC_WAITER_FLAG_MUCV) != 0) { if ((first_nw->flags & NSYNC_WAITER_FLAG_MUCV) != 0) {
uint32_t old_value; uint32_t old_value;
do { do {
old_value = ATM_LOAD (&DLL_WAITER (first)->remove_count); old_value = ATM_LOAD (&DLL_WAITER (first)->remove_count);
} while (!ATM_CAS (&DLL_WAITER (first)->remove_count, } while (!ATM_CAS (&DLL_WAITER (first)->remove_count,
old_value, old_value+1)); old_value, old_value+1));
@ -353,7 +382,7 @@ void nsync_cv_signal (nsync_cv *pcv) {
/* If the first waiter is a reader, wake all readers, and /* If the first waiter is a reader, wake all readers, and
if it's possible, one writer. This allows reader-regions if it's possible, one writer. This allows reader-regions
to be added to a monitor without invalidating code in which to be added to a monitor without invalidating code in which
a client has optimized broadcast calls by converting them to a client has optimized broadcast calls by converting them to
signal calls. In particular, we wake a writer when waking signal calls. In particular, we wake a writer when waking
readers because the readers will not invalidate the condition readers because the readers will not invalidate the condition
that motivated the client to call nsync_cv_signal(). But we that motivated the client to call nsync_cv_signal(). But we
@ -382,7 +411,7 @@ void nsync_cv_signal (nsync_cv *pcv) {
dll_remove (&pcv->waiters, p); dll_remove (&pcv->waiters, p);
if ((p_nw->flags & NSYNC_WAITER_FLAG_MUCV) != 0) { if ((p_nw->flags & NSYNC_WAITER_FLAG_MUCV) != 0) {
uint32_t old_value; uint32_t old_value;
do { do {
old_value = ATM_LOAD ( old_value = ATM_LOAD (
&DLL_WAITER (p)->remove_count); &DLL_WAITER (p)->remove_count);
} while (!ATM_CAS (&DLL_WAITER (p)->remove_count, } while (!ATM_CAS (&DLL_WAITER (p)->remove_count,
@ -427,7 +456,7 @@ void nsync_cv_broadcast (nsync_cv *pcv) {
dll_remove (&pcv->waiters, p); dll_remove (&pcv->waiters, p);
if ((p_nw->flags & NSYNC_WAITER_FLAG_MUCV) != 0) { if ((p_nw->flags & NSYNC_WAITER_FLAG_MUCV) != 0) {
uint32_t old_value; uint32_t old_value;
do { do {
old_value = ATM_LOAD (&DLL_WAITER (p)->remove_count); old_value = ATM_LOAD (&DLL_WAITER (p)->remove_count);
} while (!ATM_CAS (&DLL_WAITER (p)->remove_count, } while (!ATM_CAS (&DLL_WAITER (p)->remove_count,
old_value, old_value+1)); old_value, old_value+1));

View file

@ -149,12 +149,12 @@ int nsync_mu_wait_with_deadline (nsync_mu *mu,
lock_type *l_type; lock_type *l_type;
int first_wait; int first_wait;
int condition_is_true; int condition_is_true;
waiter *w; waiter w[1];
int outcome; int outcome;
/* Work out in which mode the lock is held. */ /* Work out in which mode the lock is held. */
uint32_t old_word; uint32_t old_word;
IGNORE_RACES_START (); IGNORE_RACES_START ();
BLOCK_CANCELLATIONS; /* not supported yet */ BLOCK_CANCELLATIONS;
old_word = ATM_LOAD (&mu->word); old_word = ATM_LOAD (&mu->word);
if ((old_word & MU_ANY_LOCK) == 0) { if ((old_word & MU_ANY_LOCK) == 0) {
nsync_panic_ ("nsync_mu not held in some mode when calling " nsync_panic_ ("nsync_mu not held in some mode when calling "
@ -165,12 +165,12 @@ int nsync_mu_wait_with_deadline (nsync_mu *mu,
l_type = nsync_reader_type_; l_type = nsync_reader_type_;
} }
w->tag = 0; /* avoid allocating system resources */
first_wait = 1; /* first time through the loop below. */ first_wait = 1; /* first time through the loop below. */
condition_is_true = (condition == NULL || (*condition) (condition_arg)); condition_is_true = (condition == NULL || (*condition) (condition_arg));
/* Loop until either the condition becomes true, or "outcome" indicates /* Loop until either the condition becomes true, or "outcome" indicates
cancellation or timeout. */ cancellation or timeout. */
w = NULL;
outcome = 0; outcome = 0;
while (outcome == 0 && !condition_is_true) { while (outcome == 0 && !condition_is_true) {
uint32_t has_condition; uint32_t has_condition;
@ -180,8 +180,10 @@ int nsync_mu_wait_with_deadline (nsync_mu *mu,
int sem_outcome; int sem_outcome;
unsigned attempts; unsigned attempts;
int have_lock; int have_lock;
if (w == NULL) {
w = nsync_waiter_new_ (); /* get a waiter struct if we need one. */ /* initialize the waiter if we haven't already */
if (!w->tag) {
nsync_waiter_init_ (w);
} }
/* Prepare to wait. */ /* Prepare to wait. */
@ -259,9 +261,7 @@ int nsync_mu_wait_with_deadline (nsync_mu *mu,
} }
condition_is_true = (condition == NULL || (*condition) (condition_arg)); condition_is_true = (condition == NULL || (*condition) (condition_arg));
} }
if (w != NULL) { nsync_waiter_destroy_ (w);
nsync_waiter_free_ (w); /* free waiter if we allocated one. */
}
if (condition_is_true) { if (condition_is_true) {
outcome = 0; /* condition is true trumps other outcomes. */ outcome = 0; /* condition is true trumps other outcomes. */
} }
@ -319,5 +319,3 @@ void nsync_mu_unlock_without_wakeup (nsync_mu *mu) {
} }
IGNORE_RACES_END (); IGNORE_RACES_END ();
} }

View file

@ -17,6 +17,7 @@
*/ */
#include "libc/calls/blockcancel.internal.h" #include "libc/calls/blockcancel.internal.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/thread/thread.h"
#include "third_party/nsync/atomic.h" #include "third_party/nsync/atomic.h"
#include "third_party/nsync/atomic.internal.h" #include "third_party/nsync/atomic.internal.h"
#include "third_party/nsync/common.internal.h" #include "third_party/nsync/common.internal.h"
@ -36,7 +37,7 @@ int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *),
int count, struct nsync_waitable_s *waitable[]) { int count, struct nsync_waitable_s *waitable[]) {
int ready; int ready;
IGNORE_RACES_START (); IGNORE_RACES_START ();
BLOCK_CANCELLATIONS; /* TODO(jart): Does this need pthread cancellations? */ BLOCK_CANCELLATIONS;
for (ready = 0; ready != count && for (ready = 0; ready != count &&
nsync_time_cmp ((*waitable[ready]->funcs->ready_time) ( nsync_time_cmp ((*waitable[ready]->funcs->ready_time) (
waitable[ready]->v, NULL), waitable[ready]->v, NULL),
@ -48,7 +49,8 @@ int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *),
int unlocked = 0; int unlocked = 0;
int j; int j;
int enqueued = 1; int enqueued = 1;
waiter *w = nsync_waiter_new_ (); waiter w[1];
nsync_waiter_init_ (w);
struct nsync_waiter_s nw_set[4]; struct nsync_waiter_s nw_set[4];
struct nsync_waiter_s *nw = nw_set; struct nsync_waiter_s *nw = nw_set;
if (count > (int) (sizeof (nw_set) / sizeof (nw_set[0]))) { if (count > (int) (sizeof (nw_set) / sizeof (nw_set[0]))) {
@ -95,10 +97,10 @@ int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *),
} }
} }
nsync_waiter_destroy_ (w);
if (nw != nw_set) { if (nw != nw_set) {
free (nw); free (nw);
} }
nsync_waiter_free_ (w);
if (unlocked) { if (unlocked) {
(*lock) (mu); (*lock) (mu);
} }
@ -107,5 +109,3 @@ int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *),
IGNORE_RACES_END (); IGNORE_RACES_END ();
return (ready); return (ready);
} }

View file

@ -103,7 +103,8 @@ void nsync_mu_lock_slow_ (nsync_mu *mu, waiter *w, uint32_t clear, lock_type *l_
/* wait until awoken. */ /* wait until awoken. */
while (ATM_LOAD_ACQ (&w->nw.waiting) != 0) { /* acquire load */ while (ATM_LOAD_ACQ (&w->nw.waiting) != 0) { /* acquire load */
nsync_mu_semaphore_p (&w->sem); /* This can only return 0 or ECANCELED. */
ASSERT (nsync_mu_semaphore_p (&w->sem) == 0);
} }
wait_count++; wait_count++;
/* If the thread has been woken more than this many /* If the thread has been woken more than this many
@ -155,9 +156,10 @@ void nsync_mu_lock (nsync_mu *mu) {
if ((old_word&MU_WZERO_TO_ACQUIRE) != 0 || if ((old_word&MU_WZERO_TO_ACQUIRE) != 0 ||
!ATM_CAS_ACQ (&mu->word, old_word, !ATM_CAS_ACQ (&mu->word, old_word,
(old_word+MU_WADD_TO_ACQUIRE) & ~MU_WCLEAR_ON_ACQUIRE)) { (old_word+MU_WADD_TO_ACQUIRE) & ~MU_WCLEAR_ON_ACQUIRE)) {
waiter *w = nsync_waiter_new_ (); waiter w;
nsync_mu_lock_slow_ (mu, w, 0, nsync_writer_type_); nsync_waiter_init_ (&w);
nsync_waiter_free_ (w); nsync_mu_lock_slow_ (mu, &w, 0, nsync_writer_type_);
nsync_waiter_destroy_ (&w);
} }
} }
IGNORE_RACES_END (); IGNORE_RACES_END ();
@ -190,9 +192,10 @@ void nsync_mu_rlock (nsync_mu *mu) {
if ((old_word&MU_RZERO_TO_ACQUIRE) != 0 || if ((old_word&MU_RZERO_TO_ACQUIRE) != 0 ||
!ATM_CAS_ACQ (&mu->word, old_word, !ATM_CAS_ACQ (&mu->word, old_word,
(old_word+MU_RADD_TO_ACQUIRE) & ~MU_RCLEAR_ON_ACQUIRE)) { (old_word+MU_RADD_TO_ACQUIRE) & ~MU_RCLEAR_ON_ACQUIRE)) {
waiter *w = nsync_waiter_new_ (); waiter w;
nsync_mu_lock_slow_ (mu, w, 0, nsync_reader_type_); nsync_waiter_init_ (&w);
nsync_waiter_free_ (w); nsync_mu_lock_slow_ (mu, &w, 0, nsync_reader_type_);
nsync_waiter_destroy_ (&w);
} }
} }
IGNORE_RACES_END (); IGNORE_RACES_END ();

View file

@ -16,6 +16,7 @@
limitations under the License. limitations under the License.
*/ */
#include "third_party/nsync/mu_semaphore.h" #include "third_party/nsync/mu_semaphore.h"
#include "libc/calls/cp.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "third_party/nsync/mu_semaphore.internal.h" #include "third_party/nsync/mu_semaphore.internal.h"
@ -36,28 +37,45 @@ void nsync_mu_semaphore_init (nsync_semaphore *s) {
} }
} }
/* Releases system resources associated with *s. */
void nsync_mu_semaphore_destroy (nsync_semaphore *s) {
if (IsXnuSilicon ()) {
return nsync_mu_semaphore_destroy_gcd (s);
} else if (IsNetbsd ()) {
return nsync_mu_semaphore_destroy_sem (s);
}
}
/* Wait until the count of *s exceeds 0, and decrement it. */ /* Wait until the count of *s exceeds 0, and decrement it. */
errno_t nsync_mu_semaphore_p (nsync_semaphore *s) { errno_t nsync_mu_semaphore_p (nsync_semaphore *s) {
errno_t err;
BEGIN_CANCELLATION_POINT;
if (IsXnuSilicon ()) { if (IsXnuSilicon ()) {
return nsync_mu_semaphore_p_gcd (s); err = nsync_mu_semaphore_p_gcd (s);
} else if (IsNetbsd ()) { } else if (IsNetbsd ()) {
return nsync_mu_semaphore_p_sem (s); err = nsync_mu_semaphore_p_sem (s);
} else { } else {
return nsync_mu_semaphore_p_futex (s); err = nsync_mu_semaphore_p_futex (s);
} }
END_CANCELLATION_POINT;
return err;
} }
/* Wait until one of: /* Wait until one of:
the count of *s is non-zero, in which case decrement *s and return 0; the count of *s is non-zero, in which case decrement *s and return 0;
or abs_deadline expires, in which case return ETIMEDOUT. */ or abs_deadline expires, in which case return ETIMEDOUT. */
errno_t nsync_mu_semaphore_p_with_deadline (nsync_semaphore *s, nsync_time abs_deadline) { errno_t nsync_mu_semaphore_p_with_deadline (nsync_semaphore *s, nsync_time abs_deadline) {
errno_t err;
BEGIN_CANCELLATION_POINT;
if (IsXnuSilicon ()) { if (IsXnuSilicon ()) {
return nsync_mu_semaphore_p_with_deadline_gcd (s, abs_deadline); err = nsync_mu_semaphore_p_with_deadline_gcd (s, abs_deadline);
} else if (IsNetbsd ()) { } else if (IsNetbsd ()) {
return nsync_mu_semaphore_p_with_deadline_sem (s, abs_deadline); err = nsync_mu_semaphore_p_with_deadline_sem (s, abs_deadline);
} else { } else {
return nsync_mu_semaphore_p_with_deadline_futex (s, abs_deadline); err = nsync_mu_semaphore_p_with_deadline_futex (s, abs_deadline);
} }
END_CANCELLATION_POINT;
return err;
} }
/* Ensure that the count of *s is at least 1. */ /* Ensure that the count of *s is at least 1. */

View file

@ -11,6 +11,9 @@ typedef struct nsync_semaphore_s_ {
/* Initialize *s; the initial value is 0. */ /* Initialize *s; the initial value is 0. */
void nsync_mu_semaphore_init(nsync_semaphore *s); void nsync_mu_semaphore_init(nsync_semaphore *s);
/* Releases system resources associated with *s. */
void nsync_mu_semaphore_destroy(nsync_semaphore *s);
/* Wait until the count of *s exceeds 0, and decrement it. */ /* Wait until the count of *s exceeds 0, and decrement it. */
errno_t nsync_mu_semaphore_p(nsync_semaphore *s); errno_t nsync_mu_semaphore_p(nsync_semaphore *s);

View file

@ -11,11 +11,13 @@ errno_t nsync_mu_semaphore_p_with_deadline_futex(nsync_semaphore *, nsync_time);
void nsync_mu_semaphore_v_futex(nsync_semaphore *); void nsync_mu_semaphore_v_futex(nsync_semaphore *);
void nsync_mu_semaphore_init_sem(nsync_semaphore *); void nsync_mu_semaphore_init_sem(nsync_semaphore *);
void nsync_mu_semaphore_destroy_sem(nsync_semaphore *);
errno_t nsync_mu_semaphore_p_sem(nsync_semaphore *); errno_t nsync_mu_semaphore_p_sem(nsync_semaphore *);
errno_t nsync_mu_semaphore_p_with_deadline_sem(nsync_semaphore *, nsync_time); errno_t nsync_mu_semaphore_p_with_deadline_sem(nsync_semaphore *, nsync_time);
void nsync_mu_semaphore_v_sem(nsync_semaphore *); void nsync_mu_semaphore_v_sem(nsync_semaphore *);
void nsync_mu_semaphore_init_gcd(nsync_semaphore *); void nsync_mu_semaphore_init_gcd(nsync_semaphore *);
void nsync_mu_semaphore_destroy_gcd(nsync_semaphore *);
errno_t nsync_mu_semaphore_p_gcd(nsync_semaphore *); errno_t nsync_mu_semaphore_p_gcd(nsync_semaphore *);
errno_t nsync_mu_semaphore_p_with_deadline_gcd(nsync_semaphore *, nsync_time); errno_t nsync_mu_semaphore_p_with_deadline_gcd(nsync_semaphore *, nsync_time);
void nsync_mu_semaphore_v_gcd(nsync_semaphore *); void nsync_mu_semaphore_v_gcd(nsync_semaphore *);

View file

@ -41,6 +41,11 @@ static dispatch_semaphore_t dispatch_semaphore_create(long count) {
return (ds); return (ds);
} }
static void dispatch_release (dispatch_semaphore_t ds) {
__syslib->dispatch_release (ds);
STRACE ("dispatch_release(%#lx)", ds);
}
static long dispatch_semaphore_wait (dispatch_semaphore_t ds, static long dispatch_semaphore_wait (dispatch_semaphore_t ds,
dispatch_time_t dt) { dispatch_time_t dt) {
long rc = __syslib->dispatch_semaphore_wait (ds, dt); long rc = __syslib->dispatch_semaphore_wait (ds, dt);
@ -65,6 +70,11 @@ void nsync_mu_semaphore_init_gcd (nsync_semaphore *s) {
*(dispatch_semaphore_t *)s = dispatch_semaphore_create (0); *(dispatch_semaphore_t *)s = dispatch_semaphore_create (0);
} }
/* Releases system resources associated with *s. */
void nsync_mu_semaphore_destroy_gcd (nsync_semaphore *s) {
dispatch_release (*(dispatch_semaphore_t *)s);
}
/* Wait until the count of *s exceeds 0, and decrement it. */ /* Wait until the count of *s exceeds 0, and decrement it. */
errno_t nsync_mu_semaphore_p_gcd (nsync_semaphore *s) { errno_t nsync_mu_semaphore_p_gcd (nsync_semaphore *s) {
dispatch_semaphore_wait (*(dispatch_semaphore_t *)s, dispatch_semaphore_wait (*(dispatch_semaphore_t *)s,

View file

@ -1,5 +1,5 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ /*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney Copyright 2022 Justine Alexandra Roberts Tunney
@ -25,6 +25,7 @@
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/f.h" #include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
#include "third_party/nsync/mu_semaphore.h" #include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/time.h" #include "third_party/nsync/time.h"
// clang-format off // clang-format off
@ -44,14 +45,19 @@ static nsync_semaphore *sem_big_enough_for_sem = (nsync_semaphore *) (uintptr_t)
/* Initialize *s; the initial value is 0. */ /* Initialize *s; the initial value is 0. */
void nsync_mu_semaphore_init_sem (nsync_semaphore *s) { void nsync_mu_semaphore_init_sem (nsync_semaphore *s) {
int newfd;
struct sem *f = (struct sem *) s; struct sem *f = (struct sem *) s;
f->id = 0; f->id = 0;
ASSERT (!sys_sem_init (0, &f->id)); ASSERT (!sys_sem_init (0, &f->id));
STRACE ("sem_init(0, [%ld]) → 0", f->id); STRACE ("sem_init(0, [%ld]) → 0", f->id);
ASSERT ((newfd = __sys_fcntl (f->id, F_DUPFD_CLOEXEC, 100)) != -1); ASSERT (__sys_fcntl (f->id, F_SETFD, FD_CLOEXEC) == 0); // ouch
ASSERT (!sys_sem_destroy (f->id)); }
f->id = newfd;
/* Releases system resources associated with *s. */
void nsync_mu_semaphore_destroy_sem (nsync_semaphore *s) {
int rc;
struct sem *f = (struct sem *) s;
ASSERT (!(rc = sys_sem_destroy (f->id)));
STRACE ("sem_destroy(%ld) → %d", rc);
} }
/* Wait until the count of *s exceeds 0, and decrement it. */ /* Wait until the count of *s exceeds 0, and decrement it. */
@ -60,9 +66,7 @@ errno_t nsync_mu_semaphore_p_sem (nsync_semaphore *s) {
errno_t result; errno_t result;
struct sem *f = (struct sem *) s; struct sem *f = (struct sem *) s;
e = errno; e = errno;
BEGIN_CANCELLATION_POINT;
rc = sys_sem_wait (f->id); rc = sys_sem_wait (f->id);
END_CANCELLATION_POINT;
STRACE ("sem_wait(%ld) → %d% m", f->id, rc); STRACE ("sem_wait(%ld) → %d% m", f->id, rc);
if (!rc) { if (!rc) {
result = 0; result = 0;
@ -82,9 +86,7 @@ errno_t nsync_mu_semaphore_p_with_deadline_sem (nsync_semaphore *s, nsync_time a
errno_t result; errno_t result;
struct sem *f = (struct sem *) s; struct sem *f = (struct sem *) s;
e = errno; e = errno;
BEGIN_CANCELLATION_POINT;
rc = sys_sem_timedwait (f->id, &abs_deadline); rc = sys_sem_timedwait (f->id, &abs_deadline);
END_CANCELLATION_POINT;
STRACE ("sem_timedwait(%ld, %s) → %d% m", f->id, STRACE ("sem_timedwait(%ld, %s) → %d% m", f->id,
DescribeTimespec(0, &abs_deadline), rc); DescribeTimespec(0, &abs_deadline), rc);
if (!rc) { if (!rc) {

View file

@ -17,9 +17,9 @@
*/ */
#include "third_party/nsync/cv.h" #include "third_party/nsync/cv.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "third_party/nsync/debug.h" #include "third_party/nsync/debug.h"
#include "third_party/nsync/mu.h" #include "third_party/nsync/mu.h"

View file

@ -15,7 +15,7 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
#include "libc/fmt/fmt.h" #include "libc/stdio/stdio.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "third_party/nsync/array.internal.h" #include "third_party/nsync/array.internal.h"
#include "third_party/nsync/cv.h" #include "third_party/nsync/cv.h"

View file

@ -16,9 +16,8 @@
limitations under the License. limitations under the License.
*/ */
#include "libc/intrin/dll.h" #include "libc/intrin/dll.h"
#include "libc/fmt/fmt.h"
#include "libc/intrin/dll.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "third_party/nsync/array.internal.h" #include "third_party/nsync/array.internal.h"
#include "third_party/nsync/testing/smprintf.h" #include "third_party/nsync/testing/smprintf.h"

View file

@ -15,7 +15,7 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
#include "libc/fmt/fmt.h" #include "libc/stdio/stdio.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "third_party/nsync/array.internal.h" #include "third_party/nsync/array.internal.h"
#include "third_party/nsync/heap.internal.h" #include "third_party/nsync/heap.internal.h"

View file

@ -15,10 +15,10 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
#include "libc/fmt/fmt.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "third_party/nsync/testing/smprintf.h" #include "third_party/nsync/testing/smprintf.h"
#include "libc/mem/mem.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
// clang-format off // clang-format off
char *smprintf (const char *fmt, ...) { char *smprintf (const char *fmt, ...) {

View file

@ -19,7 +19,6 @@
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/fmt/leb128.h" #include "libc/fmt/leb128.h"
#include "libc/intrin/atomic.h" #include "libc/intrin/atomic.h"
#include "libc/intrin/kmalloc.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/nexgen32e/crc32.h" #include "libc/nexgen32e/crc32.h"
#include "libc/runtime/internal.h" #include "libc/runtime/internal.h"
@ -53,7 +52,7 @@ void *xloadzd(_Atomic(void *) *a, const void *p, size_t n, size_t m, size_t c,
free(q); free(q);
return 0; return 0;
} }
if (!(r = kmalloc(c * z))) { if (!(r = malloc(c * z))) {
free(q); free(q);
return 0; return 0;
} }

View file

@ -264,9 +264,8 @@ void StartTcpServer(void) {
INFOF("listening on tcp:%s", DescribeAddress(&g_servaddr)); INFOF("listening on tcp:%s", DescribeAddress(&g_servaddr));
if (g_sendready) { if (g_sendready) {
printf("ready %hu\n", ntohs(g_servaddr.sin_port)); printf("ready %hu\n", ntohs(g_servaddr.sin_port));
fflush(stdout); close(1);
fclose(stdout); dup2(g_bogusfd, 1);
dup2(g_bogusfd, fileno(stdout));
} }
} }
@ -698,6 +697,7 @@ WaitAgain:
} }
void HandleClient(void) { void HandleClient(void) {
struct stat st;
struct Client *client; struct Client *client;
client = calloc(1, sizeof(struct Client)); client = calloc(1, sizeof(struct Client));
client->addrsize = sizeof(client->addr); client->addrsize = sizeof(client->addr);
@ -720,6 +720,9 @@ void HandleClient(void) {
WARNF("poll failed %m"); WARNF("poll failed %m");
} }
} }
if (fstat(2, &st) != -1 && st.st_size > kLogMaxBytes) {
ftruncate(2, 0); // auto rotate log
}
sigset_t mask; sigset_t mask;
pthread_attr_t attr; pthread_attr_t attr;
sigfillset(&mask); sigfillset(&mask);
@ -749,17 +752,18 @@ int Serve(void) {
} }
void Daemonize(void) { void Daemonize(void) {
VERBF("Daemonize");
struct stat st;
if (fork() > 0) _exit(0); if (fork() > 0) _exit(0);
setsid(); setsid();
if (fork() > 0) _exit(0); if (fork() > 0) _exit(0);
dup2(g_bogusfd, fileno(stdin)); dup2(g_bogusfd, 0);
if (!g_sendready) dup2(g_bogusfd, fileno(stdout)); if (!g_sendready) dup2(g_bogusfd, 1);
freopen(kLogFile, "ae", stderr); close(2);
if (fstat(fileno(stderr), &st) != -1 && st.st_size > kLogMaxBytes) { open(kLogFile, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0644);
ftruncate(fileno(stderr), 0); extern long __klog_handle;
if (__klog_handle > 0) {
close(__klog_handle);
} }
__klog_handle = 2;
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@ -779,8 +783,8 @@ int main(int argc, char *argv[]) {
} else { } else {
g_bogusfd = open("/dev/zero", O_RDONLY | O_CLOEXEC); g_bogusfd = open("/dev/zero", O_RDONLY | O_CLOEXEC);
} }
if (g_daemonize) Daemonize();
mkdir("o", 0700); mkdir("o", 0700);
if (g_daemonize) Daemonize();
Serve(); Serve();
free(g_psk); free(g_psk);
#if IsModeDbg() #if IsModeDbg()