mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
Use *NSYNC for POSIX threads locking APIs
Condition variables, barriers, and r/w locks now work very well.
This commit is contained in:
parent
3de35e196c
commit
b5cb71ab84
197 changed files with 3734 additions and 3817 deletions
3
Makefile
3
Makefile
|
@ -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 \
|
||||
|
|
28
ape/loader.c
28
ape/loader.c
|
@ -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) {
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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_
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_ */
|
|
@ -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;
|
||||
|
|
|
@ -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
39
libc/intrin/leaky.S
Normal 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
|
16
libc/intrin/leaky.internal.h
Normal file
16
libc/intrin/leaky.internal.h
Normal 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_ */
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
|
@ -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) {
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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];
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
21
libc/intrin/wantcrashreports.c
Normal file
21
libc/intrin/wantcrashreports.c
Normal 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;
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -5,18 +5,22 @@
|
|||
#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_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) */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
5
libc/thread/README.md
Normal 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.
|
|
@ -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_*/
|
|
@ -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;
|
||||
|
|
|
@ -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_*/
|
|
@ -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_*/
|
|
@ -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_*/
|
|
@ -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_*/
|
|
@ -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_*/
|
|
@ -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_*/
|
|
@ -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_*/
|
|
@ -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_
|
||||
|
|
@ -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_*/
|
|
@ -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_*/
|
|
@ -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_
|
|
@ -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 (μ)
|
||||
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_*/
|
|
@ -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_
|
|
@ -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_*/
|
|
@ -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_*/
|
|
@ -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_
|
||||
|
|
@ -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_*/
|
|
@ -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 = {
|
|||
¬e_enqueue,
|
||||
¬e_dequeue
|
||||
};
|
||||
|
||||
NSYNC_CPP_END_
|
|
@ -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_*/
|
|
@ -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_
|
|
@ -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_*/
|
|
@ -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_
|
85
libc/thread/nsync_sem_wait.c
Normal file
85
libc/thread/nsync_sem_wait.c
Normal 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);
|
||||
}
|
||||
|
||||
|
33
libc/thread/nsync_sem_wait_no_note.c
Normal file
33
libc/thread/nsync_sem_wait_no_note.c
Normal 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));
|
||||
}
|
|
@ -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_
|
|
@ -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_*/
|
|
@ -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_*/
|
|
@ -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
108
libc/thread/nsync_wait.c
Normal 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);
|
||||
}
|
||||
|
||||
|
|
@ -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_*/
|
|
@ -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_
|
|
@ -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_*/
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
nsync_counter c;
|
||||
if (!count) return EINVAL;
|
||||
if (!(c = nsync_counter_new(count))) return ENOMEM;
|
||||
*barrier = (pthread_barrier_t){._nsync = c};
|
||||
return 0;
|
||||
} else {
|
||||
assert(!"bad count");
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue