Use *NSYNC for POSIX threads locking APIs

Condition variables, barriers, and r/w locks now work very well.
This commit is contained in:
Justine Tunney 2022-09-11 11:02:07 -07:00
parent 3de35e196c
commit b5cb71ab84
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
197 changed files with 3734 additions and 3817 deletions

View file

@ -122,6 +122,7 @@ include libc/vga/vga.mk #─┘
include libc/calls/calls.mk #─┐
include libc/runtime/runtime.mk # ├──SYSTEMS RUNTIME
include libc/crt/crt.mk # │ You can issue system calls
include third_party/nsync/nsync.mk # │
include third_party/dlmalloc/dlmalloc.mk #─┘
include libc/mem/mem.mk #─┐
include libc/zipos/zipos.mk # ├──DYNAMIC RUNTIME
@ -299,6 +300,7 @@ COSMOPOLITAN_OBJECTS = \
LIBC_MEM \
THIRD_PARTY_DLMALLOC \
LIBC_RUNTIME \
THIRD_PARTY_NSYNC \
LIBC_ELF \
LIBC_CALLS \
LIBC_SYSV_CALLS \
@ -338,6 +340,7 @@ COSMOPOLITAN_HEADERS = \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
THIRD_PARTY_NSYNC \
THIRD_PARTY_XED \
LIBC_STR \
LIBC_SYSV \

View file

@ -179,7 +179,7 @@ struct ElfPhdr {
extern char ehdr[];
extern char _end[];
static void *syscall;
static void *syscall_;
static char relocated;
static struct PathSearcher ps;
extern char __syscall_loader[];
@ -275,7 +275,7 @@ __attribute__((__noreturn__)) static void Exit(int rc, int os) {
asm volatile("call\t*%2"
: /* no outputs */
: "a"((IsLinux() ? 60 : 1) | (IsXnu() ? 0x2000000 : 0)), "D"(rc),
"rm"(syscall)
"rm"(syscall_)
: "memory");
__builtin_unreachable();
}
@ -285,7 +285,7 @@ static void Close(int fd, int os) {
asm volatile("call\t*%4"
: "=a"(ax), "=D"(di)
: "0"((IsLinux() ? 3 : 6) | (IsXnu() ? 0x2000000 : 0)), "1"(fd),
"rm"(syscall)
"rm"(syscall_)
: "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc");
}
@ -295,7 +295,7 @@ static int Read(int fd, void *data, int size, int os) {
asm volatile("call\t*%8"
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
: "0"((IsLinux() ? 0 : 3) | (IsXnu() ? 0x2000000 : 0)), "1"(fd),
"2"(data), "3"(size), "rm"(syscall)
"2"(data), "3"(size), "rm"(syscall_)
: "rcx", "r8", "r9", "r10", "r11", "memory");
return ax;
}
@ -306,7 +306,7 @@ static void Write(int fd, const void *data, int size, int os) {
asm volatile("call\t*%8"
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
: "0"((IsLinux() ? 1 : 4) | (IsXnu() ? 0x2000000 : 0)), "1"(fd),
"2"(data), "3"(size), "rm"(syscall)
"2"(data), "3"(size), "rm"(syscall_)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
}
@ -315,7 +315,7 @@ static void Execve(const char *prog, char **argv, char **envp, int os) {
asm volatile("call\t*%8"
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
: "0"(59 | (IsXnu() ? 0x2000000 : 0)), "1"(prog), "2"(argv),
"3"(envp), "rm"(syscall)
"3"(envp), "rm"(syscall_)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
}
@ -325,7 +325,7 @@ static int Access(const char *path, int mode, int os) {
asm volatile("call\t*%7"
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
: "0"((IsLinux() ? 21 : 33) | (IsXnu() ? 0x2000000 : 0)),
"1"(path), "2"(mode), "rm"(syscall)
"1"(path), "2"(mode), "rm"(syscall_)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
return ax;
}
@ -338,7 +338,7 @@ static int Msyscall(long p, long n, int os) {
} else {
asm volatile("call\t*%6"
: "=a"(ax), "=D"(di), "=S"(si)
: "0"(37), "1"(p), "2"(n), "rm"(syscall)
: "0"(37), "1"(p), "2"(n), "rm"(syscall_)
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc");
return ax;
}
@ -350,7 +350,7 @@ static int Open(const char *path, int flags, int mode, int os) {
asm volatile("call\t*%8"
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
: "0"((IsLinux() ? 2 : 5) | (IsXnu() ? 0x2000000 : 0)),
"1"(path), "2"(flags), "3"(mode), "rm"(syscall)
"1"(path), "2"(flags), "3"(mode), "rm"(syscall_)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
return ax;
}
@ -369,7 +369,7 @@ __attribute__((__noinline__)) static long Mmap(long addr, long size, int prot,
"pop\t%%r9"
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx), "+r"(flags_),
"+r"(fd_), "+r"(off_)
: "rm"(syscall),
: "rm"(syscall_),
"0"((IsLinux() ? 9
: IsFreebsd() ? 477
: 197) |
@ -589,7 +589,7 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
// since it probably means a userspace program executed this loader
// and passed us a custom syscall function earlier.
if (Msyscall(code, codesize, os) != -1) {
syscall = 0;
syscall_ = 0;
}
#if TROUBLESHOOT
@ -600,7 +600,7 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
// to extend the behavior of this loader in the future. we don't need
// to clear the xmm registers since the ape loader should be compiled
// with the -mgeneral-regs-only flag.
register void *r8 asm("r8") = syscall;
register void *r8 asm("r8") = syscall_;
asm volatile("xor\t%%eax,%%eax\n\t"
"xor\t%%r9d,%%r9d\n\t"
"xor\t%%r10d,%%r10d\n\t"
@ -660,9 +660,9 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl,
// get syscall function pointer
if (handoff && handoff->systemcall) {
syscall = handoff->systemcall;
syscall_ = handoff->systemcall;
} else {
syscall = __syscall_loader;
syscall_ = __syscall_loader;
}
if (handoff) {

View file

@ -80,6 +80,7 @@ EXAMPLES_DIRECTDEPS = \
THIRD_PARTY_LUA \
THIRD_PARTY_MBEDTLS \
THIRD_PARTY_MUSL \
THIRD_PARTY_NSYNC \
THIRD_PARTY_QUICKJS \
THIRD_PARTY_STB \
THIRD_PARTY_XED \

View file

@ -8,6 +8,7 @@
*/
#endif
#include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/sigset.h"
@ -18,14 +19,11 @@
#include "libc/fmt/itoa.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/kprintf.h"
#include "libc/thread/thread.h"
#include "libc/intrin/wait0.internal.h"
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/thread/tls.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
@ -48,6 +46,9 @@
#include "libc/sysv/consts/sol.h"
#include "libc/sysv/consts/tcp.h"
#include "libc/thread/spawn.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "libc/thread/wait0.internal.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
#include "net/http/http.h"
@ -104,11 +105,11 @@
"Cache-Control: private; max-age=0\r\n"
int threads;
_Atomic(int) workers;
_Atomic(int) messages;
_Atomic(int) listening;
_Atomic(int) connections;
_Atomic(int) closingtime;
atomic_int workers;
atomic_int messages;
atomic_int listening;
atomic_int connections;
atomic_int closingtime;
const char *volatile status;
void *Worker(void *id) {

View file

@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_ATOMIC_H_
#define COSMOPOLITAN_LIBC_ATOMIC_H_
#include "libc/inttypes.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_

View file

@ -16,35 +16,15 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/futex.internal.h"
#include "libc/thread/thread.h"
#include "libc/sysv/consts/futex.h"
int _futex(void *, int, int, struct timespec *) hidden;
int _futex_wait(void *addr, int expect, char pshared,
struct timespec *timeout) {
int op, ax, pf;
if (IsLinux() || IsOpenbsd()) {
pf = pshared == PTHREAD_PROCESS_PRIVATE ? FUTEX_PRIVATE_FLAG : 0;
op = FUTEX_WAIT | pf;
ax = _futex(addr, op, expect, timeout);
if (SupportsLinux() && pf && ax == -ENOSYS) {
// RHEL5 doesn't support FUTEX_PRIVATE_FLAG
op = FUTEX_WAIT;
ax = _futex(addr, op, expect, timeout);
}
if (IsOpenbsd() && ax > 0) ax = -ax; // yes openbsd does this w/o cf
STRACE("futex(%t, %s, %d, %s) → %s", addr, DescribeFutexOp(op), expect,
DescribeTimespec(0, timeout), DescribeFutexResult(ax));
return ax;
} else {
return pthread_yield();
/**
* Compares two nanosecond timestamps.
*/
int _timespec_cmp(struct timespec a, struct timespec b) {
int cmp;
if (!(cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec))) {
cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec);
}
return cmp;
}

View file

@ -16,7 +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/sysv/consts/clock.h"
#include "libc/sysv/consts/nr.h"
@ -26,6 +25,6 @@ struct timespec _timespec_real(void) {
int ax, dx;
struct timespec ts;
ax = clock_gettime(CLOCK_REALTIME_FAST, &ts);
assert(!ax);
if (ax) notpossible;
return ts;
}

View file

@ -21,12 +21,12 @@
/**
* Subtracts two nanosecond timestamps.
*/
struct timespec _timespec_sub(struct timespec x, struct timespec y) {
x.tv_sec -= y.tv_sec;
x.tv_nsec -= y.tv_nsec;
if (x.tv_nsec < 0) {
x.tv_nsec += 1000000000;
x.tv_sec -= 1;
struct timespec _timespec_sub(struct timespec a, struct timespec b) {
a.tv_sec -= b.tv_sec;
if (a.tv_nsec < b.tv_nsec) {
a.tv_nsec += 1000000000;
a.tv_sec--;
}
return x;
a.tv_nsec -= b.tv_nsec;
return a;
}

View file

@ -30,5 +30,5 @@ void(__sig_unlock)(void) {
}
__attribute__((__constructor__)) static void init(void) {
__sig_lock_obj.type = PTHREAD_MUTEX_RECURSIVE;
__sig_lock_obj._type = PTHREAD_MUTEX_RECURSIVE;
}

View file

@ -17,6 +17,7 @@ int utimensat(int, const char *, const struct timespec[2], int);
int timespec_get(struct timespec *, int);
int timespec_getres(struct timespec *, int);
int _timespec_cmp(struct timespec, struct timespec) pureconst;
bool _timespec_eq(struct timespec, struct timespec) pureconst;
bool _timespec_gte(struct timespec, struct timespec) pureconst;
int64_t _timespec_tomicros(struct timespec) pureconst;

View file

@ -25,6 +25,7 @@
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/asancodes.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/leaky.internal.h"
#include "libc/intrin/likely.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/intrin/nomultics.internal.h"
@ -170,7 +171,7 @@ struct ReportOriginHeap {
};
static int __asan_noreentry;
static pthread_mutex_t __asan_lock;
static pthread_spinlock_t __asan_lock;
static struct AsanMorgue __asan_morgue;
#define __asan_unreachable() \
@ -867,25 +868,25 @@ dontdiscard __asan_die_f *__asan_report_memory_fault(void *addr, int size,
void *__asan_morgue_add(void *p) {
int i;
void *r;
if (__threaded) pthread_mutex_lock(&__asan_lock);
if (__threaded) pthread_spin_lock(&__asan_lock);
i = __asan_morgue.i++ & (ARRAYLEN(__asan_morgue.p) - 1);
r = __asan_morgue.p[i];
__asan_morgue.p[i] = p;
if (__threaded) pthread_mutex_unlock(&__asan_lock);
if (__threaded) pthread_spin_unlock(&__asan_lock);
return r;
}
static void __asan_morgue_flush(void) {
int i;
void *p;
if (__threaded) pthread_mutex_lock(&__asan_lock);
if (__threaded) pthread_spin_lock(&__asan_lock);
for (i = 0; i < ARRAYLEN(__asan_morgue.p); ++i) {
if (__asan_morgue.p[i] && weaken(dlfree)) {
weaken(dlfree)(__asan_morgue.p[i]);
}
__asan_morgue.p[i] = 0;
}
if (__threaded) pthread_mutex_unlock(&__asan_lock);
if (__threaded) pthread_spin_unlock(&__asan_lock);
}
static size_t __asan_user_size(size_t n) {
@ -1052,6 +1053,30 @@ int __asan_print_trace(void *p) {
return 0;
}
// Returns true if `p` was allocated by an IGNORE_LEAKS(function).
int __asan_is_leaky(void *p) {
int sym;
size_t c, i, n;
intptr_t f, *l;
struct AsanExtra *e;
struct SymbolTable *st;
if (!weaken(GetSymbolTable)) notpossible;
if (!(e = __asan_get_extra(p, &c))) return 0;
if (!__asan_read48(e->size, &n)) return 0;
if (!__asan_is_mapped((((intptr_t)p >> 3) + 0x7fff8000) >> 16)) return 0;
if (!(st = GetSymbolTable())) return 0;
for (i = 0; i < ARRAYLEN(e->bt.p) && e->bt.p[i]; ++i) {
if ((sym = weaken(__get_symbol)(st, e->bt.p[i])) == -1) continue;
f = st->addr_base + st->symbols[sym].x;
for (l = _leaky_start; l < _leaky_end; ++l) {
if (f == *l) {
return 1;
}
}
}
return 0;
}
static void __asan_deallocate(char *p, long kind) {
size_t c, n;
struct AsanExtra *e;

View file

@ -3,6 +3,7 @@
#include "libc/calls/struct/iovec.h"
#include "libc/intrin/asancodes.h"
#include "libc/macros.internal.h"
#include "libc/thread/thread.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -29,6 +30,7 @@ struct AsanFault __asan_check(const void *, long) nosideeffect;
void __asan_free(void *);
void *__asan_malloc(size_t);
int __asan_is_leaky(void *);
int __asan_malloc_trim(size_t);
int __asan_print_trace(void *);
void *__asan_calloc(size_t, size_t);

View file

@ -17,7 +17,7 @@ const char *DescribeCapability(char[20], int);
const char *DescribeClockName(char[32], int);
const char *DescribeDirfd(char[12], int);
const char *DescribeFrame(char[32], int);
const char *DescribeFutexOp(int);
const char *DescribeFutexOp(char[64], int);
const char *DescribeFutexResult(char[12], int);
const char *DescribeHow(char[12], int);
const char *DescribeMapFlags(char[64], int);
@ -60,6 +60,7 @@ const char *DescribeWhence(char[12], int);
#define DescribeClockName(x) DescribeClockName(alloca(32), x)
#define DescribeDirfd(x) DescribeDirfd(alloca(12), x)
#define DescribeFrame(x) DescribeFrame(alloca(32), x)
#define DescribeFutexOp(x) DescribeFutexOp(alloca(64), x)
#define DescribeFutexResult(x) DescribeFutexResult(alloca(12), x)
#define DescribeHow(x) DescribeHow(alloca(12), x)
#define DescribeMapFlags(x) DescribeMapFlags(alloca(64), x)

View file

@ -16,16 +16,47 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/itoa.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/futex.h"
const char *DescribeFutexOp(int x) {
if (x == FUTEX_WAIT) return "FUTEX_WAIT";
if (x == FUTEX_WAKE) return "FUTEX_WAKE";
if (x == FUTEX_REQUEUE) return "FUTEX_REQUEUE";
// order matters (the private bit might be zero)
if (x == FUTEX_WAIT_PRIVATE) return "FUTEX_WAIT_PRIVATE";
if (x == FUTEX_WAKE_PRIVATE) return "FUTEX_WAKE_PRIVATE";
if (x == FUTEX_REQUEUE_PRIVATE) return "FUTEX_REQUEUE_PRIVATE";
return "FUTEX_???";
const char *(DescribeFutexOp)(char buf[64], int x) {
bool priv = false;
if (x & FUTEX_PRIVATE_FLAG) {
priv = true;
x &= ~FUTEX_PRIVATE_FLAG;
}
bool real = false;
if (x & FUTEX_CLOCK_REALTIME) {
real = true;
x &= ~FUTEX_CLOCK_REALTIME;
}
char *p = buf;
if (x == FUTEX_WAIT) {
p = stpcpy(p, "FUTEX_WAIT");
} else if (x == FUTEX_WAKE) {
p = stpcpy(p, "FUTEX_WAKE");
} else if (x == FUTEX_REQUEUE) {
p = stpcpy(p, "FUTEX_REQUEUE");
} else if (x == FUTEX_WAIT_BITSET) {
p = stpcpy(p, "FUTEX_WAIT_BITSET");
} else {
p = stpcpy(p, "FUTEX_");
p = FormatUint32(p, x);
}
if (priv) {
p = stpcpy(p, "_PRIVATE");
}
if (real) {
p = stpcpy(p, "|FUTEX_CLOCK_REALTIME");
}
return buf;
}

View file

@ -37,11 +37,12 @@ static void _mapframe(void *p) {
if ((dm = sys_mmap(p, G, prot, flags, -1, 0)).addr != p) {
notpossible;
}
if (TrackMemoryInterval(&_mmi, (uintptr_t)p >> 16,
((uintptr_t)p + G - 1) >> 16, dm.maphandle, prot,
flags, false, false, 0, G)) {
__mmi_lock();
if (TrackMemoryInterval(&_mmi, (uintptr_t)p >> 16, (uintptr_t)p >> 16,
dm.maphandle, prot, flags, false, false, 0, G)) {
notpossible;
}
__mmi_unlock();
}
/**
@ -80,5 +81,6 @@ noasan void *_extend(void *p, size_t n, void *e, intptr_t h) {
*SHADOW(q) = 0;
}
}
asm("mfence");
return q;
}

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/atomic.h"
#include "libc/runtime/runtime.h"
/**
@ -39,4 +40,4 @@
* though under normal circumstances, `__ftrace` should only be either
* zero or one.
*/
_Atomic(int) __ftrace;
atomic_int __ftrace;

View file

@ -1,12 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_FUTEX_INTERNAL_H_
#define COSMOPOLITAN_LIBC_INTRIN_FUTEX_INTERNAL_H_
#include "libc/calls/struct/timespec.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int _futex_wait(void *, int, char, struct timespec *) hidden;
int _futex_wake(void *, int, char) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_INTRIN_FUTEX_INTERNAL_H_ */

View file

@ -19,11 +19,11 @@
#include "libc/calls/extend.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/thread/thread.h"
#include "libc/intrin/pushpop.h"
#include "libc/intrin/weaken.h"
#include "libc/nt/runtime.h"
#include "libc/sysv/consts/o.h"
#include "libc/thread/thread.h"
STATIC_YOINK("_init_g_fds");
@ -41,7 +41,7 @@ static textwindows dontinline void SetupWinStd(struct Fds *fds, int i, int x) {
textstartup void InitializeFileDescriptors(void) {
struct Fds *fds;
__fds_lock_obj.type = PTHREAD_MUTEX_RECURSIVE;
__fds_lock_obj._type = PTHREAD_MUTEX_RECURSIVE;
fds = VEIL("r", &g_fds);
fds->p = fds->e = (void *)0x6fe000040000;
fds->n = 4;

View file

@ -24,6 +24,8 @@
#include "libc/errno.h"
#include "libc/fmt/divmod10.internal.h"
#include "libc/fmt/fmt.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/asancodes.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/kprintf.h"

39
libc/intrin/leaky.S Normal file
View file

@ -0,0 +1,39 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 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/macros.internal.h"
// Decentralized section for leaky functions.
.section .piro.relo.sort.leaky.1,"aw",@nobits
.type _leaky_start,@object
.type _leaky_end,@object
.globl _leaky_start,_leaky_end
.hidden _leaky_start,_leaky_end
.byte 0
.align __SIZEOF_POINTER__
.underrun
_leaky_start:
.previous/*
...
decentralized content
...
*/.section .piro.relo.sort.leaky.3,"aw",@nobits
_leaky_end:
.quad 0
.overrun
.previous

View file

@ -0,0 +1,16 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_
#define COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define IGNORE_LEAKS(FUNC) \
STATIC_YOINK("_leaky_start"); \
void *_leaky_##FUNC[] _Section(".piro.relo.sort.leaky.2." #FUNC \
",\"aw\",@init_array #") = {FUNC}
extern intptr_t _leaky_end[] __attribute__((__weak__));
extern intptr_t _leaky_start[] __attribute__((__weak__));
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_ */

View file

@ -17,14 +17,14 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/likely.h"
#include "libc/intrin/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/likely.h"
#include "libc/intrin/weaken.h"
#include "libc/log/libfatal.internal.h"
#include "libc/log/log.h"
#include "libc/macros.internal.h"
@ -201,6 +201,9 @@ int TrackMemoryInterval(struct MemoryIntervals *mm, int x, int y, long h,
unsigned i;
#if IsModeDbg()
assert(y >= x);
if (!AreMemoryIntervalsOk(mm)) {
PrintMemoryIntervals(2, mm);
}
assert(AreMemoryIntervalsOk(mm));
#endif

View file

@ -22,5 +22,5 @@
.init.start 200,_init__mmi
movb $OPEN_MAX,_mmi+8
movl $_mmi+24,_mmi+16
movb $PTHREAD_MUTEX_RECURSIVE,__mmi_lock_obj(%rip)
movb $PTHREAD_MUTEX_RECURSIVE,__mmi_lock_obj+16(%rip)
.init.end 200,_init__mmi

View file

@ -16,8 +16,10 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/thread/thread.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/thread/thread.h"
// this lock currently needs to be (1) recursive and (2) not nsync
extern pthread_mutex_t __mmi_lock_obj;

View file

@ -16,10 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/errno.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
/**
* Gets value of TLS slot for current thread.

View file

@ -27,17 +27,20 @@
* Allocates TLS slot.
*/
int pthread_key_create(pthread_key_t *key, pthread_key_dtor dtor) {
int i, j;
int i, j, rc = EAGAIN;
pthread_spin_lock(&_pthread_keys_lock);
for (i = 0; i < (PTHREAD_KEYS_MAX + 63) / 64; ++i) {
if (~_pthread_key_usage[i]) {
j = bsrl(~_pthread_key_usage[i]);
_pthread_key_usage[i] |= 1ul << j;
_pthread_key_dtor[i * 64 + j] = dtor;
*key = i * 64 + j;
return 0;
rc = 0;
break;
}
}
return EAGAIN;
pthread_spin_unlock(&_pthread_keys_lock);
return rc;
}
static textexit void _pthread_key_atexit(void) {

View file

@ -24,11 +24,15 @@
* Deletes TLS slot.
*/
int pthread_key_delete(pthread_key_t key) {
int rc;
pthread_spin_lock(&_pthread_keys_lock);
if (key < PTHREAD_KEYS_MAX) {
_pthread_key_usage[key / 64] &= ~(1ul << (key % 64));
_pthread_key_dtor[key] = 0;
return 0;
rc = 0;
} else {
return EINVAL;
rc = EINVAL;
}
pthread_spin_unlock(&_pthread_keys_lock);
return rc;
}

View file

@ -18,12 +18,16 @@
*/
#include "libc/nexgen32e/bsr.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
void _pthread_key_destruct(void *key[PTHREAD_KEYS_MAX]) {
int i, j;
uint64_t x;
void *value;
pthread_key_dtor dtor;
if (!__tls_enabled) return;
pthread_spin_lock(&_pthread_keys_lock);
if (!key) key = _pthread_keys;
StartOver:
for (i = 0; i < (PTHREAD_KEYS_MAX + 63) / 64; ++i) {
@ -32,10 +36,13 @@ StartOver:
j = bsrl(x);
if ((value = key[i * 64 + j]) && (dtor = _pthread_key_dtor[i * 64 + j])) {
key[i * 64 + j] = 0;
pthread_spin_unlock(&_pthread_keys_lock);
dtor(value);
pthread_spin_lock(&_pthread_keys_lock);
goto StartOver;
}
x &= ~(1ul << j);
}
}
pthread_spin_unlock(&_pthread_keys_lock);
}

View file

@ -17,6 +17,9 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
pthread_spinlock_t _pthread_keys_lock;
// tls value slots for pthread keys api
_Thread_local void *_pthread_keys[PTHREAD_KEYS_MAX];

View file

@ -27,8 +27,8 @@
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr) {
*mutex = (pthread_mutex_t){
attr ? attr->type : 0,
attr ? attr->pshared : 0,
._type = attr ? attr->_type : 0,
._pshared = attr ? attr->_pshared : 0,
};
return 0;
}

View file

@ -16,12 +16,14 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/futex.internal.h"
#include "libc/intrin/likely.h"
#include "libc/intrin/weaken.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "third_party/nsync/mu.h"
/**
* Locks mutex.
@ -60,28 +62,18 @@
int pthread_mutex_lock(pthread_mutex_t *mutex) {
int c, d, t;
if (mutex->type == PTHREAD_MUTEX_NORMAL) {
// From Futexes Are Tricky Version 1.1 § Mutex, Take 3;
// Ulrich Drepper, Red Hat Incorporated, June 27, 2004.
c = 0;
if (!atomic_compare_exchange_strong_explicit(
&mutex->lock, &c, 1, memory_order_acquire, memory_order_relaxed)) {
if (c != 2) {
c = atomic_exchange_explicit(&mutex->lock, 2, memory_order_acquire);
}
while (c) {
_futex_wait(&mutex->lock, 2, mutex->pshared, 0);
c = atomic_exchange_explicit(&mutex->lock, 2, memory_order_acquire);
}
}
if (LIKELY(__tls_enabled && //
mutex->_type == PTHREAD_MUTEX_NORMAL && //
mutex->_pshared == PTHREAD_PROCESS_PRIVATE && //
weaken(nsync_mu_lock))) {
weaken(nsync_mu_lock)((nsync_mu *)mutex);
return 0;
}
t = gettid();
if (mutex->type == PTHREAD_MUTEX_ERRORCHECK) {
c = atomic_load_explicit(&mutex->lock, memory_order_relaxed);
if (mutex->_type == PTHREAD_MUTEX_ERRORCHECK) {
c = atomic_load_explicit(&mutex->_lock, memory_order_relaxed);
if ((c & 0x000fffff) == t) {
assert(!"deadlock");
return EDEADLK;
}
}
@ -90,29 +82,29 @@ int pthread_mutex_lock(pthread_mutex_t *mutex) {
c = 0;
d = 0x10100000 | t;
if (atomic_compare_exchange_weak_explicit(
&mutex->lock, &c, d, memory_order_acquire, memory_order_relaxed)) {
&mutex->_lock, &c, d, memory_order_acquire, memory_order_relaxed)) {
break;
} else {
if ((c & 0x000fffff) == t) {
if ((c & 0x0ff00000) < 0x0ff00000) {
c = atomic_fetch_add_explicit(&mutex->lock, 0x00100000,
c = atomic_fetch_add_explicit(&mutex->_lock, 0x00100000,
memory_order_relaxed);
break;
} else {
assert(!"recurse");
return EAGAIN;
}
}
if ((c & 0xf0000000) == 0x10000000) {
d = 0x20000000 | c;
if (atomic_compare_exchange_weak_explicit(&mutex->lock, &c, d,
if (atomic_compare_exchange_weak_explicit(&mutex->_lock, &c, d,
memory_order_acquire,
memory_order_relaxed)) {
c = d;
}
}
if ((c & 0xf0000000) == 0x20000000) {
_futex_wait(&mutex->lock, c, mutex->pshared, 0);
// _futex_wait(&mutex->_lock, c, mutex->_pshared, 0);
pthread_yield();
}
}
}

View file

@ -20,7 +20,11 @@
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/likely.h"
#include "libc/intrin/weaken.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "third_party/nsync/mu.h"
/**
* Locks mutex if it isn't locked already.
@ -35,10 +39,21 @@
int pthread_mutex_trylock(pthread_mutex_t *mutex) {
int c, d, t;
if (mutex->type == PTHREAD_MUTEX_NORMAL) {
if (LIKELY(__tls_enabled && //
mutex->_type == PTHREAD_MUTEX_NORMAL && //
mutex->_pshared == PTHREAD_PROCESS_PRIVATE && //
weaken(nsync_mu_trylock))) {
if (weaken(nsync_mu_trylock)((nsync_mu *)mutex)) {
return 0;
} else {
return EBUSY;
}
}
if (mutex->_type == PTHREAD_MUTEX_NORMAL) {
c = 0;
if (atomic_compare_exchange_strong_explicit(
&mutex->lock, &c, 1, memory_order_acquire, memory_order_relaxed)) {
&mutex->_lock, &c, 1, memory_order_acquire, memory_order_relaxed)) {
return 0;
} else {
return EBUSY;
@ -49,14 +64,14 @@ int pthread_mutex_trylock(pthread_mutex_t *mutex) {
t = gettid();
d = 0x10100000 | t;
if (!atomic_compare_exchange_strong_explicit(
&mutex->lock, &c, d, memory_order_acquire, memory_order_relaxed)) {
if ((c & 0x000fffff) != t || mutex->type == PTHREAD_MUTEX_ERRORCHECK) {
&mutex->_lock, &c, d, memory_order_acquire, memory_order_relaxed)) {
if ((c & 0x000fffff) != t || mutex->_type == PTHREAD_MUTEX_ERRORCHECK) {
return EBUSY;
}
if ((c & 0x0ff00000) == 0x0ff00000) {
return EAGAIN;
}
atomic_fetch_add_explicit(&mutex->lock, 0x00100000, memory_order_relaxed);
atomic_fetch_add_explicit(&mutex->_lock, 0x00100000, memory_order_relaxed);
}
return 0;

View file

@ -16,12 +16,14 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/futex.internal.h"
#include "libc/intrin/likely.h"
#include "libc/intrin/weaken.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "third_party/nsync/mu.h"
/**
* Releases mutex.
@ -32,30 +34,28 @@
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
int c, t;
if (mutex->type == PTHREAD_MUTEX_NORMAL) {
// From Futexes Are Tricky Version 1.1 § Mutex, Take 3;
// Ulrich Drepper, Red Hat Incorporated, June 27, 2004.
if ((c = atomic_fetch_sub(&mutex->lock, 1)) != 1) {
atomic_store_explicit(&mutex->lock, 0, memory_order_release);
_futex_wake(&mutex->lock, 1, mutex->pshared);
}
if (LIKELY(__tls_enabled && //
mutex->_type == PTHREAD_MUTEX_NORMAL && //
mutex->_pshared == PTHREAD_PROCESS_PRIVATE && //
weaken(nsync_mu_unlock))) {
weaken(nsync_mu_unlock)((nsync_mu *)mutex);
return 0;
}
t = gettid();
if (mutex->type == PTHREAD_MUTEX_ERRORCHECK) {
c = atomic_load_explicit(&mutex->lock, memory_order_relaxed);
if (mutex->_type == PTHREAD_MUTEX_ERRORCHECK) {
c = atomic_load_explicit(&mutex->_lock, memory_order_relaxed);
if ((c & 0x000fffff) != t) {
assert(!"permlock");
return EPERM;
}
}
c = atomic_fetch_sub(&mutex->lock, 0x00100000);
c = atomic_fetch_sub(&mutex->_lock, 0x00100000);
if ((c & 0x0ff00000) == 0x00100000) {
atomic_store_explicit(&mutex->lock, 0, memory_order_release);
atomic_store_explicit(&mutex->_lock, 0, memory_order_release);
if ((c & 0xf0000000) == 0x20000000) {
_futex_wake(&mutex->lock, 1, mutex->pshared);
// _futex_wake(&mutex->_lock, 1, mutex->_pshared);
pthread_yield();
}
}

View file

@ -16,11 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/weaken.h"
#include "libc/thread/thread.h"
#include "third_party/nsync/once.h"
#define INIT 0
#define CALLING 1
@ -44,28 +45,30 @@
* @return 0 on success, or errno on error
*/
int pthread_once(pthread_once_t *once, void init(void)) {
char old;
switch ((old = atomic_load_explicit(&once->lock, memory_order_relaxed))) {
uint32_t old;
if (weaken(nsync_run_once)) {
weaken(nsync_run_once)((nsync_once *)once, init);
return 0;
}
switch ((old = atomic_load_explicit(&once->_lock, memory_order_relaxed))) {
case INIT:
if (atomic_compare_exchange_strong_explicit(&once->lock, &old, CALLING,
if (atomic_compare_exchange_strong_explicit(&once->_lock, &old, CALLING,
memory_order_acquire,
memory_order_relaxed)) {
init();
atomic_store(&once->lock, FINISHED);
break;
atomic_store(&once->_lock, FINISHED);
return 0;
}
// fallthrough
case CALLING:
do {
pthread_yield();
} while (atomic_load_explicit(&once->lock, memory_order_relaxed) ==
} while (atomic_load_explicit(&once->_lock, memory_order_relaxed) ==
CALLING);
break;
return 0;
case FINISHED:
break;
return 0;
default:
assert(!"bad once");
return EINVAL;
}
return 0;
}

View file

@ -0,0 +1,21 @@
/*-*- 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/log/internal.h"
bool _wantcrashreports;

View file

@ -7,6 +7,7 @@ COSMOPOLITAN_C_START_
extern hidden bool __nocolor;
extern hidden int kCrashSigs[8];
extern hidden bool _wantcrashreports;
extern hidden bool g_isrunningundermake;
void __start_fatal(const char *, int) hidden;

View file

@ -16,9 +16,10 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/bits.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/mem/mem.h"
@ -37,7 +38,7 @@ static bool hasleaks;
static noasan void CheckLeak(void *x, void *y, size_t n, void *a) {
if (n) {
if (IsAsan()) {
if (__asan_get_heap_size(x)) {
if (__asan_get_heap_size(x) && !__asan_is_leaky(x)) {
hasleaks = true;
}
} else {
@ -53,9 +54,10 @@ static noasan void OnMemory(void *x, void *y, size_t n, void *a) {
if (i < MAXLEAKS) {
++i;
kprintf("%p %,lu bytes [dlmalloc]", x, n);
if (IsAsan()) {
__asan_print_trace(x);
if (__asan_is_leaky(x)) {
kprintf(" [leaky]");
}
__asan_print_trace(x);
kprintf("\n");
} else if (i == MAXLEAKS) {
++i;
@ -79,6 +81,7 @@ static noasan bool HasLeaks(void) {
*/
noasan void CheckForMemoryLeaks(void) {
struct mallinfo mi;
if (!IsAsan()) return; // we need traces to exclude leaky
if (!_lockcmpxchg(&once, false, true)) {
kprintf("CheckForMemoryLeaks() may only be called once\n");
exit(1);

View file

@ -105,6 +105,7 @@ static void FreeSigAltStack(void *p) {
void ShowCrashReports(void) {
char *sp;
struct sigaltstack ss;
_wantcrashreports = true;
/* <SYNC-LIST>: showcrashreports.c, oncrashthunks.S, oncrash.c */
kCrashSigs[0] = SIGQUIT; /* ctrl+\ aka ctrl+break */
kCrashSigs[1] = SIGFPE; /* 1 / 0 */

View file

@ -1,2 +1,12 @@
.include "o/libc/nt/codegen.inc"
.imp kernel32,__imp_CreateSemaphoreW,CreateSemaphoreW,0
.text.windows
CreateSemaphore:
push %rbp
mov %rsp,%rbp
.profilable
mov __imp_CreateSemaphoreW(%rip),%rax
jmp __sysv2nt
.endfn CreateSemaphore,globl
.previous

View file

@ -166,7 +166,7 @@ imp 'CreateMutexEx' CreateMutexExW kernel32 0
imp 'CreatePrivateNamespace' CreatePrivateNamespaceW kernel32 0
imp 'CreateRemoteThread' CreateRemoteThread kernel32 0
imp 'CreateRemoteThreadEx' CreateRemoteThreadEx kernel32 0
imp 'CreateSemaphore' CreateSemaphoreW kernel32 0
imp 'CreateSemaphore' CreateSemaphoreW kernel32 0 4
imp 'CreateSemaphoreEx' CreateSemaphoreExW kernel32 0
imp 'CreateSymbolicLinkTransacted' CreateSymbolicLinkTransactedW kernel32 238
imp 'CreateTapePartition' CreateTapePartition kernel32 240

View file

@ -82,6 +82,10 @@ bool32 SetWaitableTimer(int64_t hTimer, const int64_t *lpDueTimeAsFtOrNegRela,
int32_t opt_lPeriodMs, NtTimerapcroutine opt_callback,
void *lpArgToCallback, bool32 fUnsleepSystem);
int64_t CreateSemaphore(struct NtSecurityAttributes *opt_lpSemaphoreAttributes,
uint32_t lInitialCount, uint32_t lMaximumCount,
const char16_t *opt_lpName);
int32_t SetEvent(int64_t hEvent);
int32_t ResetEvent(int64_t hEvent);
int32_t PulseEvent(int64_t hEvent);

View file

@ -1808,7 +1808,7 @@
6f900000-6f9fffff 64gb free
6fa00000-6fafffff 64gb free
6fb00000-6fbfffff 64gb free
6fc00000-6fcfffff 64gb free
6fc00004-6fcfffff 64gb nsync
6fd00000-6fdfffff 64gb zipos
6fe00004-6feffffc 64gb g_fds
6ff00000-6ffffffd 64gb free

View file

@ -17,12 +17,12 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/thread/thread.h"
#include "libc/mem/mem.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
/**
* Allocates stream object for already-opened file descriptor.
@ -38,7 +38,7 @@ FILE *fdopen(int fd, const char *mode) {
f->fd = fd;
f->bufmode = ischardev(fd) ? _IOLBF : _IOFBF;
f->iomode = fopenflags(mode);
f->lock.type = PTHREAD_MUTEX_RECURSIVE;
f->lock._type = PTHREAD_MUTEX_RECURSIVE;
f->size = BUFSIZ;
if ((f->buf = malloc(f->size))) {
if ((f->iomode & O_ACCMODE) != O_RDONLY) {

View file

@ -16,12 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/thread/thread.h"
#include "libc/mem/mem.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
/**
* Opens buffer as stream.
@ -54,7 +54,7 @@ FILE *fmemopen(void *buf, size_t size, const char *mode) {
f->end = size;
f->size = size;
f->iomode = fopenflags(mode);
f->lock.type = PTHREAD_MUTEX_RECURSIVE;
f->lock._type = PTHREAD_MUTEX_RECURSIVE;
if (f->iomode & O_APPEND) {
if ((p = memchr(buf, '\0', size))) {
f->beg = p - (char *)buf;

View file

@ -34,6 +34,6 @@
ezlea __stderr_buf,cx
mov %rcx,0x18(%rax) # f.buf
movl $BUFSIZ,0x20(%rax) # f.size
movb $PTHREAD_MUTEX_RECURSIVE,0x38(%rax) # f.lock.attr
movb $PTHREAD_MUTEX_RECURSIVE,0x38+16(%rax) # f.lock.attr
mov %rax,stderr(%rip)
.init.end 400,_init_stderr,globl,hidden

View file

@ -30,6 +30,6 @@
ezlea __stdin_buf,cx
mov %rcx,0x18(%rax) # f.buf
movl $BUFSIZ,0x20(%rax) # f.size
movb $PTHREAD_MUTEX_RECURSIVE,0x38(%rax) # f.lock.attr
movb $PTHREAD_MUTEX_RECURSIVE,0x38+16(%rax) # f.lock.attr
mov %rax,stdin(%rip)
.init.end 400,_init_stdin,globl,hidden

View file

@ -32,6 +32,6 @@
ezlea __stdout_buf,cx
mov %rcx,0x18(%rax) # f.buf
movl $BUFSIZ,0x20(%rax) # f.size
movb $PTHREAD_MUTEX_RECURSIVE,0x38(%rax) # f.lock.attr
movb $PTHREAD_MUTEX_RECURSIVE,0x38+16(%rax) # f.lock.attr
mov %rax,stdout(%rip)
.init.end 400,_init_stdout,globl,hidden

View file

@ -579,7 +579,7 @@ syscon sicode SYS_USER_DISPATCH 2 -1 -1 -1 -1 -1 # SIGSYS; syscall
# sigaltstack() values
#
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
syscon ss SIGSTKSZ 8192 131072 34816 28672 28672 8192 # overlaid with STACKSIZE; you need to #undef SIGSTKSZ to access this symbol
syscon ss SIGSTKSZ 8192 131072 34816 28672 28672 8192 # overlaid with FRAMESIZE; you need to #undef SIGSTKSZ to access this symbol
syscon ss MINSIGSTKSZ 2048 32768 2048 12288 8192 2048 # overlaid with 32768; you need to #undef MINSIGSTKSZ to access this symbol
syscon ss SS_ONSTACK 1 1 1 1 1 1 # unix consensus
syscon ss SS_DISABLE 2 4 4 4 4 2 # bsd consensus
@ -1316,9 +1316,9 @@ syscon rusage RUSAGE_BOTH -2 99 99 99 99 99 # woop
#
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
syscon futex FUTEX_WAIT 0 0 0 1 0 0
syscon futex FUTEX_WAKE 1 0 0 2 1 0
syscon futex FUTEX_REQUEUE 3 0 0 3 3 0
syscon futex FUTEX_PRIVATE_FLAG 128 0 0 128 128 0
syscon futex FUTEX_WAKE 1 0 0 2 0 0
syscon futex FUTEX_REQUEUE 3 0 0 3 0 0
syscon futex FUTEX_PRIVATE_FLAG 128 0 0 128 0 0
# lio_listio() magnums
#
@ -1336,7 +1336,7 @@ syscon sched SCHED_OTHER 0 127 2 127 0 127 # standard round-robin
syscon sched SCHED_FIFO 1 127 1 127 1 127 # [real-time] first-in, first-out policy
syscon sched SCHED_RR 2 127 3 127 2 127 # [real-time] round-robin policy
syscon sched SCHED_BATCH 3 127 2 127 0 127 # for "batch" style execution of processes; polyfilled as SCHED_OTHER on non-Linux
𝔰𝔶𝔰𝔠𝔬𝔫 sched SCHED_IDLE 5 127 2 127 0 127 # for running very low priority background jobs; polyfilled as SCHED_OTHER on non-Linux
syscon sched SCHED_IDLE 5 127 2 127 0 127 # for running very low priority background jobs; polyfilled as SCHED_OTHER on non-Linux
syscon sched SCHED_DEADLINE 6 127 127 127 127 127 # can only be set by sched_setattr()
syscon sched SCHED_RESET_ON_FORK 0x40000000 0 0 0 0 0 # Can be ORed in to make sure the process is reverted back to SCHED_NORMAL on fork(); no-op on non-Linux
@ -1847,7 +1847,7 @@ syscon nr __NR_kill 0x003e 0x2000025 0x0025 0x007a 0x025 0xfff
syscon nr __NR_killpg 0xfff 0xfff 0x0092 0xfff 0xfff 0xfff
syscon nr __NR_clone 0x0038 0xfff 0xfff 0xfff 0x11f 0xfff
syscon nr __NR_tkill 0x00c8 0xfff 0xfff 0xfff 0xfff 0xfff
syscon nr __NR_futex 0x00ca 0xfff 0xfff 0x0053 0x0a6 0xfff
syscon nr __NR_futex 0x00ca 0xfff 0xfff 0x0053 0xfff 0xfff
syscon nr __NR_set_robust_list 0x0111 0xfff 0xfff 0xfff 0x0a7 0xfff
syscon nr __NR_get_robust_list 0x0112 0xfff 0xfff 0xfff 0x0a8 0xfff
syscon nr __NR_uname 0x003f 0xfff 0x00a4 0xfff 0xfff 0xfff

View file

@ -1,2 +1,2 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon futex,FUTEX_PRIVATE_FLAG,128,0,0,128,128,0
.syscon futex,FUTEX_PRIVATE_FLAG,128,0,0,128,0,0

View file

@ -1,2 +1,2 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon futex,FUTEX_REQUEUE,3,0,0,3,3,0
.syscon futex,FUTEX_REQUEUE,3,0,0,3,0,0

View file

@ -1,2 +1,2 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon futex,FUTEX_WAKE,1,0,0,2,1,0
.syscon futex,FUTEX_WAKE,1,0,0,2,0,0

View file

@ -1,2 +1,2 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon misc,SOMAXCONN,0x80,0x80,0x80,0x80,0x80,0x7fffffff
.syscon limits,SOMAXCONN,4096,128,128,128,128,2147483647

View file

@ -1,2 +1,2 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon nr,__NR_futex,0x00ca,0xfff,0xfff,0x0053,0x0a6,0xfff
.syscon nr,__NR_futex,0x00ca,0xfff,0xfff,0x0053,0xfff,0xfff

View file

@ -2,21 +2,25 @@
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_FUTEX_H_
#include "libc/runtime/symbolic.h"
#define FUTEX_WAIT SYMBOLIC(FUTEX_WAIT)
#define FUTEX_WAKE SYMBOLIC(FUTEX_WAKE)
#define FUTEX_REQUEUE SYMBOLIC(FUTEX_REQUEUE)
#define FUTEX_PRIVATE_FLAG SYMBOLIC(FUTEX_PRIVATE_FLAG)
#define FUTEX_WAIT SYMBOLIC(FUTEX_WAIT)
#define FUTEX_WAKE SYMBOLIC(FUTEX_WAKE)
#define FUTEX_REQUEUE SYMBOLIC(FUTEX_REQUEUE)
#define FUTEX_PRIVATE_FLAG 128
#define FUTEX_WAIT_PRIVATE (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)
#define FUTEX_WAKE_PRIVATE (FUTEX_WAKE | FUTEX_PRIVATE_FLAG)
#define FUTEX_REQUEUE_PRIVATE (FUTEX_REQUEUE | FUTEX_PRIVATE_FLAG)
#define FUTEX_WAIT_BITSET 9
#define FUTEX_CLOCK_REALTIME 256
#define FUTEX_BITSET_MATCH_ANY 0xffffffff
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const int FUTEX_WAIT;
extern const int FUTEX_WAKE;
extern const int FUTEX_REQUEUE;
extern const int FUTEX_PRIVATE_FLAG;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -10,7 +10,7 @@ extern const int SS_DISABLE;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#define SIGSTKSZ STACKSIZE
#define SIGSTKSZ FRAMESIZE
#define MINSIGSTKSZ 32768
#define SS_ONSTACK 1
#define SS_DISABLE SS_DISABLE

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/atomic.h"
#include "libc/runtime/runtime.h"
/**
@ -40,4 +41,4 @@
* under normal circumstances, `__strace` should only be either zero or
* one.
*/
_Atomic(int) __strace;
atomic_int __strace;

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/macros.internal.h"
.privileged
@ -32,12 +33,52 @@
// @param %rsi,%rdx,%rcx,%r8,%r9 may supply parameters 1 through 5
// @param sixth is optionally pushed on the stack before call
// @return %rax has result, or -1 w/ errno on failure
syscall:mov %rdi,%rax
mov %rsi,%rdi
mov %rdx,%rsi
mov %rcx,%rdx
mov %r8,%rcx # intended
mov %r9,%r8
mov 8(%rsp),%r9
jmp *__systemfive(%rip)
syscall:
push %rbp
mov %rsp,%rbp
.profilable
// slide arguments into their right places
mov %rdi,%rax # nr
mov %rsi,%rdi # arg 1
mov %rdx,%rsi # arg 2
mov %rcx,%rdx # arg 3
mov %r8,%rcx # arg 4
mov %r9,%r8 # arg 5
mov 16(%rbp),%r9 # arg 6
push 32(%rbp) # arg 8
push 24(%rbp) # arg 7
// convert from consts.sh to syscalls.sh encoding
push %rcx
mov __hostos(%rip),%cl
test $LINUX,%cl
jnz 2f
1: test $FREEBSD,%cl
jz 1f
shl $4*7,%rax
jmp 2f
1: test $OPENBSD,%cl
jz 1f
shl $4*10,%rax
jmp 2f
1: test $NETBSD,%cl
jz 1f
shl $4*13,%rax
jmp 2f
1: test $XNU,%cl
jz 2f
mov %eax,%ecx
and $0x0f000000,%ecx
and $0x00000fff,%eax
shl $4*3,%eax
or %ecx,%eax
2: pop %rcx
// trigger the system call
call *__systemfive(%rip)
// clean up stack and return
leave
ret
.endfn syscall,globl

View file

@ -16,8 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/atomic.h"
#include "libc/testlib/testlib.h"
char g_fixturename[256];
_Atomic(unsigned) g_testlib_ran;
_Atomic(unsigned) g_testlib_failed;
atomic_uint g_testlib_ran;
atomic_uint g_testlib_failed;

View file

@ -182,13 +182,13 @@ noasan int main(int argc, char *argv[]) {
testlib_runalltests();
if (!g_testlib_failed && runbenchmarks_ && weaken(testlib_runallbenchmarks)) {
weaken(testlib_runallbenchmarks)();
if (!g_testlib_failed) {
if (IsAsan() && !g_testlib_failed) {
CheckForMemoryLeaks();
}
if (!g_testlib_failed && IsRunningUnderMake()) {
return 254; // compile.com considers this 0 and propagates output
}
} else if (!g_testlib_failed) {
} else if (IsAsan() && !g_testlib_failed) {
CheckForMemoryLeaks();
}

5
libc/thread/README.md Normal file
View file

@ -0,0 +1,5 @@
# Cosmpolitan POSIX Threads Library
Cosmopolitan Libc implements threading as it is written in The Open
Group Base Specifications Issue 7, 2018 edition IEEE Std 1003.1-2017
(Revision of IEEE Std 1003.1-2008) in addition to GNU extensions.

View file

@ -1,103 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PLATFORM_C11_ATOMIC_H_
#define NSYNC_PLATFORM_C11_ATOMIC_H_
/* Atomic operations on nsync_atomic_uint32_ quantities
CAS, load, and store.
Normally, these are used only on nsync_atomic_uint32_ values, but on Linux they may be
invoked on int values, because futexes operate on int values. A
compile-time check in the futex code ensures that both int and
nsync_atomic_uint32_ are 32 bits.
Memory barriers:
Operations with the suffixes _ACQ and _RELACQ ensure that the operation
appears to complete before other memory operations subsequently performed by
the same thread, as seen by other threads. (In the case of ATM_CAS_ACQ,
this applies only if the operation returns a non-zero value.)
Operations with the suffixes _REL and _RELACQ ensure that the operation
appears to complete after other memory operations previously performed by
the same thread, as seen by other threads. (In the case of ATM_CAS_REL,
this applies only if the operation returns a non-zero value.)
// Atomically,
// int ATM_CAS (nsync_atomic_uint32_ *p, uint32_t old_value, uint32_t new_value) {
// if (*p == old_value) {
// *p = new_value;
// return (some-non-zero-value);
// } else {
// return (0);
// }
// }
// *_ACQ, *_REL, *_RELACQ variants are available,
// with the barrier semantics described above.
int ATM_CAS (nsync_atomic_uint32_ *p, uint32_t old_value, uint32_t new_value);
// Atomically,
// uint32_t ATM_LOAD (nsync_atomic_uint32_ *p) { return (*p); }
// A *_ACQ variant is available,
// with the barrier semantics described above.
uint32_t ATM_LOAD (nsync_atomic_uint32_ *p);
// Atomically,
// void ATM_STORE (nsync_atomic_uint32_ *p, uint32_t value) { *p = value; }
// A *_REL variant is available,
// with the barrier semantics described above.
void ATM_STORE (nsync_atomic_uint32_ *p, uint32_t value);
*/
#include "libc/thread/compiler.h"
#include "libc/intrin/atomic.h"
#include "libc/thread/nsync_atomic.h"
NSYNC_CPP_START_
static __inline__ int atm_cas_nomb_u32_ (nsync_atomic_uint32_ *p, uint32_t o, uint32_t n) {
return (atomic_compare_exchange_strong_explicit (NSYNC_ATOMIC_UINT32_PTR_ (p), &o, n,
memory_order_relaxed, memory_order_relaxed));
}
static __inline__ int atm_cas_acq_u32_ (nsync_atomic_uint32_ *p, uint32_t o, uint32_t n) {
return (atomic_compare_exchange_strong_explicit (NSYNC_ATOMIC_UINT32_PTR_ (p), &o, n,
memory_order_acquire, memory_order_relaxed));
}
static __inline__ int atm_cas_rel_u32_ (nsync_atomic_uint32_ *p, uint32_t o, uint32_t n) {
return (atomic_compare_exchange_strong_explicit (NSYNC_ATOMIC_UINT32_PTR_ (p), &o, n,
memory_order_release, memory_order_relaxed));
}
static __inline__ int atm_cas_relacq_u32_ (nsync_atomic_uint32_ *p, uint32_t o, uint32_t n) {
return (atomic_compare_exchange_strong_explicit (NSYNC_ATOMIC_UINT32_PTR_ (p), &o, n,
memory_order_acq_rel, memory_order_relaxed));
}
#define ATM_CAS_HELPER_(barrier, p, o, n) (atm_cas_##barrier##_u32_ ((p), (o), (n)))
#define ATM_CAS(p,o,n) ATM_CAS_HELPER_ (nomb, (p), (o), (n))
#define ATM_CAS_ACQ(p,o,n) ATM_CAS_HELPER_ (acq, (p), (o), (n))
#define ATM_CAS_REL(p,o,n) ATM_CAS_HELPER_ (rel, (p), (o), (n))
#define ATM_CAS_RELACQ(p,o,n) ATM_CAS_HELPER_ (relacq, (p), (o), (n))
/* Need a cast to remove "const" from some uses. */
#define ATM_LOAD(p) (atomic_load_explicit ((nsync_atomic_uint32_ *) NSYNC_ATOMIC_UINT32_PTR_ (p), memory_order_relaxed))
#define ATM_LOAD_ACQ(p) (atomic_load_explicit ((nsync_atomic_uint32_ *) NSYNC_ATOMIC_UINT32_PTR_ (p), memory_order_acquire))
#define ATM_STORE(p,v) (atomic_store_explicit (NSYNC_ATOMIC_UINT32_PTR_ (p), (v), memory_order_relaxed))
#define ATM_STORE_REL(p,v) (atomic_store_explicit (NSYNC_ATOMIC_UINT32_PTR_ (p), (v), memory_order_release))
NSYNC_CPP_END_
#endif /*NSYNC_PLATFORM_C11_ATOMIC_H_*/

View file

@ -215,7 +215,7 @@ static int CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags,
wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid;
wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid;
wt->tls = flags & CLONE_SETTLS ? tls : 0;
wt->lock.lock = 1;
wt->lock._lock = 1;
if ((rc = bsdthread_create(fn, arg, wt, 0, PTHREAD_START_CUSTOM_XNU)) != -1) {
pthread_spin_lock(&wt->lock);
rc = wt->tid;

View file

@ -1,293 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_INTERNAL_COMMON_H_
#define NSYNC_INTERNAL_COMMON_H_
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/platform.h"
#include "libc/thread/nsync_atomic.h"
#include "libc/thread/sem.h"
#include "libc/thread/nsync_waiter.h"
#include "libc/thread/dll.h"
#include "libc/thread/nsync_mu.h"
#include "libc/thread/nsync_cv.h"
#include "libc/thread/nsync_note.h"
#include "libc/thread/wait_internal.h"
/* Annotations for race detectors. */
#if defined(__has_feature) && !defined(__SANITIZE_THREAD__)
#if __has_feature(thread_sanitizer) /* used by clang */
#define __SANITIZE_THREAD__ 1 /* GCC uses this; fake it in clang */
#endif
#endif
#if defined(__SANITIZE_THREAD__)
NSYNC_C_START_
void AnnotateIgnoreWritesBegin(const char* file, int line);
void AnnotateIgnoreWritesEnd(const char* file, int line);
void AnnotateIgnoreReadsBegin(const char* file, int line);
void AnnotateIgnoreReadsEnd(const char* file, int line);
NSYNC_C_END_
#define IGNORE_RACES_START() \
do { \
AnnotateIgnoreReadsBegin(__FILE__, __LINE__); \
AnnotateIgnoreWritesBegin(__FILE__, __LINE__); \
} while (0)
#define IGNORE_RACES_END() \
do { \
AnnotateIgnoreWritesEnd(__FILE__, __LINE__); \
AnnotateIgnoreReadsEnd(__FILE__, __LINE__); \
} while (0)
#else
#define IGNORE_RACES_START()
#define IGNORE_RACES_END()
#endif
#ifndef NSYNC_DEBUG
#define NSYNC_DEBUG 0
#endif
NSYNC_CPP_START_
/* Yield the CPU. Platform specific. */
void nsync_yield_ (void);
/* Retrieve the per-thread cache of the waiter object. Platform specific. */
void *nsync_per_thread_waiter_ (void (*dest) (void *));
/* Set the per-thread cache of the waiter object. Platform specific. */
void nsync_set_per_thread_waiter_ (void *v, void (*dest) (void *));
/* Used in spinloops to delay resumption of the loop.
Usage:
unsigned attempts = 0;
while (try_something) {
attempts = nsync_spin_delay_ (attempts);
} */
unsigned nsync_spin_delay_ (unsigned attempts);
/* Spin until (*w & test) == 0, then atomically perform *w = ((*w | set) &
~clear), perform an acquire barrier, and return the previous value of *w.
*/
uint32_t nsync_spin_test_and_set_ (nsync_atomic_uint32_ *w, uint32_t test,
uint32_t set, uint32_t clear);
/* Abort after printing the nul-temrinated string s[]. */
void nsync_panic_ (const char *s);
/* ---------- */
#define MIN_(a_,b_) ((a_) < (b_)? (a_) : (b_))
#define MAX_(a_,b_) ((a_) > (b_)? (a_) : (b_))
/* ---------- */
/* Fields in nsync_mu.word.
- At least one of the MU_WLOCK or MU_RLOCK_FIELD fields must be zero.
- MU_WLOCK indicates that a write lock is held.
- MU_RLOCK_FIELD is a count of readers with read locks.
- MU_SPINLOCK represents a spinlock that must be held when manipulating the
waiter queue.
- MU_DESIG_WAKER indicates that a former waiter has been woken, but has
neither acquired the lock nor gone back to sleep. Legal to fail to set it;
illegal to set it when no such waiter exists.
- MU_WAITING indicates whether the waiter queue is non-empty.
The following bits should be zero if MU_WAITING is zero.
- MU_CONDITION indicates that some waiter may have an associated condition
(from nsync_mu_wait, etc.). Legal to set it with no such waiter exists,
but illegal to fail to set it with such a waiter.
- MU_WRITER_WAITING indicates that a reader that has not yet blocked
at least once should not acquire in order not to starve waiting writers.
It set when a writer blocks or a reader is woken with a writer waiting.
It is reset when a writer acquires, but set again when that writer
releases if it wakes readers and there is a waiting writer.
- MU_LONG_WAIT indicates that a waiter has been woken many times but
repeatedly failed to acquire when competing for the lock. This is used
only to prevent long-term starvation by writers. The thread that sets it
clears it when if acquires.
- MU_ALL_FALSE indicates that a complete scan of the waiter list found no
waiters with true conditions, and the lock has not been acquired by a
writer since then. This allows a reader lock to be released without
testing conditions again. It is legal to fail to set this, but illegal
to set it inappropriately.
*/
#define MU_WLOCK ((uint32_t) (1 << 0)) /* writer lock is held. */
#define MU_SPINLOCK ((uint32_t) (1 << 1)) /* spinlock is held (protects waiters). */
#define MU_WAITING ((uint32_t) (1 << 2)) /* waiter list is non-empty. */
#define MU_DESIG_WAKER ((uint32_t) (1 << 3)) /* a former waiter awoke, and hasn't yet acquired or slept anew */
#define MU_CONDITION ((uint32_t) (1 << 4)) /* the wait list contains some conditional waiters. */
#define MU_WRITER_WAITING ((uint32_t) (1 << 5)) /* there is a writer waiting */
#define MU_LONG_WAIT ((uint32_t) (1 << 6)) /* the waiter at the head of the queue has been waiting a long time */
#define MU_ALL_FALSE ((uint32_t) (1 << 7)) /* all waiter conditions are false */
#define MU_RLOCK ((uint32_t) (1 << 8)) /* low-order bit of reader count, which uses rest of word */
/* The constants below are derived from those above. */
#define MU_RLOCK_FIELD (~(uint32_t) (MU_RLOCK - 1)) /* mask of reader count field */
#define MU_ANY_LOCK (MU_WLOCK | MU_RLOCK_FIELD) /* mask for any lock held */
#define MU_WZERO_TO_ACQUIRE (MU_ANY_LOCK | MU_LONG_WAIT) /* bits to be zero to acquire write lock */
#define MU_WADD_TO_ACQUIRE (MU_WLOCK) /* add to acquire a write lock */
#define MU_WHELD_IF_NON_ZERO (MU_WLOCK) /* if any of these bits are set, write lock is held */
#define MU_WSET_WHEN_WAITING (MU_WAITING | MU_WRITER_WAITING) /* a writer is waiting */
#define MU_WCLEAR_ON_ACQUIRE (MU_WRITER_WAITING) /* clear MU_WRITER_WAITING when a writer acquires */
#define MU_WCLEAR_ON_UNCONTENDED_RELEASE (MU_ALL_FALSE) /* clear if a writer releases w/o waking */
/* bits to be zero to acquire read lock */
#define MU_RZERO_TO_ACQUIRE (MU_WLOCK | MU_WRITER_WAITING | MU_LONG_WAIT)
#define MU_RADD_TO_ACQUIRE (MU_RLOCK) /* add to acquire a read lock */
#define MU_RHELD_IF_NON_ZERO (MU_RLOCK_FIELD) /* if any of these bits are set, read lock is held */
#define MU_RSET_WHEN_WAITING (MU_WAITING) /* indicate that some thread is waiting */
#define MU_RCLEAR_ON_ACQUIRE ((uint32_t) 0) /* nothing to clear when a read acquires */
#define MU_RCLEAR_ON_UNCONTENDED_RELEASE ((uint32_t) 0) /* nothing to clear when a read releases */
/* A lock_type holds the values needed to manipulate a mu in some mode (read or
write). This allows some of the code to be generic, and parameterized by
the lock type. */
typedef struct lock_type_s {
uint32_t zero_to_acquire; /* bits that must be zero to acquire */
uint32_t add_to_acquire; /* constant to add to acquire */
uint32_t held_if_non_zero; /* if any of these bits are set, the lock is held */
uint32_t set_when_waiting; /* set when thread waits */
uint32_t clear_on_acquire; /* clear when thread acquires */
uint32_t clear_on_uncontended_release; /* clear when thread releases without waking */
} lock_type;
/* writer_type points to a lock_type that describes how to manipulate a mu for a writer. */
extern lock_type *nsync_writer_type_;
/* reader_type points to a lock_type that describes how to manipulate a mu for a reader. */
extern lock_type *nsync_reader_type_;
/* ---------- */
/* Bits in nsync_cv.word */
#define CV_SPINLOCK ((uint32_t) (1 << 0)) /* protects waiters */
#define CV_NON_EMPTY ((uint32_t) (1 << 1)) /* waiters list is non-empty */
/* ---------- */
/* Hold a pair of condition function and its argument. */
struct wait_condition_s {
int (*f) (const void *v);
const void *v;
int (*eq) (const void *a, const void *b);
};
/* Return whether wait conditions *a_ and *b_ are equal and non-null. */
#define WAIT_CONDITION_EQ(a_, b_) ((a_)->f != NULL && (a_)->f == (b_)->f && \
((a_)->v == (b_)->v || \
((a_)->eq != NULL && (*(a_)->eq) ((a_)->v, (b_)->v))))
/* If a waiter has waited this many times, it may set the MU_LONG_WAIT bit. */
#define LONG_WAIT_THRESHOLD 30
/* ---------- */
#define NOTIFIED_TIME(n_) (ATM_LOAD_ACQ (&(n_)->notified) != 0? nsync_time_zero : \
(n_)->expiry_time_valid? (n_)->expiry_time : nsync_time_no_deadline)
/* A waiter represents a single waiter on a cv or a mu.
To wait:
Allocate a waiter struct *w with new_waiter(), set w.waiting=1, and
w.cv_mu=nil or to the associated mu if waiting on a condition variable, then
queue w.nsync_dll on some queue, and then wait using:
while (ATM_LOAD_ACQ (&w.waiting) != 0) { nsync_mu_semaphore_p (&w.sem); }
Return *w to the freepool by calling free_waiter (w).
To wakeup:
Remove *w from the relevant queue then:
ATM_STORE_REL (&w.waiting, 0);
nsync_mu_semaphore_v (&w.sem); */
typedef struct {
uint32_t tag; /* debug DLL_NSYNC_WAITER, DLL_WAITER, DLL_WAITER_SAMECOND */
nsync_semaphore sem; /* Thread waits on this semaphore. */
struct nsync_waiter_s nw; /* An embedded nsync_waiter_s. */
struct nsync_mu_s_ *cv_mu; /* pointer to nsync_mu associated with a cv wait */
lock_type *l_type; /* Lock type of the mu, or nil if not associated with a mu. */
nsync_atomic_uint32_ remove_count; /* count of removals from queue */
struct wait_condition_s cond; /* A condition on which to acquire a mu. */
nsync_dll_element_ same_condition; /* Links neighbours in nw.q with same non-nil condition. */
int flags; /* see WAITER_* bits below */
} waiter;
static const uint32_t WAITER_TAG = 0x0590239f;
static const uint32_t NSYNC_WAITER_TAG = 0x726d2ba9;
#define WAITER_RESERVED 0x1 /* waiter reserved by a thread, even when not in use */
#define WAITER_IN_USE 0x2 /* waiter in use by a thread */
#define CONTAINER(t_,f_,p_) ((t_ *) (((char *) (p_)) - offsetof (t_, f_)))
#define ASSERT(x) do { if (!(x)) { *(volatile int *)0 = 0; } } while (0)
/* Return a pointer to the nsync_waiter_s containing nsync_dll_element_ *e. */
#define DLL_NSYNC_WAITER(e) (NSYNC_DEBUG? nsync_dll_nsync_waiter_ (e) : \
((struct nsync_waiter_s *)((e)->container)))
struct nsync_waiter_s *nsync_dll_nsync_waiter_ (nsync_dll_element_ *e);
/* Return a pointer to the waiter struct that *e is embedded in, where *e is an nw.q field. */
#define DLL_WAITER(e) (NSYNC_DEBUG? nsync_dll_waiter_ (e) : \
CONTAINER (waiter, nw, DLL_NSYNC_WAITER(e)))
waiter *nsync_dll_waiter_ (nsync_dll_element_ *e);
/* Return a pointer to the waiter struct that *e is embedded in, where *e is a
same_condition field. */
#define DLL_WAITER_SAMECOND(e) (NSYNC_DEBUG? nsync_dll_waiter_samecond_ (e) : \
((waiter *) ((e)->container)))
waiter *nsync_dll_waiter_samecond_ (nsync_dll_element_ *e);
/* Return a pointer to an unused waiter struct.
Ensures that the enclosed timer is stopped and its channel drained. */
waiter *nsync_waiter_new_ (void);
/* Return an unused waiter struct *w to the free pool. */
void nsync_waiter_free_ (waiter *w);
/* ---------- */
/* The internals of an nync_note. See internal/note.c for details of locking
discipline. */
struct nsync_note_s_ {
nsync_dll_element_ parent_child_link; /* parent's children, under parent->note_mu */
int expiry_time_valid; /* whether expiry_time is valid; r/o after init */
nsync_time expiry_time; /* expiry time, if expiry_time_valid != 0; r/o after init */
nsync_mu note_mu; /* protects fields below except "notified" */
nsync_cv no_children_cv; /* signalled when children becomes empty */
uint32_t disconnecting; /* non-zero => node is being disconnected */
nsync_atomic_uint32_ notified; /* non-zero if the note has been notified */
struct nsync_note_s_ *parent; /* points to parent, if any */
nsync_dll_element_ *children; /* list of children */
nsync_dll_element_ *waiters; /* list of waiters */
};
/* ---------- */
void nsync_mu_lock_slow_ (nsync_mu *mu, waiter *w, uint32_t clear, lock_type *l_type);
void nsync_mu_unlock_slow_ (nsync_mu *mu, lock_type *l_type);
nsync_dll_list_ nsync_remove_from_mu_queue_ (nsync_dll_list_ mu_queue, nsync_dll_element_ *e);
void nsync_maybe_merge_conditions_ (nsync_dll_element_ *p, nsync_dll_element_ *n);
nsync_time nsync_note_notified_deadline_ (nsync_note n);
int nsync_sem_wait_with_cancel_ (waiter *w, nsync_time abs_deadline,
nsync_note cancel_note);
NSYNC_CPP_END_
#endif /*NSYNC_INTERNAL_COMMON_H_*/

View file

@ -1,24 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PLATFORM_GCC_COMPILER_H_
#define NSYNC_PLATFORM_GCC_COMPILER_H_
#define INLINE __inline
#define UNUSED __attribute__((unused))
#define THREAD_LOCAL __thread
#define HAVE_THREAD_LOCAL 1
#endif /*NSYNC_PLATFORM_GCC_COMPILER_H_*/

View file

@ -1,21 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PLATFORM_X86_64_CPUTYPE_H_
#define NSYNC_PLATFORM_X86_64_CPUTYPE_H_
#define ATM_LD_IS_ACQ_ST_IS_REL_ 1
#endif /*NSYNC_PLATFORM_X86_64_CPUTYPE_H_*/

View file

@ -1,78 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_INTERNAL_DLL_H_
#define NSYNC_INTERNAL_DLL_H_
/* Doubly linked lists. */
#include "libc/thread/nsync_cpp.h"
NSYNC_CPP_START_
/* A nsync_dll_element_ represents an element of a doubly-linked list of waiters. */
typedef struct nsync_dll_element_s_ {
struct nsync_dll_element_s_ *next;
struct nsync_dll_element_s_ *prev;
void *container; /* points to the struct this nsync_dll struct is embedded in. */
} nsync_dll_element_;
/* A nsync_dll_list_ represents a list of nsync_dll_elements_. */
typedef nsync_dll_element_ *nsync_dll_list_; /* last elem of circular list; nil => empty; first is x.next. */
/* Initialize *e. */
void nsync_dll_init_ (nsync_dll_element_ *e, void *container);
/* Return whether list is empty. */
int nsync_dll_is_empty_ (nsync_dll_list_ list);
/* Remove *e from list, and returns the new list. */
nsync_dll_list_ nsync_dll_remove_ (nsync_dll_list_ list, nsync_dll_element_ *e);
/* Cause element *n and its successors to come after element *p.
Requires n and p are non-NULL and do not point at elements of the same list. */
void nsync_dll_splice_after_ (nsync_dll_element_ *p, nsync_dll_element_ *n);
/* Make element *e the first element of list, and return
the list. The resulting list will have *e as its first element, followed by
any elements in the same list as *e, followed by the elements that were
previously in list. Requires that *e not be in list. If e==NULL, list is
returned unchanged. */
nsync_dll_list_ nsync_dll_make_first_in_list_ (nsync_dll_list_ list, nsync_dll_element_ *e);
/* Make element *e the last element of list, and return
the list. The resulting list will have *e as its last element, preceded by
any elements in the same list as *e, preceded by the elements that were
previously in list. Requires that *e not be in list. If e==NULL, list is
returned unchanged. */
nsync_dll_list_ nsync_dll_make_last_in_list_ (nsync_dll_list_ list, nsync_dll_element_ *e);
/* Return a pointer to the first element of list, or NULL if list is empty. */
nsync_dll_element_ *nsync_dll_first_ (nsync_dll_list_ list);
/* Return a pointer to the last element of list, or NULL if list is empty. */
nsync_dll_element_ *nsync_dll_last_ (nsync_dll_list_ list);
/* Return a pointer to the next element of list following *e,
or NULL if there is no such element. */
nsync_dll_element_ *nsync_dll_next_ (nsync_dll_list_ list, nsync_dll_element_ *e);
/* Return a pointer to the previous element of list following *e,
or NULL if there is no such element. */
nsync_dll_element_ *nsync_dll_prev_ (nsync_dll_list_ list, nsync_dll_element_ *e);
NSYNC_CPP_END_
#endif /*NSYNC_INTERNAL_DLL_H_*/

View file

@ -1,27 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_INTERNAL_HEADERS_H_
#define NSYNC_INTERNAL_HEADERS_H_
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/platform.h"
#include "libc/thread/compiler.h"
#include "libc/thread/cputype.h"
#include "libc/thread/nsync.h"
#include "libc/thread/atomic.h"
#include "libc/thread/sem.h"
#endif /*NSYNC_INTERNAL_HEADERS_H_*/

View file

@ -1,28 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PUBLIC_NSYNC_H_
#define NSYNC_PUBLIC_NSYNC_H_
#include "libc/thread/nsync_mu.h"
#include "libc/thread/nsync_mu_wait.h"
#include "libc/thread/nsync_cv.h"
#include "libc/thread/nsync_note.h"
#include "libc/thread/nsync_counter.h"
#include "libc/thread/nsync_waiter.h"
#include "libc/thread/nsync_once.h"
#include "libc/thread/nsync_debug.h"
#endif /*NSYNC_PUBLIC_NSYNC_H_*/

View file

@ -1,67 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PUBLIC_NSYNC_ATOMIC_H_
#define NSYNC_PUBLIC_NSYNC_ATOMIC_H_
#include "libc/thread/nsync_cpp.h"
/* This file is not to be included directly by the client. It exists because
on some platforms, one cannot use a simple uint32_t with atomic operations.
*/
#if NSYNC_ATOMIC_TYPECHECK
#include "libc/fmt/conv.h"
#include "libc/inttypes.h"
NSYNC_CPP_START_
typedef struct { uint32_t value; } nsync_atomic_uint32_;
NSYNC_CPP_END_
#define NSYNC_ATOMIC_UINT32_INIT_ { 0 }
#define NSYNC_ATOMIC_UINT32_LOAD_(p) ((p)->value)
#define NSYNC_ATOMIC_UINT32_STORE_(p,v) ((p)->value = (v))
#define NSYNC_ATOMIC_UINT32_PTR_(p) (&(p)->value)
#elif NSYNC_ATOMIC_C11
#include "libc/intrin/atomic.h"
NSYNC_CPP_START_
typedef atomic_uint_least32_t nsync_atomic_uint32_;
NSYNC_CPP_END_
#define NSYNC_ATOMIC_UINT32_INIT_ 0
#define NSYNC_ATOMIC_UINT32_LOAD_(p) (*(p))
#define NSYNC_ATOMIC_UINT32_STORE_(p,v) (*(p) = (v))
#define NSYNC_ATOMIC_UINT32_PTR_(p) (p)
#elif NSYNC_ATOMIC_CPP11
#include "third_party/libcxx/atomic"
NSYNC_CPP_START_
typedef std::atomic<uint32_t> nsync_atomic_uint32_;
NSYNC_CPP_END_
#define NSYNC_ATOMIC_UINT32_INIT_ ATOMIC_VAR_INIT (0)
#define NSYNC_ATOMIC_UINT32_LOAD_(p) (std::atomic_load (p))
#define NSYNC_ATOMIC_UINT32_STORE_(p,v) (std::atomic_store ((p), (uint32_t) (v)))
#define NSYNC_ATOMIC_UINT32_PTR_(p) (p)
#else
#include "libc/fmt/conv.h"
#include "libc/inttypes.h"
NSYNC_CPP_START_
typedef uint32_t nsync_atomic_uint32_;
NSYNC_CPP_END_
#define NSYNC_ATOMIC_UINT32_INIT_ 0
#define NSYNC_ATOMIC_UINT32_LOAD_(p) (*(p))
#define NSYNC_ATOMIC_UINT32_STORE_(p,v) (*(p) = (v))
#define NSYNC_ATOMIC_UINT32_PTR_(p) (p)
#endif
#endif /*NSYNC_PUBLIC_NSYNC_ATOMIC_H_*/

View file

@ -1,30 +1,36 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 │
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "third_party/nsync/atomic.h"
#include "third_party/nsync/atomic.internal.h"
#include "third_party/nsync/common.internal.h"
#include "third_party/nsync/counter.h"
#include "third_party/nsync/dll.h"
#include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/wait_s.internal.h"
#include "third_party/nsync/waiter.h"
asm(".ident\t\"\\n\\n\
*NSYNC (Apache 2.0)\\n\
Copyright 2016 Google, Inc.\\n\
https://github.com/google/nsync\"");
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/platform.h"
#include "libc/thread/compiler.h"
#include "libc/thread/cputype.h"
#include "libc/thread/nsync.h"
#include "libc/thread/atomic.h"
#include "libc/thread/dll.h"
#include "libc/thread/sem.h"
#include "libc/thread/wait_internal.h"
#include "libc/thread/common.h"
NSYNC_CPP_START_
/* Internal details of nsync_counter. */
struct nsync_counter_s_ {
@ -106,7 +112,7 @@ uint32_t nsync_counter_wait (nsync_counter c, nsync_time abs_deadline) {
return (result);
}
static nsync_time counter_ready_time (void *v, struct nsync_waiter_s *nw UNUSED) {
static nsync_time counter_ready_time (void *v, struct nsync_waiter_s *nw) {
nsync_counter c = (nsync_counter) v;
nsync_time r;
ATM_STORE (&c->waited, 1);
@ -148,4 +154,4 @@ const struct nsync_waitable_funcs_s nsync_counter_waitable_funcs = {
&counter_dequeue
};
NSYNC_CPP_END_

View file

@ -1,64 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PUBLIC_NSYNC_COUNTER_H_
#define NSYNC_PUBLIC_NSYNC_COUNTER_H_
#include "libc/fmt/conv.h"
#include "libc/inttypes.h"
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/nsync_mu.h"
#include "libc/thread/nsync_atomic.h"
#include "libc/thread/nsync_time.h"
NSYNC_CPP_START_
struct nsync_dll_element_s_;
/* An nsync_counter represents an unsigned integer that can count up and down,
and wake waiters when zero. */
typedef struct nsync_counter_s_ *nsync_counter;
/* Return a freshly allocated nsync_counter with the specified value,
of NULL if an nsync_counter cannot be created.
Any non-NULL returned value should be passed to nsync_counter_free() when no
longer needed. */
nsync_counter nsync_counter_new (uint32_t value);
/* Free resources associated with c. Requires that c was allocated by
nsync_counter_new(), and no concurrent or future operations are applied to
c. */
void nsync_counter_free (nsync_counter c);
/* Add delta to c, and return its new value. It is a checkable runtime error
to decrement c below 0, or to increment c (i.e., apply a delta > 0) after a
waiter has waited. */
uint32_t nsync_counter_add (nsync_counter c, int32_t delta);
/* Return the current value of c. */
uint32_t nsync_counter_value (nsync_counter c);
/* Wait until c has value 0, or until abs_deadline, then return
the value of c. It is a checkable runtime error to increment c after
a waiter may have been woken due to the counter reaching zero.
If abs_deadline==nsync_time_no_deadline, the deadline
is far in the future. */
uint32_t nsync_counter_wait (nsync_counter c, nsync_time abs_deadline);
NSYNC_COUNTER_CPP_OVERLOAD_
NSYNC_CPP_END_
#endif /*NSYNC_PUBLIC_NSYNC_COUNTER_H_*/

View file

@ -1,46 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PUBLIC_NSYNC_CPP_H_
#define NSYNC_PUBLIC_NSYNC_CPP_H_
/* This header file permits compilation via a C++ compiler using the macros
NSYNC_CPP_START_, NSYNC_CPP_END_, and NSYNC_CPP_USING_.
NSYNC_CPP_START_ and NSYNC_CPP_END_ surround C code in the public library.
They put all public symbols into the "nsync" name space.
NSYNC_CPP_USING_ is used before C code (used for testing) that might use
public exports from this package. It makes symbols in the "nsync"
name space available without the "nsync::" prefix.
NSYNC_C_START_ and NSYNC_C_END_ surround C code in the C++ modules.
*/
#if defined(__cplusplus)
#define NSYNC_CPP_START_ namespace nsync {
#define NSYNC_CPP_END_ }
#define NSYNC_CPP_USING_ using namespace nsync;
#define NSYNC_C_START_ extern "C" {
#define NSYNC_C_END_ }
#else
#define NSYNC_CPP_START_
#define NSYNC_CPP_END_
#define NSYNC_CPP_USING_
#define NSYNC_C_START_
#define NSYNC_C_END_
#endif
#endif /*NSYNC_PUBLIC_NSYNC_CPP_H_*/

View file

@ -1,30 +1,33 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 │
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "libc/str/str.h"
#include "third_party/nsync/atomic.internal.h"
#include "third_party/nsync/common.internal.h"
#include "third_party/nsync/cv.h"
#include "third_party/nsync/dll.h"
#include "third_party/nsync/wait_s.internal.h"
#include "third_party/nsync/waiter.h"
asm(".ident\t\"\\n\\n\
*NSYNC (Apache 2.0)\\n\
Copyright 2016 Google, Inc.\\n\
https://github.com/google/nsync\"");
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/platform.h"
#include "libc/thread/compiler.h"
#include "libc/thread/cputype.h"
#include "libc/thread/nsync.h"
#include "libc/thread/dll.h"
#include "libc/thread/sem.h"
#include "libc/thread/wait_internal.h"
#include "libc/thread/common.h"
#include "libc/thread/atomic.h"
NSYNC_CPP_START_
/* Initialize *cv. */
void nsync_cv_init (nsync_cv *cv) {
@ -451,7 +454,7 @@ void nsync_cv_wait (nsync_cv *pcv, nsync_mu *pmu) {
nsync_cv_wait_with_deadline (pcv, pmu, nsync_time_no_deadline, NULL);
}
static nsync_time cv_ready_time (void *v UNUSED, struct nsync_waiter_s *nw) {
static nsync_time cv_ready_time (void *v, struct nsync_waiter_s *nw) {
nsync_time r;
r = (nw == NULL || ATM_LOAD_ACQ (&nw->waiting) != 0? nsync_time_no_deadline : nsync_time_zero);
return (r);
@ -491,5 +494,3 @@ const struct nsync_waitable_funcs_s nsync_cv_waitable_funcs = {
&cv_enqueue,
&cv_dequeue
};
NSYNC_CPP_END_

View file

@ -1,150 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PUBLIC_NSYNC_CV_H_
#define NSYNC_PUBLIC_NSYNC_CV_H_
#include "libc/fmt/conv.h"
#include "libc/inttypes.h"
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/nsync_mu.h"
#include "libc/thread/nsync_atomic.h"
#include "libc/thread/nsync_time.h"
NSYNC_CPP_START_
struct nsync_dll_element_s_;
struct nsync_note_s_;
/* An nsync_cv is a condition variable in the style of Mesa, Java, POSIX, and Go's sync.Cond.
It allows a thread to wait for a condition on state protected by a mutex,
and to proceed with the mutex held and the condition true.
See also nsync_mu_wait() and nsync_mu_wait_with_deadline(), which implement conditional
critical sections. In many cases, they are easier to use than condition
variables.
Usage:
after making the desired predicate true, call:
nsync_cv_signal (&cv); // If at most one thread can make use of the predicate becoming true.
or
nsync_cv_broadcast (&cv); // If multiple threads can make use of the predicate becoming true.
To wait for a predicate with no deadline (assuming nsync_cv_broadcast() or
nsync_cv_signal() is called whenever the predicate becomes true):
nsync_mu_lock (&mu;)
while (!some_predicate_protected_by_mu) { // the while-loop is required.
nsync_cv_wait (&cv, &mu);
}
// predicate is now true
nsync_mu_unlock (&mu);
To wait for a predicate with a deadline (assuming nsync_cv_broadcast() or
nsync_cv_signal() is called whenever the predicate becomes true):
nsync_mu_lock (&mu);
while (!some_predicate_protected_by_mu &&
nsync_cv_wait_with_deadline (&cv, &mu, abs_deadline, cancel_note) == 0) {
}
if (some_predicate_protected_by_mu) { // predicate is true
} else { // predicate is false, and deadline expired, or cancel_note was notified.
}
nsync_mu_unlock (&mu);
or, if the predicate is complex and you wish to write it just once and
inline, you could use the following instead of the for-loop above:
nsync_mu_lock (&mu);
int pred_is_true = 0;
int outcome = 0;
while (!(pred_is_true = some_predicate_protected_by_mu) && outcome == 0) {
outcome = nsync_cv_wait_with_deadline (&cv, &mu, abs_deadline, cancel_note);
}
if (pred_is_true) { // predicate is true
} else { // predicate is false, and deadline expired, or cancel_note was notified.
}
nsync_mu_unlock (&mu);
As the examples show, Mesa-style condition variables require that waits use
a loop that tests the predicate anew after each wait. It may be surprising
that these are preferred over the precise wakeups offered by the condition
variables in Hoare monitors. Imprecise wakeups make more efficient use of
the critical section, because threads can enter it while a woken thread is
still emerging from the scheduler, which may take thousands of cycles.
Further, they make the programme easier to read and debug by making the
predicate explicit locally at the wait, where the predicate is about to be
assumed; the reader does not have to infer the predicate by examining all
the places where wakeups may occur. */
typedef struct nsync_cv_s_ {
nsync_atomic_uint32_ word; /* see bits below */
struct nsync_dll_element_s_ *waiters; /* points to tail of list of waiters; under mu. */
} nsync_cv;
/* An nsync_cv should be zeroed to initialize, which can be accomplished by
initializing with static initializer NSYNC_CV_INIT, or by setting the entire
struct to 0, or using nsync_cv_init(). */
#define NSYNC_CV_INIT { NSYNC_ATOMIC_UINT32_INIT_, 0 }
void nsync_cv_init (nsync_cv *cv);
/* Wake at least one thread if any are currently blocked on *cv. If
the chosen thread is a reader on an nsync_mu, wake all readers and, if
possible, a writer. */
void nsync_cv_signal (nsync_cv *cv);
/* Wake all threads currently blocked on *cv. */
void nsync_cv_broadcast (nsync_cv *cv);
/* Atomically release "mu" (which must be held on entry) and block the caller
on *cv. Wait until awakened by a call to nsync_cv_signal() or
nsync_cv_broadcast(), or a spurious wakeup; then reacquire "mu", and return.
Equivalent to a call to nsync_mu_wait_with_deadline() with
abs_deadline==nsync_time_no_deadline, and cancel_note==NULL. Callers should use
nsync_cv_wait() in a loop, as with all standard Mesa-style condition
variables. See examples above. */
void nsync_cv_wait (nsync_cv *cv, nsync_mu *mu);
/* Atomically release "mu" (which must be held on entry)
and block the calling thread on *cv. It then waits until awakened by a
call to nsync_cv_signal() or nsync_cv_broadcast() (or a spurious wakeup), or by the time
reaching abs_deadline, or by cancel_note being notified. In all cases, it
reacquires "mu", and returns the reason for the call returned (0, ETIMEDOUT,
or ECANCELED). Use abs_deadline==nsync_time_no_deadline for no deadline, and
cancel_note==NULL for no cancellation. wait_with_deadline() should be used in a
loop, as with all Mesa-style condition variables. See examples above.
There are two reasons for using an absolute deadline, rather than a relative
timeout---these are why pthread_cond_timedwait() also uses an absolute
deadline. First, condition variable waits have to be used in a loop; with
an absolute times, the deadline does not have to be recomputed on each
iteration. Second, in most real programmes, some activity (such as an RPC
to a server, or when guaranteeing response time in a UI), there is a
deadline imposed by the specification or the caller/user; relative delays
can shift arbitrarily with scheduling delays, and so after multiple waits
might extend beyond the expected deadline. Relative delays tend to be more
convenient mostly in tests and trivial examples than they are in real
programmes. */
int nsync_cv_wait_with_deadline (nsync_cv *cv, nsync_mu *mu,
nsync_time abs_deadline,
struct nsync_note_s_ *cancel_note);
/* Like nsync_cv_wait_with_deadline(), but allow an arbitrary lock *v to be used,
given its (*lock)(mu) and (*unlock)(mu) routines. */
int nsync_cv_wait_with_deadline_generic (nsync_cv *cv,
void *mu, void (*lock) (void *), void (*unlock) (void *),
nsync_time abs_deadline,
struct nsync_note_s_ *cancel_note);
NSYNC_CV_CPP_OVERLOAD_
NSYNC_CPP_END_
#endif /*NSYNC_PUBLIC_NSYNC_CV_H_*/

View file

@ -1,35 +1,34 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 │
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "third_party/nsync/atomic.h"
#include "third_party/nsync/common.internal.h"
#include "third_party/nsync/dll.h"
#include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/wait_s.internal.h"
asm(".ident\t\"\\n\\n\
*NSYNC (Apache 2.0)\\n\
Copyright 2016 Google, Inc.\\n\
https://github.com/google/nsync\"");
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
/* Routines for debugging. */
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/platform.h"
#include "libc/thread/compiler.h"
#include "libc/thread/cputype.h"
#include "libc/thread/nsync.h"
#include "libc/thread/dll.h"
#include "libc/thread/sem.h"
#include "libc/thread/wait_internal.h"
#include "libc/thread/common.h"
#include "libc/thread/atomic.h"
NSYNC_CPP_START_
/* ---------- */
/* An emit_buf represents a buffer into which debug information can
be written. */
struct emit_buf {
@ -290,5 +289,3 @@ char *nsync_cv_debugger (nsync_cv *cv) {
(int) sizeof (nsync_debug_buf)),
cv, 0, 1));
}
NSYNC_CPP_END_

View file

@ -1,55 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PUBLIC_NSYNC_DEBUG_H_
#define NSYNC_PUBLIC_NSYNC_DEBUG_H_
/* Debugging operations for mutexes and condition variables.
These operations should not be relied upon for normal functionality. The
implementation may be slow, output formats may change, and the
implementation is free to yield the empty string. */
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/nsync_mu.h"
#include "libc/thread/nsync_cv.h"
NSYNC_CPP_START_
/* Place in buf[0,..,n-1] a nul-terminated, human readable string indicative of
some of the internal state of the mutex or condition variable, and return
buf. If n>=4, buffer overflow is indicated by placing the characters "..."
at the end of the string.
The *_and_waiters() variants attempt to output the waiter lists in addition
to the basic state. These variants may acquire internal locks and follow
internal pointers. Thus, they are riskier if invoked in an address space
whose overall health is uncertain. */
char *nsync_mu_debug_state (nsync_mu *mu, char *buf, int n);
char *nsync_cv_debug_state (nsync_cv *cv, char *buf, int n);
char *nsync_mu_debug_state_and_waiters (nsync_mu *mu, char *buf, int n);
char *nsync_cv_debug_state_and_waiters (nsync_cv *cv, char *buf, int n);
/* Like nsync_*_debug_state_and_waiters(), but ignoring all locking and safety
considerations, and using an internal, possibly static buffer that may be
overwritten by subsequent or concurrent calls to these routines. These
variants should be used only from an interactive debugger, when all other
threads are stopped; the debugger is expected to recover from errors. */
char *nsync_mu_debugger (nsync_mu *mu);
char *nsync_cv_debugger (nsync_cv *cv);
NSYNC_CPP_END_
#endif /*NSYNC_PUBLIC_NSYNC_DEBUG_H_*/

View file

@ -1,115 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PUBLIC_NSYNC_MU_H_
#define NSYNC_PUBLIC_NSYNC_MU_H_
#include "libc/fmt/conv.h"
#include "libc/inttypes.h"
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/nsync_atomic.h"
NSYNC_CPP_START_
struct nsync_dll_element_s_;
/* An nsync_mu is a lock. If initialized to all zeroes, it is valid and unlocked.
An nsync_mu can be "free", held by a single thread (aka fiber, goroutine) in
"write" (exclusive) mode, or by many threads in "read" (shared) mode. A
thread that acquires it should eventually release it. It is illegal to
acquire an nsync_mu in one thread and release it in another. It is
illegal for a thread to reacquire an nsync_mu while holding it (even a
second share of a "read" lock).
Example usage:
static struct foo {
nsync_mu mu; // protects invariant a+b==0 on fields below.
int a;
int b;
} p = { NSYNC_MU_INIT, 0, 0 };
....
nsync_mu_lock (&p.mu);
// The current thread now has exclusive access to p.a and p.b; invariant assumed true.
p.a++;
p.b--; // restore invariant p.a+p.b==0 before releasing p.mu
nsync_mu_unlock (&p.mu)
Mutexes can be used with condition variables; see nsync_cv.h.
nsync_mu_wait() and nsync_mu_wait_with_deadline() can be used instead of
condition variables. See nsync_mu_wait.h for more details.
Example use of nsync_mu_wait() to wait for p.a==0, using definition above:
int a_is_zero (const void *condition_arg) {
return (((const struct foo *)condition_arg)->a == 0);
}
...
nsync_mu_lock (&p.mu);
nsync_mu_wait (&p.mu, &a_is_zero, &p, NULL);
// The current thread now has exclusive access to p.a and p.b, and p.a==0.
...
nsync_mu_unlock (&p.mu); */
typedef struct nsync_mu_s_ {
nsync_atomic_uint32_ word; /* internal use only */
struct nsync_dll_element_s_ *waiters; /* internal use only */
} nsync_mu;
/* An nsync_mu should be zeroed to initialize, which can be accomplished by
initializing with static initializer NSYNC_MU_INIT, or by setting the entire
structure to all zeroes, or using nsync_mu_init(). */
#define NSYNC_MU_INIT { NSYNC_ATOMIC_UINT32_INIT_, 0 }
void nsync_mu_init (nsync_mu *mu);
/* Block until *mu is free and then acquire it in writer mode.
Requires that the calling thread not already hold *mu in any mode. */
void nsync_mu_lock (nsync_mu *mu);
/* Unlock *mu, which must have been acquired in write mode by the calling
thread, and wake waiters, if appropriate. */
void nsync_mu_unlock (nsync_mu *mu);
/* Attempt to acquire *mu in writer mode without blocking, and return non-zero
iff successful. Return non-zero with high probability if *mu was free
on entry. */
int nsync_mu_trylock (nsync_mu *mu);
/* Block until *mu can be acquired in reader mode and then acquire it.
Requires that the calling thread not already hold *mu in any mode. */
void nsync_mu_rlock (nsync_mu *mu);
/* Unlock *mu, which must have been acquired in read mode by the calling
thread, and wake waiters, if appropriate. */
void nsync_mu_runlock (nsync_mu *mu);
/* Attempt to acquire *mu in reader mode without blocking, and return non-zero
iff successful. Return non-zero with high probability if *mu was free on
entry. Perhaps fail to acquire if a writer is waiting, to avoid starvation.
*/
int nsync_mu_rtrylock (nsync_mu *mu);
/* May abort if *mu is not held in write mode by the calling thread. */
void nsync_mu_assert_held (const nsync_mu *mu);
/* May abort if *mu is not held in read or write mode
by the calling thread. */
void nsync_mu_rassert_held (const nsync_mu *mu);
/* Return whether *mu is held in read mode.
Requires that the calling thread holds *mu in some mode. */
int nsync_mu_is_reader (const nsync_mu *mu);
NSYNC_CPP_END_
#endif /*NSYNC_PUBLIC_NSYNC_MU_H_*/

View file

@ -1,30 +1,31 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 │
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "third_party/nsync/atomic.h"
#include "third_party/nsync/common.internal.h"
#include "third_party/nsync/dll.h"
#include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/wait_s.internal.h"
asm(".ident\t\"\\n\\n\
*NSYNC (Apache 2.0)\\n\
Copyright 2016 Google, Inc.\\n\
https://github.com/google/nsync\"");
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/platform.h"
#include "libc/thread/compiler.h"
#include "libc/thread/cputype.h"
#include "libc/thread/nsync.h"
#include "libc/thread/dll.h"
#include "libc/thread/sem.h"
#include "libc/thread/wait_internal.h"
#include "libc/thread/common.h"
#include "libc/thread/atomic.h"
NSYNC_CPP_START_
/* Attempt to remove waiter *w from *mu's
waiter queue. If successful, leave the lock held in mode *l_type, and
@ -317,4 +318,4 @@ void nsync_mu_unlock_without_wakeup (nsync_mu *mu) {
IGNORE_RACES_END ();
}
NSYNC_CPP_END_

View file

@ -1,129 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PUBLIC_NSYNC_MU_WAIT_H_
#define NSYNC_PUBLIC_NSYNC_MU_WAIT_H_
/* nsync_mu_wait() and nsync_mu_wait_with_deadline() can be used instead of condition
variables. In many straightforward situations they are of equivalent
performance and are somewhat easier to use, because unlike condition
variables, they do not require that the waits be placed in a loop, and they
do not require explicit wakeup calls. Example:
Definitions:
static nsync_mu mu = NSYNC_MU_INIT;
static int i = 0; // protected by mu
// Condition for use with nsync_mu_wait().
static int int_is_zero (const void *v) { return (*(const int *)v == 0); }
Waiter:
nsync_mu_lock (&mu);
// Wait until i is zero.
nsync_mu_wait (&mu, &int_is_zero, &i, NULL);
// i is known to be zero here.
// ...
nsync_mu_unlock (&mu);
Thread potentially making i zero:
nsync_mu_lock (&mu);
i--;
// No need to signal that i may have become zero. The unlock call below
// will evaluate waiters' conditions to decide which to wake.
nsync_mu_unlock (&mu);
It is legal to use conditional critical sections and condition variables
on the same mutex.
--------------
The implementation benefits from determining whether waiters are waiting for
the same condition; it may then evaluate a condition once on behalf
of several waiters. Two waiters have equal condition if their "condition"
pointers are equal, and either:
- their "condition_arg" pointers are equal, or
- "condition_arg_eq" is non-null and
(*condition_arg_eq) (condition_arg0, condition_arg1) returns non-zero.
*condition_arg_eq will not be invoked unless the "condition" pointers
are equal, and the "condition_arg" pointers are unequal.
If many waiters wait for distinct conditions simultaneously, condition
variables may be faster.
*/
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/nsync_mu.h"
#include "libc/thread/nsync_time.h"
NSYNC_CPP_START_
struct nsync_note_s_; /* forward declaration for an nsync_note */
/* Return when (*condition) (condition_arg) is true. Perhaps unlock and relock
*mu while blocked waiting for the condition to become true. nsync_mu_wait()
is equivalent to nsync_mu_wait_with_deadline() with
abs_deadline==nsync_time_no_deadline, and cancel_note==NULL.
Requires that *mu be held on entry.
See nsync_mu_wait_with_deadline() for more details on *condition and
*condition_arg_eq. */
void nsync_mu_wait (nsync_mu *mu, int (*condition) (const void *condition_arg),
const void *condition_arg,
int (*condition_arg_eq) (const void *a, const void *b));
/* Return when at least one of: (*condition) (condition_arg) is true, the
deadline expires, or *cancel_note is notified. Perhaps unlock and relock *mu
while blocked waiting for one of these events, but always return with *mu
held. Return 0 iff the (*condition) (condition_arg) is true on return, and
otherwise either ETIMEDOUT or ECANCELED, depending on why the call returned
early. Callers should use abs_deadline==nsync_time_no_deadline for no
deadline, and cancel_note==NULL for no cancellation.
Requires that *mu be held on entry.
The implementation may call *condition from any thread using the mutex, and
while holding *mu in either read or write mode; it guarantees that any
thread calling *condition will hold *mu in some mode.
Requires that (*condition) (condition_arg) neither modify state protected by
*mu, nor return a value dependent on state not protected by *mu. To depend
on time, use the abs_deadline parameter.
(Conventional use of condition variables have the same restrictions on the
conditions tested by the while-loop.)
If non-null, condition_arg_eq should return whether two condition_arg
calls with the same "condition" pointer are considered equivalent; it should
have no side-effects. */
int nsync_mu_wait_with_deadline (nsync_mu *mu,
int (*condition) (const void *condition_arg),
const void *condition_arg,
int (*condition_arg_eq) (const void *a, const void *b),
nsync_time abs_deadline,
struct nsync_note_s_ *cancel_note);
/* Unlock *mu, which must be held in write mode, and wake waiters, if
appropriate. Unlike nsync_mu_unlock(), this call is not required to wake
nsync_mu_wait/nsync_mu_wait_with_deadline calls on conditions that were
false before this thread acquired the lock. This call should be used only
at the end of critical sections for which:
- nsync_mu_wait and/or nsync_mu_wait_with_deadline are in use on the same
mutex,
- this critical section cannot make the condition true for any of those
nsync_mu_wait/nsync_mu_wait_with_deadline waits, and
- when performance is significantly improved by using this call. */
void nsync_mu_unlock_without_wakeup (nsync_mu *mu);
NSYNC_MU_WAIT_CPP_OVERLOAD_
NSYNC_CPP_END_
#endif /*NSYNC_PUBLIC_NSYNC_MU_WAIT_H_*/

View file

@ -1,30 +1,35 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 │
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "third_party/nsync/atomic.h"
#include "third_party/nsync/common.internal.h"
#include "third_party/nsync/dll.h"
#include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/mu_wait.h"
#include "third_party/nsync/wait_s.internal.h"
#include "third_party/nsync/waiter.h"
asm(".ident\t\"\\n\\n\
*NSYNC (Apache 2.0)\\n\
Copyright 2016 Google, Inc.\\n\
https://github.com/google/nsync\"");
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/platform.h"
#include "libc/thread/compiler.h"
#include "libc/thread/cputype.h"
#include "libc/thread/nsync.h"
#include "libc/thread/dll.h"
#include "libc/thread/sem.h"
#include "libc/thread/wait_internal.h"
#include "libc/thread/common.h"
#include "libc/thread/atomic.h"
NSYNC_CPP_START_
/* Locking discipline for the nsync_note implementation:
@ -256,7 +261,7 @@ nsync_time nsync_note_expiry (nsync_note n) {
return (n->expiry_time);
}
static nsync_time note_ready_time (void *v, struct nsync_waiter_s *nw UNUSED) {
static nsync_time note_ready_time (void *v, struct nsync_waiter_s *nw) {
return (nsync_note_notified_deadline_ ((nsync_note)v));
}
@ -299,5 +304,3 @@ const struct nsync_waitable_funcs_s nsync_note_waitable_funcs = {
&note_enqueue,
&note_dequeue
};
NSYNC_CPP_END_

View file

@ -1,68 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PUBLIC_NSYNC_NOTE_H_
#define NSYNC_PUBLIC_NSYNC_NOTE_H_
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/nsync_time.h"
NSYNC_CPP_START_
/* An nsync_note represents a single bit that can transition from 0 to 1 at
most once. When 1, the note is said to be notified. There are operations
to wait for the transition, which can be triggered either by an explicit
call, or timer expiry. Notes can have parent notes; a note becomes notified
if its parent becomes notified. */
typedef struct nsync_note_s_ *nsync_note;
/* Return a freshly allocated nsync_note, or NULL if an nsync_note cannot be
created.
If parent!=NULL, the allocated nsync_note's parent will be parent. The
newaly allocated note will be automatically notified at abs_deadline, and is
notified at initialization if abs_deadline==nsync_zero_time.
nsync_notes should be passed to nsync_note_free() when no longer needed. */
nsync_note nsync_note_new (nsync_note parent, nsync_time abs_deadline);
/* Free resources associated with n. Requires that n was allocated by
nsync_note_new(), and no concurrent or future operations are applied to n
directly.
It is legal to call nsync_note_free() on a node even if it has a parent or
children that are in use; if n has both a parent and children, n's
parent adopts its children. */
void nsync_note_free (nsync_note n);
/* Notify n and all its descendants. */
void nsync_note_notify (nsync_note n);
/* Return whether n has been notified. */
int nsync_note_is_notified (nsync_note n);
/* Wait until n has been notified or abs_deadline is reached, and return
whether n has been notified. If abs_deadline==nsync_time_no_deadline,
the deadline is far in the future. */
int nsync_note_wait (nsync_note n, nsync_time abs_deadline);
/* Return the expiry time associated with n.
This is the minimum of the abs_deadline passed on creation and that of any
of its ancestors. */
nsync_time nsync_note_expiry (nsync_note n);
NSYNC_NOTE_CPP_OVERLOAD_
NSYNC_CPP_END_
#endif /*NSYNC_PUBLIC_NSYNC_NOTE_H_*/

View file

@ -1,30 +1,33 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 │
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "third_party/nsync/atomic.h"
#include "third_party/nsync/atomic.internal.h"
#include "third_party/nsync/common.internal.h"
#include "third_party/nsync/dll.h"
#include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/once.h"
#include "third_party/nsync/wait_s.internal.h"
asm(".ident\t\"\\n\\n\
*NSYNC (Apache 2.0)\\n\
Copyright 2016 Google, Inc.\\n\
https://github.com/google/nsync\"");
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/platform.h"
#include "libc/thread/compiler.h"
#include "libc/thread/cputype.h"
#include "libc/thread/nsync.h"
#include "libc/thread/dll.h"
#include "libc/thread/sem.h"
#include "libc/thread/wait_internal.h"
#include "libc/thread/common.h"
#include "libc/thread/atomic.h"
NSYNC_CPP_START_
/* An once_sync_s struct contains a lock, and a condition variable on which
threads may wait for an nsync_once to be initialized by another thread.
@ -144,5 +147,3 @@ void nsync_run_once_arg_spin (nsync_once *once, void (*farg) (void *arg), void *
}
IGNORE_RACES_END ();
}
NSYNC_CPP_END_

View file

@ -1,51 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PUBLIC_NSYNC_ONCE_H_
#define NSYNC_PUBLIC_NSYNC_ONCE_H_
#include "libc/fmt/conv.h"
#include "libc/inttypes.h"
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/nsync_atomic.h"
NSYNC_CPP_START_
/* An nsync_once allows a function to be called exactly once, when first referenced. */
typedef nsync_atomic_uint32_ nsync_once;
/* An initializer for nsync_once; it is guaranteed to be all zeroes. */
#define NSYNC_ONCE_INIT NSYNC_ATOMIC_UINT32_INIT_
/* The first time nsync_run_once() or nsync_run_once_arg() is applied to *once,
the supplied function is run (with argument, in the case of nsync_run_once_arg()).
Other callers will wait until the run of the function is complete, and then
return without running the function again. */
void nsync_run_once (nsync_once *once, void (*f) (void));
void nsync_run_once_arg (nsync_once *once, void (*farg) (void *arg), void *arg);
/* Same as nsync_run_once()/nsync_run_once_arg() but uses a spinloop.
Can be used on the same nsync_once as nsync_run_once/nsync_run_once_arg().
These *_spin variants should be used only in contexts where normal blocking
is disallowed, such as within user-space schedulers, when the runtime is
not fully initialized, etc. They provide no significant performance benefit,
and they should be avoided in normal code. */
void nsync_run_once_spin (nsync_once *once, void (*f) (void));
void nsync_run_once_arg_spin (nsync_once *once, void (*farg) (void *arg), void *arg);
NSYNC_CPP_END_
#endif /*NSYNC_PUBLIC_NSYNC_ONCE_H_*/

View file

@ -1,42 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "libc/thread/headers.h"
NSYNC_CPP_START_
/* Write the nul-terminated string s[] to file descriptor fd. */
static void writestr (int fd, const char *s) {
int len = strlen (s);
int n = 0;
while (len != 0 && n >= 0) {
n = write (fd, s, len);
if (n >= 0) {
len -= n;
s += n;
} else if (n == -1 && errno == EINTR) {
n = 0;
}
}
}
/* Abort after printing the nul-terminated string s[]. */
void nsync_panic_ (const char *s) {
writestr (2, "panic: ");
writestr (2, s);
abort ();
}
NSYNC_CPP_END_

View file

@ -0,0 +1,85 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 │
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "libc/errno.h"
#include "third_party/nsync/atomic.h"
#include "third_party/nsync/atomic.internal.h"
#include "third_party/nsync/common.internal.h"
#include "third_party/nsync/dll.h"
#include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/wait_s.internal.h"
asm(".ident\t\"\\n\\n\
*NSYNC (Apache 2.0)\\n\
Copyright 2016 Google, Inc.\\n\
https://github.com/google/nsync\"");
// clang-format off
/* Wait until one of:
w->sem is non-zero----decrement it and return 0.
abs_deadline expires---return ETIMEDOUT.
cancel_note is non-NULL and *cancel_note becomes notified---return ECANCELED. */
int nsync_sem_wait_with_cancel_ (waiter *w, nsync_time abs_deadline,
nsync_note cancel_note) {
int sem_outcome;
if (cancel_note == NULL) {
sem_outcome = nsync_mu_semaphore_p_with_deadline (&w->sem, abs_deadline);
} else {
nsync_time cancel_time;
cancel_time = nsync_note_notified_deadline_ (cancel_note);
sem_outcome = ECANCELED;
if (nsync_time_cmp (cancel_time, nsync_time_zero) > 0) {
struct nsync_waiter_s nw;
nw.tag = NSYNC_WAITER_TAG;
nw.sem = &w->sem;
nsync_dll_init_ (&nw.q, &nw);
ATM_STORE (&nw.waiting, 1);
nw.flags = 0;
nsync_mu_lock (&cancel_note->note_mu);
cancel_time = NOTIFIED_TIME (cancel_note);
if (nsync_time_cmp (cancel_time, nsync_time_zero) > 0) {
nsync_time local_abs_deadline;
int deadline_is_nearer = 0;
cancel_note->waiters = nsync_dll_make_last_in_list_ (
cancel_note->waiters, &nw.q);
local_abs_deadline = cancel_time;
if (nsync_time_cmp (abs_deadline, cancel_time) < 0) {
local_abs_deadline = abs_deadline;
deadline_is_nearer = 1;
}
nsync_mu_unlock (&cancel_note->note_mu);
sem_outcome = nsync_mu_semaphore_p_with_deadline (&w->sem,
local_abs_deadline);
if (sem_outcome == ETIMEDOUT && !deadline_is_nearer) {
sem_outcome = ECANCELED;
nsync_note_notify (cancel_note);
}
nsync_mu_lock (&cancel_note->note_mu);
cancel_time = NOTIFIED_TIME (cancel_note);
if (nsync_time_cmp (cancel_time,
nsync_time_zero) > 0) {
cancel_note->waiters = nsync_dll_remove_ (
cancel_note->waiters, &nw.q);
}
}
nsync_mu_unlock (&cancel_note->note_mu);
}
}
return (sem_outcome);
}

View file

@ -0,0 +1,33 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 │
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "third_party/nsync/common.internal.h"
#include "third_party/nsync/mu_semaphore.h"
asm(".ident\t\"\\n\\n\
*NSYNC (Apache 2.0)\\n\
Copyright 2016 Google, Inc.\\n\
https://github.com/google/nsync\"");
// clang-format off
/* Wait until one of:
w->sem is non-zero----decrement it and return 0.
abs_deadline expires---return ETIMEDOUT.
Ignores cancel_note. */
int nsync_sem_wait_with_cancel_ (waiter *w, nsync_time abs_deadline, nsync_note cancel_note) {
return (nsync_mu_semaphore_p_with_deadline (&w->sem, abs_deadline));
}

View file

@ -1,132 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "libc/thread/headers.h"
NSYNC_CPP_START_
static int futex (int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2,
int val3) {
return (syscall (__NR_futex, uaddr, op, val, timeout, uaddr2, val3));
}
/* Check that atomic operations on nsync_atomic_uint32_ can be applied to int. */
static const int assert_int_size = 1 /
(sizeof (assert_int_size) == sizeof (uint32_t) &&
sizeof (nsync_atomic_uint32_) == sizeof (uint32_t));
#if defined(FUTEX_PRIVATE_FLAG)
#define FUTEX_PRIVATE_FLAG_ FUTEX_PRIVATE_FLAG
#else
#define FUTEX_PRIVATE_FLAG_ 0
#endif
#if defined(FUTEX_WAIT_BITSET)
#define FUTEX_WAIT_ (FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG_ | FUTEX_CLOCK_REALTIME)
#define FUTEX_WAIT_BITS_ FUTEX_BITSET_MATCH_ANY
#else
#define FUTEX_WAIT_ (FUTEX_WAIT | FUTEX_PRIVATE_FLAG_)
#define FUTEX_WAIT_BITS_ 0
#endif
#define FUTEX_WAKE_ (FUTEX_WAKE | FUTEX_PRIVATE_FLAG_)
#define FUTEX_TIMEOUT_IS_ABSOLUTE (FUTEX_WAIT_BITS_ != 0)
#define ASSERT(x) do { if (!(x)) { *(volatile int *)0 = 0; } } while (0)
struct futex {
int i; /* lo half=count; hi half=waiter count */
};
static nsync_semaphore *sem_big_enough_for_futex = (nsync_semaphore *) (uintptr_t)(1 /
(sizeof (struct futex) <= sizeof (*sem_big_enough_for_futex)));
/* Initialize *s; the initial value is 0. */
void nsync_mu_semaphore_init (nsync_semaphore *s) {
struct futex *f = (struct futex *) s;
f->i = 0;
}
/* Wait until the count of *s exceeds 0, and decrement it. */
void nsync_mu_semaphore_p (nsync_semaphore *s) {
struct futex *f = (struct futex *) s;
int i;
do {
i = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i);
if (i == 0) {
int futex_result = futex (&f->i, FUTEX_WAIT_, i, NULL,
NULL, FUTEX_WAIT_BITS_);
ASSERT (futex_result == 0 || errno == EINTR ||
errno == EWOULDBLOCK);
}
} while (i == 0 || !ATM_CAS_ACQ ((nsync_atomic_uint32_ *) &f->i, i, i-1));
}
/* Wait until one of:
the count of *s is non-zero, in which case decrement *s and return 0;
or abs_deadline expires, in which case return ETIMEDOUT. */
int nsync_mu_semaphore_p_with_deadline (nsync_semaphore *s, nsync_time abs_deadline) {
struct futex *f = (struct futex *)s;
int i;
int result = 0;
do {
i = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i);
if (i == 0) {
int futex_result;
struct timespec ts_buf;
const struct timespec *ts = NULL;
if (nsync_time_cmp (abs_deadline, nsync_time_no_deadline) != 0) {
memset (&ts_buf, 0, sizeof (ts_buf));
if (FUTEX_TIMEOUT_IS_ABSOLUTE) {
ts_buf.tv_sec = NSYNC_TIME_SEC (abs_deadline);
ts_buf.tv_nsec = NSYNC_TIME_NSEC (abs_deadline);
} else {
nsync_time now;
now = nsync_time_now ();
if (nsync_time_cmp (now, abs_deadline) > 0) {
ts_buf.tv_sec = 0;
ts_buf.tv_nsec = 0;
} else {
nsync_time rel_deadline;
rel_deadline = nsync_time_sub (abs_deadline, now);
ts_buf.tv_sec = NSYNC_TIME_SEC (rel_deadline);
ts_buf.tv_nsec = NSYNC_TIME_NSEC (rel_deadline);
}
}
ts = &ts_buf;
}
futex_result = futex (&f->i, FUTEX_WAIT_, i, ts, NULL, FUTEX_WAIT_BITS_);
ASSERT (futex_result == 0 || errno == EINTR || errno == EWOULDBLOCK ||
errno == ETIMEDOUT);
/* Some systems don't wait as long as they are told. */
if (futex_result == -1 && errno == ETIMEDOUT &&
nsync_time_cmp (abs_deadline, nsync_time_now ()) <= 0) {
result = ETIMEDOUT;
}
}
} while (result == 0 && (i == 0 || !ATM_CAS_ACQ ((nsync_atomic_uint32_ *) &f->i, i, i - 1)));
return (result);
}
/* Ensure that the count of *s is at least 1. */
void nsync_mu_semaphore_v (nsync_semaphore *s) {
struct futex *f = (struct futex *) s;
uint32_t old_value;
do {
old_value = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i);
} while (!ATM_CAS_REL ((nsync_atomic_uint32_ *) &f->i, old_value, old_value+1));
ASSERT (futex (&f->i, FUTEX_WAKE_, 1, NULL, NULL, 0) >= 0);
}
NSYNC_CPP_END_

View file

@ -1,62 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PUBLIC_NSYNC_TIME_H_
#define NSYNC_PUBLIC_NSYNC_TIME_H_
#include "libc/thread/nsync_cpp.h"
#include "libc/thread/nsync_time_internal.h"
/* The type nsync_time represents the interval elapsed between two moments in
time. Often the first such moment is an address-space-wide epoch, such as
the Unix epoch, but clients should not rely on the epoch in one address
space being the same as that in another. Intervals relative to the epoch
are known as absolute times.
The internals of nsync_time should be treated as opaque by clients.
See nsync_time_internal.h. */
NSYNC_CPP_START_
extern const nsync_time nsync_time_no_deadline; /* A deadline infinitely far in the future. */
extern const nsync_time nsync_time_zero; /* The zero delay, or an expired deadline. */
nsync_time nsync_time_now (void); /* Return the current time since the epoch. */
/* Sleep for the specified delay. Returns the unslept time
which may be non-zero if the call was interrupted. */
nsync_time nsync_time_sleep (nsync_time delay);
/* Return a+b */
nsync_time nsync_time_add (nsync_time a, nsync_time b);
/* Return a-b */
nsync_time nsync_time_sub (nsync_time a, nsync_time b);
/* Return +ve, 0, or -ve according to whether a>b, a==b, or a<b. */
int nsync_time_cmp (nsync_time a, nsync_time b);
/* Return the specified number of milliseconds as a time. */
nsync_time nsync_time_ms (unsigned ms);
/* Return the specified number of microseconds as a time. */
nsync_time nsync_time_us (unsigned us);
/* Return an nsync_time constructed from second and nanosecond components */
nsync_time nsync_time_s_ns (time_t s, unsigned ns);
NSYNC_CPP_END_
#endif /*NSYNC_PUBLIC_NSYNC_TIME_H_*/

View file

@ -1,21 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PLATFORM_POSIX_NSYNC_TIME_INIT_H_
#define NSYNC_PLATFORM_POSIX_NSYNC_TIME_INIT_H_
#define NSYNC_TIME_STATIC_INIT(t,ns) { (t), (ns) }
#endif /*NSYNC_PLATFORM_POSIX_NSYNC_TIME_INIT_H_*/

View file

@ -1,215 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PUBLIC_NSYNC_TIME_INTERNAL_H_
#define NSYNC_PUBLIC_NSYNC_TIME_INTERNAL_H_
#include "libc/thread/nsync_cpp.h"
/* Internal details of the implementation of the type nsync_time.
The type nsync_time can have different implementations on different
platforms, because the world has many different representations of time.
Further, the "epoch" of absolute times can vary from address space to
address space.
On monotonic clocks: In our testing, we found that the monotonic clock on
various popular systems (such as Linux, and some BSD variants) was no better
behaved than the realtime clock, and routinely took large steps backwards,
especially on multiprocessors. Given that "monotonic" doesn't seem to mean
what it says, implementers of nsync_time might consider retaining the
simplicity of a single epoch within an address space, by configuring any
time synchronization mechanism (like ntp) to adjust for leap seconds by
adjusting the rate, rather than with a backwards step. */
#if NSYNC_USE_GPR_TIMESPEC
// MISSING #include "grpc/support/time.h"
NSYNC_CPP_START_
typedef gpr_timespec nsync_time;
#define NSYNC_TIME_SEC(t) ((t).tv_sec)
#define NSYNC_TIME_NSEC(t) ((t).tv_nsec)
NSYNC_CPP_END_
#elif defined(NSYNC_USE_INT_TIME)
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timeval.h"
#include "libc/calls/weirdtypes.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/sched.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
NSYNC_CPP_START_
typedef NSYNC_USE_INT_TIME nsync_time;
#define NSYNC_TIME_SEC(t) (sizeof (nsync_time) >= 8? \
(t) / (1000 * 1000 * 1000): \
((t) / 1000))
#define NSYNC_TIME_NSEC(t) (sizeof (nsync_time) >= 8? \
(t) % (1000 * 1000 * 1000): \
(((t) % 1000) * 1000 * 1000))
#define NSYNC_TIME_MAX_ MAX_INT_TYPE (nsync_time)
NSYNC_CPP_END_
#elif defined(NSYNC_USE_FLOATING_TIME)
#include "libc/math.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timeval.h"
#include "libc/calls/weirdtypes.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/sched.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
NSYNC_CPP_START_
typedef NSYNC_USE_FLOATING_TIME nsync_time;
#define NSYNC_TIME_SEC(t) (trunc ((t) / (nsync_time) (1000 * 1000 * 1000)))
#define NSYNC_TIME_NSEC(t) ((t) - ((1000 * 1000 * 1000) * NSYNC_TIME_SEC (t)))
#define NSYNC_TIME_MAX_ DBL_MAX
NSYNC_CPP_END_
#elif NSYNC_USE_DEBUG_TIME
/* Check that the library can be built with a different time struct. */
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timeval.h"
#include "libc/calls/weirdtypes.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/sched.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
NSYNC_CPP_START_
typedef struct {
time_t seconds;
unsigned nanoseconds;
} nsync_time;
#define NSYNC_TIME_SEC(t) ((t).seconds)
#define NSYNC_TIME_NSEC(t) ((t).nanoseconds)
NSYNC_CPP_END_
#elif defined(__cplusplus) && \
(NSYNC_USE_CPP11_TIMEPOINT || (__cplusplus >= 201103L) || (_MSC_VER >= 1700))
/* The inline functions below provide function overloads that accept the most
likely C++11 time type(s).
C++11 time types have many variations and subtleties:
- There are multiple clocks with potentially differing epochs; these clocks
are not necessarily phase-locked to the same rate, making conversion and
comparison between clocks tricky.
- Relative and absolute times are distinguished in the type system.
- Either integral or floating point counters may be used to represent time
intervals, and code valid with one may not be valid with the other
(see std::chrono::treat_as_floating_point).
- A counter increment of one can represent any rational number of seconds
(for whatever "seconds" means for this clock).
- Conversions between duration types may round or truncate at the
implementation's discretion.
- As mentioned above, common implementations of the default monotonic clock
("steady_clock") illegally allow a thread to observe time going backwards,
especially in the face of scheduling on a different CPU, making its use
misleading, at best.
I've chosen to handle this complexity by doing a conversion to absolute
timespec at the interface layer, so all the C++ complication is here, rather
than spread throughout the library. */
#include "third_party/libcxx/chrono"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timeval.h"
#include "libc/calls/weirdtypes.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/sched.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
NSYNC_CPP_START_
typedef struct timespec nsync_time;
#define NSYNC_TIME_SEC(t) ((t).tv_sec)
#define NSYNC_TIME_NSEC(t) ((t).tv_nsec)
typedef std::chrono::system_clock::time_point nsync_cpp_time_point_;
nsync_time nsync_from_time_point_ (nsync_cpp_time_point_);
nsync_cpp_time_point_ nsync_to_time_point_ (nsync_time);
#define NSYNC_COUNTER_CPP_OVERLOAD_ \
static inline uint32_t nsync_counter_wait (nsync_counter c, \
nsync_cpp_time_point_ abs_deadline) { \
return (nsync_counter_wait (c, nsync_from_time_point_ (abs_deadline))); \
}
#define NSYNC_CV_CPP_OVERLOAD_ \
static inline int nsync_cv_wait_with_deadline (nsync_cv *cv, nsync_mu *mu, \
nsync_cpp_time_point_ abs_deadline, struct nsync_note_s_ *cancel_note) { \
return (nsync_cv_wait_with_deadline (cv, mu, \
nsync_from_time_point_ (abs_deadline), \
cancel_note)); \
} \
static inline int nsync_cv_wait_with_deadline_generic (nsync_cv *cv, \
void *mu, void (*lock) (void *), void (*unlock) (void *), \
nsync_cpp_time_point_ abs_deadline, struct nsync_note_s_ *cancel_note) { \
return (nsync_cv_wait_with_deadline_generic (cv, mu, lock, unlock, \
nsync_from_time_point_ (abs_deadline), \
cancel_note)); \
}
#define NSYNC_MU_WAIT_CPP_OVERLOAD_ \
static inline int nsync_mu_wait_with_deadline (nsync_mu *mu, \
int (*condition) (const void *condition_arg), const void *condition_arg, \
int (*condition_arg_eq) (const void *a, const void *b), \
nsync_cpp_time_point_ abs_deadline, struct nsync_note_s_ *cancel_note) { \
return (nsync_mu_wait_with_deadline (mu, condition, condition_arg, \
condition_arg_eq, \
nsync_from_time_point_ (abs_deadline), \
cancel_note)); \
}
#define NSYNC_NOTE_CPP_OVERLOAD_ \
static inline nsync_note nsync_note_new (nsync_note parent, \
nsync_cpp_time_point_ abs_deadline) { \
return (nsync_note_new (parent, nsync_from_time_point_ (abs_deadline))); \
} \
static inline int nsync_note_wait (nsync_note n, nsync_cpp_time_point_ abs_deadline) { \
return (nsync_note_wait (n, nsync_from_time_point_ (abs_deadline))); \
} \
static inline nsync_cpp_time_point_ nsync_note_expiry_timepoint (nsync_note n) { \
return (nsync_to_time_point_ (nsync_note_expiry (n))); \
}
#define NSYNC_WAITER_CPP_OVERLOAD_ \
static inline int nsync_wait_n (void *mu, void (*lock) (void *), \
void (*unlock) (void *), \
nsync_cpp_time_point_ abs_deadline, \
int count, struct nsync_waitable_s *waitable[]) { \
return (nsync_wait_n (mu, lock, unlock, \
nsync_from_time_point_ (abs_deadline), count, waitable)); \
}
NSYNC_CPP_END_
#else
/* Default is to use timespec. */
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timeval.h"
#include "libc/calls/weirdtypes.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/sched.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
NSYNC_CPP_START_
typedef struct timespec nsync_time;
#define NSYNC_TIME_SEC(t) ((t).tv_sec)
#define NSYNC_TIME_NSEC(t) ((t).tv_nsec)
NSYNC_CPP_END_
#endif
#if !defined(NSYNC_COUNTER_CPP_OVERLOAD_)
#define NSYNC_COUNTER_CPP_OVERLOAD_
#define NSYNC_CV_CPP_OVERLOAD_
#define NSYNC_MU_WAIT_CPP_OVERLOAD_
#define NSYNC_NOTE_CPP_OVERLOAD_
#define NSYNC_WAITER_CPP_OVERLOAD_
#endif
#endif /*NSYNC_PUBLIC_NSYNC_TIME_INTERNAL_H_*/

108
libc/thread/nsync_wait.c Normal file
View file

@ -0,0 +1,108 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 │
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "libc/mem/mem.h"
#include "third_party/nsync/atomic.h"
#include "third_party/nsync/atomic.internal.h"
#include "third_party/nsync/common.internal.h"
#include "third_party/nsync/dll.h"
#include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/wait_s.internal.h"
#include "third_party/nsync/waiter.h"
asm(".ident\t\"\\n\\n\
*NSYNC (Apache 2.0)\\n\
Copyright 2016 Google, Inc.\\n\
https://github.com/google/nsync\"");
// clang-format off
int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *),
nsync_time abs_deadline,
int count, struct nsync_waitable_s *waitable[]) {
int ready;
IGNORE_RACES_START ();
for (ready = 0; ready != count &&
nsync_time_cmp ((*waitable[ready]->funcs->ready_time) (
waitable[ready]->v, NULL),
nsync_time_zero) > 0;
ready++) {
}
if (ready == count && nsync_time_cmp (abs_deadline, nsync_time_zero) > 0) {
int i;
int unlocked = 0;
int j;
int enqueued = 1;
waiter *w = nsync_waiter_new_ ();
struct nsync_waiter_s nw_set[4];
struct nsync_waiter_s *nw = nw_set;
if (count > (int) (sizeof (nw_set) / sizeof (nw_set[0]))) {
nw = (struct nsync_waiter_s *) malloc (count * sizeof (nw[0]));
}
for (i = 0; i != count && enqueued; i++) {
nw[i].tag = NSYNC_WAITER_TAG;
nw[i].sem = &w->sem;
nsync_dll_init_ (&nw[i].q, &nw[i]);
ATM_STORE (&nw[i].waiting, 0);
nw[i].flags = 0;
enqueued = (*waitable[i]->funcs->enqueue) (waitable[i]->v, &nw[i]);
}
if (i == count) {
nsync_time min_ntime;
if (mu != NULL) {
(*unlock) (mu);
unlocked = 1;
}
do {
min_ntime = abs_deadline;
for (j = 0; j != count; j++) {
nsync_time ntime;
ntime = (*waitable[j]->funcs->ready_time) (
waitable[j]->v, &nw[j]);
if (nsync_time_cmp (ntime, min_ntime) < 0) {
min_ntime = ntime;
}
}
} while (nsync_time_cmp (min_ntime, nsync_time_zero) > 0 &&
nsync_mu_semaphore_p_with_deadline (&w->sem,
min_ntime) == 0);
}
/* An attempt was made above to enqueue waitable[0..i-1].
Dequeue any that are still enqueued, and remember the index
of the first ready (i.e., not still enqueued) object, if any. */
for (j = 0; j != i; j++) {
int was_still_enqueued =
(*waitable[j]->funcs->dequeue) (waitable[j]->v, &nw[j]);
if (!was_still_enqueued && ready == count) {
ready = j;
}
}
if (nw != nw_set) {
free (nw);
}
nsync_waiter_free_ (w);
if (unlocked) {
(*lock) (mu);
}
}
IGNORE_RACES_END ();
return (ready);
}

View file

@ -1,153 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PUBLIC_NSYNC_WAITER_H_
#define NSYNC_PUBLIC_NSYNC_WAITER_H_
/* nsync_wait_n() allows the client to wait on multiple objects (condition
variables, nsync_notes, nsync_counters, etc.) until at least one of them
becomes ready, or a deadline expires.
It can be thought of as rather like Unix's select() or poll(),
except the the objects being waited for are synchronization
data structures, rather than file descriptors.
The client can construct new objects that can be waited for by implementing
three routines.
Examples:
To wait on two nsync_notes n0, n1, and a nsync_counter c0,
with a deadline of abs_deadline:
// Form an array of struct nsync_waitable_s, identifying the
// objects and the corresponding descriptors. (static initialization
// syntax is used for brevity)
static struct nsync_waitable_s w[] = {
{ &n0, &nsync_note_waitable_funcs },
{ &n1, &nsync_note_waitable_funcs },
{ &c0, &nsync_counter_waitable_funcs }
};
static struct nsync_waitable_s *pw[] = { &w[0], &w[1], &w[2] };
int n = sizeof (w) / sizeof (w[0]);
// Wait. The mu, lock, and unlock arguments are NULL because
// no condition variables are invovled.
int i = nsync_wait_n (NULL, NULL, NULL, abs_deadline, n, pw);
if (i == n) {
// timeout
} else {
// w[i].v became ready.
}
To wait on multiple condition variables, the mu/lock/unlock parameters are
used. Imagine cv0 and cv1 are signalled when predicates pred0() (under
lock mu0) and pred1() (under lock mu1) become true respectively. Assume
that mu0 is acquired before mu1.
static void lock2 (void *v) { // lock two mutexes in order
nsync_mu **mu = (nsync_mu **) v;
nsync_mu_lock (mu[0]);
nsync_mu_lock (mu[1]);
}
static void unlock2 (void *v) { // unlock two mutexes.
nsync_mu **mu = (nsync_mu **) v;
nsync_mu_unlock (mu[1]);
nsync_mu_unlock (mu[0]);
}
// Describe the condition variables and the locks.
static struct nsync_waitable_s w[] = {
{ &cv0, &nsync_cv_waitable_funcs },
{ &cv1, &nsync_cv_waitable_funcs }
};
static struct nsync_waitable_s *pw[] = { &w[0], &w[1] };
nsync_mu *lock_list[] = { &mu0, &mu1 };
int n = sizeof (w) / sizeof (w[0]);
lock2 (list_list);
while (!pred0 () && !pred1 ()) {
// Wait for one of the condition variables to be signalled,
// with no timeout.
nsync_wait_n (lock_list, &lock2, &unlock2,
nsync_time_no_deadline, n, pw);
}
if (pred0 ()) { ... }
if (pred1 ()) { ... }
unlock2 (list_list);
*/
#include "libc/thread/nsync_time.h"
#include "libc/thread/nsync_cpp.h"
NSYNC_CPP_START_
struct nsync_waitable_funcs_s; /* forward declaration of struct that contains
type dependent wait operations */
/* Clients wait on objects by forming an array of struct nsync_waitable_s.
Each each element points to one object and its type-dependent functions. */
struct nsync_waitable_s {
void *v; /* pointer to object */
/* pointer to type-dependent functions. Use
&nsync_note_waitable_funcs for an nsync_note,
&nsync_counternote_waitable_funcs for an nsync_counter,
&nsync_cv_waitable_funcs for an nsync_cv. */
const struct nsync_waitable_funcs_s *funcs;
};
/* Wait until at least one of *waitable[0,..,count-1] is has been notified, or
abs_deadline is reached. Return the index of the notified element of
waitable[], or count if no such element exists.
If mu!=NULL, (*unlock)(mu) is called after the thread is queued on the
various waiters, and (*lock)(mu) is called before return; mu/lock/unlock are
used to acquire and release the relevant locks whan waiting on condition
variables. */
int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *),
nsync_time abs_deadline, int count,
struct nsync_waitable_s *waitable[]);
/* --------------------------------------------------- */
/* A "struct nsync_waitable_s" implementation must implement these functions.
Clients should ignore the internals. */
struct nsync_waiter_s;
struct nsync_waitable_funcs_s {
/* Return the time when *v will be ready (max time if
unknown), or 0 if it is already ready. The parameter nw may be
passed as NULL, in which case the result should indicate whether the
thread would block if it were to wait on *v.
All calls with the same *v must report the same result until the
object becomes ready, from which point calls must report 0. */
nsync_time (*ready_time) (void *v, struct nsync_waiter_s *nw);
/* If *v is ready, return zero; otherwise enqueue *nw on *v and return
non-zero. */
int (*enqueue) (void *v, struct nsync_waiter_s *nw);
/* If nw has been previously dequeued, return zero; otherwise dequeue
*nw from *v and return non-zero. */
int (*dequeue) (void *v, struct nsync_waiter_s *nw);
};
/* The "struct nsync_waitable_s" for nsync_note, nsync_counter, and nsync_cv. */
extern const struct nsync_waitable_funcs_s nsync_note_waitable_funcs;
extern const struct nsync_waitable_funcs_s nsync_counter_waitable_funcs;
extern const struct nsync_waitable_funcs_s nsync_cv_waitable_funcs;
NSYNC_WAITER_CPP_OVERLOAD_
NSYNC_CPP_END_
#endif /*NSYNC_PUBLIC_NSYNC_WAITER_H_*/

View file

@ -1,49 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "libc/thread/headers.h"
NSYNC_CPP_START_
static pthread_key_t waiter_key;
static nsync_atomic_uint32_ pt_once;
static void do_once (nsync_atomic_uint32_ *ponce, void (*dest) (void *)) {
uint32_t o = ATM_LOAD_ACQ (ponce);
if (o != 2) {
while (o == 0 && !ATM_CAS_ACQ (ponce, 0, 1)) {
o = ATM_LOAD (ponce);
}
if (o == 0) {
pthread_key_create (&waiter_key, dest);
ATM_STORE_REL (ponce, 2);
}
while (ATM_LOAD_ACQ (ponce) != 2) {
sched_yield ();
}
}
}
void *nsync_per_thread_waiter_ (void (*dest) (void *)) {
do_once (&pt_once, dest);
return (pthread_getspecific (waiter_key));
}
void nsync_set_per_thread_waiter_ (void *v, void (*dest) (void *)) {
do_once (&pt_once, dest);
pthread_setspecific (waiter_key, v);
}
NSYNC_CPP_END_

View file

@ -1,72 +0,0 @@
// clang-format off
/* Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#ifndef NSYNC_PLATFORM_LINUX_PLATFORM_H_
#define NSYNC_PLATFORM_LINUX_PLATFORM_H_
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE /* for futexes */
#endif
#include "libc/mem/alg.h"
#include "libc/str/str.h"
#include "libc/calls/calls.h"
#include "libc/calls/weirdtypes.h"
#include "libc/runtime/pathconf.h"
#include "libc/runtime/sysconf.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/ok.h"
#include "third_party/getopt/getopt.h"
#include "libc/errno.h"
#include "libc/mem/alg.h"
#include "libc/fmt/conv.h"
#include "libc/mem/mem.h"
#include "libc/stdio/rand.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/temp.h"
#include "libc/sysv/consts/exit.h"
#include "third_party/gdtoa/gdtoa.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timeval.h"
#include "libc/calls/weirdtypes.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/sched.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
#include "libc/fmt/conv.h"
#include "libc/inttypes.h"
#include "libc/thread/thread.h"
#include "libc/limits.h"
#include "libc/sysv/consts/_posix.h"
#include "libc/sysv/consts/iov.h"
#include "libc/sysv/consts/limits.h"
#include "libc/sysv/consts/xopen.h"
#include "libc/sysv/consts/futex.h"
#include "libc/sysv/consts/nr.h"
#include "libc/calls/calls.h"
#include "libc/thread/thread.h"
#include "libc/thread/thread2.h"
#include "libc/calls/semaphore.internal.h"
#include "libc/calls/calls.h"
#include "libc/fmt/fmt.h"
#include "libc/stdio/lock.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/stdio/temp.h"
#endif /*NSYNC_PLATFORM_LINUX_PLATFORM_H_*/

View file

@ -62,11 +62,13 @@ struct PosixThread {
int *ctid;
char *tls;
char *tib;
char *altstack;
_Atomic(enum PosixThreadStatus) status;
jmp_buf exiter;
pthread_attr_t attr;
};
hidden extern pthread_spinlock_t _pthread_keys_lock;
hidden extern uint64_t _pthread_key_usage[(PTHREAD_KEYS_MAX + 63) / 64];
hidden extern pthread_key_dtor _pthread_key_dtor[PTHREAD_KEYS_MAX];
hidden extern _Thread_local void *_pthread_keys[PTHREAD_KEYS_MAX];

View file

@ -16,10 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/errno.h"
#include "libc/thread/thread.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
#include "third_party/nsync/counter.h"
/**
* Destroys barrier.
@ -28,10 +27,9 @@
* @raise EINVAL if threads are still inside the barrier
*/
int pthread_barrier_destroy(pthread_barrier_t *barrier) {
if (barrier->waits || barrier->popped) {
assert(!"deadlock");
return EINVAL;
if (barrier->_nsync) {
nsync_counter_free(barrier->_nsync);
barrier->_nsync = 0;
}
memset(barrier, -1, sizeof(*barrier));
return 0;
}

View file

@ -16,10 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/errno.h"
#include "libc/limits.h"
#include "libc/thread/thread.h"
#include "third_party/nsync/counter.h"
/**
* Initializes barrier.
@ -28,15 +27,14 @@
* @param count is how many threads need to call pthread_barrier_wait()
* before the barrier is released, which must be greater than zero
* @return 0 on success, or error number on failure
* @raise EINVAL if `count` isn't greater than zero or overflows
* @raise EINVAL if `count` isn't greater than zero
* @raise ENOMEM if insufficient memory exists
*/
int pthread_barrier_init(pthread_barrier_t *barrier,
const pthread_barrierattr_t *attr, unsigned count) {
if (count && count < INT_MAX / 2) {
*barrier = (pthread_barrier_t){attr ? *attr : 0, count};
return 0;
} else {
assert(!"bad count");
return EINVAL;
}
nsync_counter c;
if (!count) return EINVAL;
if (!(c = nsync_counter_new(count))) return ENOMEM;
*barrier = (pthread_barrier_t){._nsync = c};
return 0;
}

Some files were not shown because too many files have changed in this diff Show more