Make futexes cancellable by pthreads

This commit is contained in:
Justine Tunney 2022-11-04 18:19:05 -07:00
parent 2278327eba
commit 022536cab6
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
101 changed files with 627 additions and 391 deletions

View file

@ -32,7 +32,7 @@ struct timespec _timespec_sleep(struct timespec delay) {
if (!(rc = clock_nanosleep(CLOCK_REALTIME, 0, &delay, &remain))) {
return (struct timespec){0};
} else {
_npassert(rc == EINTR);
_npassert(rc == EINTR || rc == ECANCELED);
return remain;
}
}

View file

@ -27,9 +27,9 @@
*
* @return 0 on success, or EINTR if interrupted
*/
int _timespec_sleep_until(struct timespec abs_deadline) {
errno_t _timespec_sleep_until(struct timespec abs_deadline) {
int rc;
rc = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &abs_deadline, 0);
_npassert(!rc || rc == EINTR);
_npassert(!rc || rc == EINTR || rc == ECANCELED);
return rc;
}

View file

@ -0,0 +1,19 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_BLOCKCANCEL_H_
#define COSMOPOLITAN_LIBC_CALLS_BLOCKCANCEL_H_
#include "libc/thread/thread.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define BLOCK_CANCELLATIONS \
do { \
int _CancelState; \
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &_CancelState)
#define ALLOW_CANCELLATIONS \
pthread_setcancelstate(_CancelState, 0); \
} \
while (0)
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_BLOCKCANCEL_H_ */

View file

@ -1,5 +1,5 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_BLOCKSIGS_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_BLOCKSIGS_INTERNAL_H_
#ifndef COSMOPOLITAN_LIBC_CALLS_BLOCKSIGS_H_
#define COSMOPOLITAN_LIBC_CALLS_BLOCKSIGS_H_
#include "libc/calls/struct/sigset.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -16,4 +16,4 @@ COSMOPOLITAN_C_START_
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_BLOCKSIGS_INTERNAL_H_ */
#endif /* COSMOPOLITAN_LIBC_CALLS_BLOCKSIGS_H_ */

View file

@ -112,7 +112,7 @@ int fcntl(int fd, int cmd, ...) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = _weaken(__zipos_fcntl)(fd, cmd, arg);
} else if (!IsWindows()) {
if (cmd == F_SETLKW) {
if (cmd == F_SETLKW || cmd == F_OFD_SETLKW) {
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl_cp);
} else {
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl);

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/ioctl.h"
#include "libc/calls/struct/metatermios.internal.h"
@ -43,31 +44,12 @@ struct IoctlPtmGet {
char sname[16];
};
/**
* Opens new pseudo teletypewriter.
*
* @param mfd receives controlling tty rw fd on success
* @param sfd receives subordinate tty rw fd on success
* @param tio may be passed to tune a century of legacy behaviors
* @param wsz may be passed to set terminal display dimensions
* @params flags is usually O_RDWR|O_NOCTTY
* @return 0 on success, or -1 w/ errno
*/
int openpty(int *mfd, int *sfd, char *name, const struct termios *tio,
static int openpty_impl(int *mfd, int *sfd, char *name,
const struct termios *tio, //
const struct winsize *wsz) {
int m, s, p;
union metatermios mt;
struct IoctlPtmGet t;
if (IsWindows() || IsMetal()) {
return enosys();
}
if (IsAsan() && (!__asan_is_valid(mfd, sizeof(int)) ||
!__asan_is_valid(sfd, sizeof(int)) ||
(name && !__asan_is_valid(name, 16)) ||
(tio && !__asan_is_valid(tio, sizeof(*tio))) ||
(wsz && !__asan_is_valid(wsz, sizeof(*wsz))))) {
return efault();
}
RETURN_ON_ERROR((m = posix_openpt(O_RDWR | O_NOCTTY)));
if (!IsOpenbsd()) {
RETURN_ON_ERROR(grantpt(m));
@ -90,3 +72,33 @@ OnError:
if (m != -1) sys_close(m);
return -1;
}
/**
* Opens new pseudo teletypewriter.
*
* @param mfd receives controlling tty rw fd on success
* @param sfd receives subordinate tty rw fd on success
* @param tio may be passed to tune a century of legacy behaviors
* @param wsz may be passed to set terminal display dimensions
* @params flags is usually O_RDWR|O_NOCTTY
* @return 0 on success, or -1 w/ errno
*/
int openpty(int *mfd, int *sfd, char *name, //
const struct termios *tio, //
const struct winsize *wsz) {
int rc;
if (IsWindows() || IsMetal()) {
return enosys();
}
if (IsAsan() && (!__asan_is_valid(mfd, sizeof(int)) ||
!__asan_is_valid(sfd, sizeof(int)) ||
(name && !__asan_is_valid(name, 16)) ||
(tio && !__asan_is_valid(tio, sizeof(*tio))) ||
(wsz && !__asan_is_valid(wsz, sizeof(*wsz))))) {
return efault();
}
BLOCK_CANCELLATIONS;
rc = openpty(mfd, sfd, name, tio, wsz);
ALLOW_CANCELLATIONS;
return rc;
}

View file

@ -27,6 +27,7 @@
#include "libc/log/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/thread/thread.h"
relegated void __assert_fail(const char *expr, const char *file, int line) {
int me, owner;
@ -36,6 +37,7 @@ relegated void __assert_fail(const char *expr, const char *file, int line) {
ftrace_enabled(-1);
owner = 0;
me = sys_gettid();
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
kprintf("%s:%d: assert(%s) failed (tid %d)\n", file, line, expr, me);
if (__vforked ||
atomic_compare_exchange_strong_explicit(

View file

@ -22,9 +22,11 @@
#include "libc/thread/tls.h"
void _pthread_cleanup_pop(struct _pthread_cleanup_buffer *cb, int execute) {
struct PosixThread *pt = (struct PosixThread *)__get_tls()->tib_pthread;
struct PosixThread *pt;
if (__tls_enabled && (pt = (struct PosixThread *)__get_tls()->tib_pthread)) {
_unassert(cb == pt->cleanup);
pt->cleanup = cb->__prev;
}
if (execute) {
cb->__routine(cb->__arg);
}

View file

@ -18,12 +18,15 @@
*/
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
void _pthread_cleanup_push(struct _pthread_cleanup_buffer *cb,
void (*routine)(void *), void *arg) {
struct PosixThread *pt = (struct PosixThread *)__get_tls()->tib_pthread;
struct PosixThread *pt;
cb->__routine = routine;
cb->__arg = arg;
if (__tls_enabled && (pt = (struct PosixThread *)__get_tls()->tib_pthread)) {
cb->__prev = pt->cleanup;
pt->cleanup = cb;
}
}

View file

@ -43,13 +43,13 @@
* @raise EINVAL if `state` has bad value
* @asyncsignalsafe
*/
int pthread_setcancelstate(int state, int *oldstate) {
errno_t pthread_setcancelstate(int state, int *oldstate) {
struct PosixThread *pt;
if (__tls_enabled && (pt = (struct PosixThread *)__get_tls()->tib_pthread)) {
switch (state) {
case PTHREAD_CANCEL_ENABLE:
case PTHREAD_CANCEL_DISABLE:
case PTHREAD_CANCEL_MASKED:
pt = (struct PosixThread *)__get_tls()->tib_pthread;
if (oldstate) {
if (pt->flags & PT_NOCANCEL) {
*oldstate = PTHREAD_CANCEL_DISABLE;
@ -69,4 +69,10 @@ int pthread_setcancelstate(int state, int *oldstate) {
default:
return EINVAL;
}
} else {
if (oldstate) {
*oldstate = 0;
}
return 0;
}
}

View file

@ -1,32 +0,0 @@
/*-*- 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"
.privileged
// Calls FreeBSD's futex() API.
// Normalizes return value to Linux ABI -errno convention.
sys_umtx_op:
mov $0x1c6,%eax
mov %rcx,%r10
syscall
jc 1f
ret
1: neg %eax
ret
.endfn sys_umtx_op,globl,hidden

View file

@ -20,6 +20,9 @@
#include "libc/sysv/consts/clock.h"
#include "libc/thread/freebsd.internal.h"
int sys_umtx_timedwait_uint_cp(atomic_int *, int, int, size_t,
struct _umtx_time *) asm("sys_futex_cp");
int sys_umtx_timedwait_uint(atomic_int *p, int expect, bool pshare,
const struct timespec *abstime) {
int op;
@ -40,5 +43,5 @@ int sys_umtx_timedwait_uint(atomic_int *p, int expect, bool pshare,
} else {
op = UMTX_OP_WAIT_UINT_PRIVATE;
}
return sys_umtx_op(p, op, expect, (void *)size, tm_p);
return sys_umtx_timedwait_uint_cp(p, op, expect, size, tm_p);
}

View file

@ -29,6 +29,7 @@
#include "libc/log/log.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/thread.h"
#if SupportsMetal()
STATIC_YOINK("_idt");
@ -45,6 +46,7 @@ relegated wontreturn void __die(void) {
static atomic_int once;
owner = 0;
me = sys_gettid();
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
if (__vforked ||
atomic_compare_exchange_strong_explicit(
&once, &owner, me, memory_order_relaxed, memory_order_relaxed)) {

View file

@ -43,6 +43,7 @@
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "third_party/libcxx/math.h"
@ -307,6 +308,7 @@ relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
strace_enabled(-1);
owner = 0;
me = sys_gettid();
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
if (atomic_compare_exchange_strong_explicit(
&once, &owner, me, memory_order_relaxed, memory_order_relaxed)) {
if (!__vforked) {

View file

@ -20,6 +20,7 @@
#include "libc/intrin/safemacros.internal.h"
#include "libc/log/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/thread.h"
/**
* Prints initial part of fatal message.
@ -27,6 +28,7 @@
* @note this is support code for __check_fail(), __assert_fail(), etc.
*/
relegated void __start_fatal(const char *file, int line) {
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
__restore_tty();
kprintf("%r%serror%s:%s:%d:%s%s: ", !__nocolor ? "\e[J\e[30;101m" : "",
!__nocolor ? "\e[94;49m" : "", file, line,

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/dprintf.h"
#include "libc/calls/struct/timespec.h"
@ -124,16 +125,13 @@ void vsyslog(int priority, const char *message, va_list ap) {
int l, l2;
int hlen; /* If LOG_CONS is specified, use to store the point in
* the header message after the timestamp */
BLOCK_CANCELLATIONS;
if (log_fd < 0) __openlog();
if (!(priority & LOG_FACMASK)) priority |= log_facility;
/* Build the time string */
now = Time(NULL);
gmtime_r(&now, &tm);
strftime(timebuf, sizeof(timebuf), "%b %e %T", &tm);
pid = (log_opt & LOG_PID) ? getpid() : 0;
/* This is a clever trick to optionally include "[<pid>]"
* only if pid != 0. When pid==0, the while "[%.0d]" is skipped:
@ -149,7 +147,6 @@ void vsyslog(int priority, const char *message, va_list ap) {
l += snprintf(buf + l, sizeof(buf) - l, "%hs%s%.0d%s: ", log_ident,
"[" + !pid, pid, "]" + !pid);
errno = errno_save;
/* Append user message */
l2 = vsnprintf(buf + l, sizeof(buf) - l, message, ap);
if (l2 >= 0) {
@ -166,37 +163,17 @@ void vsyslog(int priority, const char *message, va_list ap) {
* - First try to send it to syslogd
* - If fails and LOG_CONS is provided, writes to /dev/console
*/
#if 0
if (send(log_fd, buf, l, 0) < 0 && (!is_lost_conn(errno)
|| connect(log_fd, (void *)&log_addr, sizeof(log_addr)) < 0
|| send(log_fd, buf, l, 0) < 0)
&& (log_opt & LOG_CONS)) {
if (send(log_fd, buf, l, 0) < 0 &&
(!is_lost_conn(errno) ||
connect(log_fd, (void *)&log_addr, sizeof(log_addr)) < 0 ||
send(log_fd, buf, l, 0) < 0) &&
(log_opt & LOG_CONS)) {
int fd = open("/dev/console", O_WRONLY | O_NOCTTY);
if (fd >= 0) {
dprintf(fd, "%.*s", l - hlen, buf + hlen);
close(fd);
}
}
#else
int rc = send(log_fd, buf, l, 0);
if (rc < 0) {
printf("ERR: send(1) failed: %s (errno=%d)\n", strerror(errno), errno);
if (!is_lost_conn(errno)) {
rc = connect(log_fd, (void *)&log_addr, sizeof(log_addr));
if (rc < 0) {
printf("ERR: connect(syslog) failed: %s (errno=%d)\n",
strerror(errno), errno);
} else {
rc = send(log_fd, buf, l, 0);
if (rc < 0) {
printf("ERR: send(2) failed: %s (errno=%d)\n", strerror(errno),
errno);
}
}
}
}
#endif
} else {
uint16_t evtType;
uint32_t evtID;
@ -229,11 +206,11 @@ void vsyslog(int priority, const char *message, va_list ap) {
NULL /* Arguments */);
++log_id;
}
if (log_opt & LOG_PERROR) {
dprintf(2, "%.*s", l - hlen, buf + hlen);
}
}
ALLOW_CANCELLATIONS;
}
/**
@ -250,9 +227,7 @@ void vsyslog(int priority, const char *message, va_list ap) {
*/
int setlogmask(int maskpri) {
int ret;
if (log_facility == -1) {
__initlog();
}
if (log_facility == -1) __initlog();
ret = log_mask;
if (maskpri) log_mask = LOG_PRI(maskpri);
return ret;
@ -287,19 +262,15 @@ int setlogmask(int maskpri) {
*/
void openlog(const char *ident, int opt, int facility) {
size_t n;
if (log_facility == -1) {
__initlog();
}
if (!ident) {
ident = firstnonnull(program_invocation_short_name, "unknown");
}
BLOCK_CANCELLATIONS;
if (log_facility == -1) __initlog();
if (!ident) ident = firstnonnull(program_invocation_short_name, "unknown");
tprecode8to16(log_ident, ARRAYLEN(log_ident), ident);
log_opt = opt;
log_facility = facility;
log_id = 0;
if ((opt & LOG_NDELAY) && log_fd < 0) __openlog();
ALLOW_CANCELLATIONS;
}
/**
@ -339,9 +310,7 @@ void closelog(void) {
if (log_facility == -1) {
__initlog();
}
if (log_fd == -1) {
return;
}
if (log_fd != -1) {
if (IsWindows()) {
DeregisterEventSource(log_fd);
} else {
@ -349,3 +318,4 @@ void closelog(void) {
}
log_fd = -1;
}
}

View file

@ -16,13 +16,21 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/stdio/rand.h"
#include "libc/sysv/consts/grnd.h"
#include "libc/sysv/errfuns.h"
/**
* Returns random seeding bytes, the XNU/OpenBSD way.
*
* @return 0 on success, or -1 w/ errno
* @raise EIO if more than 256 bytes are requested
* @see getrandom()
*/
int getentropy(void *buf, size_t size) {
return getrandom(buf, size, GRND_RANDOM);
if (size > 256) return eio();
BLOCK_CANCELLATIONS;
if (getrandom(buf, size, 0) != size) notpossible;
ALLOW_CANCELLATIONS;
return 0;
}

View file

@ -41,9 +41,44 @@
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
static bool have_getrandom;
static ssize_t GetDevRandom(char *p, size_t n) {
int fd;
ssize_t rc;
fd = __sys_openat(AT_FDCWD, "/dev/urandom", O_RDONLY | O_CLOEXEC, 0);
if (fd == -1) return -1;
pthread_cleanup_push((void *)sys_close, (void *)(intptr_t)fd);
rc = sys_read(fd, p, n);
pthread_cleanup_pop(1);
close(fd);
return rc;
}
static ssize_t GetKernArnd(char *p, size_t n) {
size_t m, i = 0;
int cmd[2];
if (IsFreebsd()) {
cmd[0] = 1; /* CTL_KERN */
cmd[1] = 37; /* KERN_ARND */
} else {
cmd[0] = 1; /* CTL_KERN */
cmd[1] = 81; /* KERN_ARND */
}
for (;;) {
m = n - i;
if (sys_sysctl(cmd, 2, p + i, &m, 0, 0) != -1) {
if ((i += m) == n) {
return n;
}
} else {
return i ? i : -1;
}
}
}
/**
* Returns cryptographic random data.
*
@ -76,37 +111,17 @@ static bool have_getrandom;
* @vforksafe
*/
ssize_t getrandom(void *p, size_t n, unsigned f) {
char cf;
ssize_t rc;
uint64_t x;
int fd, cmd[2];
size_t i, j, m;
const char *via;
sigset_t neu, old;
if (n > 256) n = 256;
if ((f & ~(GRND_RANDOM | GRND_NONBLOCK))) {
rc = einval();
via = "n/a";
} else if (IsWindows()) {
via = "RtlGenRandom";
if (RtlGenRandom(p, n)) {
rc = n;
} else {
rc = __winerr();
}
rc = RtlGenRandom(p, n) ? n : __winerr();
} else if (IsFreebsd() || IsNetbsd()) {
via = "KERN_ARND";
if (IsFreebsd()) {
cmd[0] = 1; /* CTL_KERN */
cmd[1] = 37; /* KERN_ARND */
} else {
cmd[0] = 1; /* CTL_KERN */
cmd[1] = 81; /* KERN_ARND */
}
m = n;
if ((rc = sys_sysctl(cmd, 2, p, &m, 0, 0)) != -1) {
rc = m;
}
rc = GetKernArnd(p, n);
} else if (have_getrandom) {
via = "getrandom";
if ((rc = sys_getrandom(p, n, f & (GRND_RANDOM | GRND_NONBLOCK))) != -1) {
@ -114,15 +129,9 @@ ssize_t getrandom(void *p, size_t n, unsigned f) {
rc = n;
}
}
} else if ((fd = __sys_openat(
AT_FDCWD,
(via = (f & GRND_RANDOM) ? "/dev/random" : "/dev/urandom"),
O_RDONLY | ((f & GRND_NONBLOCK) ? O_NONBLOCK : 0), 0)) !=
-1) {
rc = sys_read(fd, p, n);
sys_close(fd);
} else {
rc = enosys();
via = "/dev/urandom";
rc = GetDevRandom(p, n);
}
STRACE("getrandom(%p, %'zu, %#x) via %s → %'ld% m", p, n, f, via, rc);
return rc;

View file

@ -1,2 +1,2 @@
.include "o/libc/sysv/macros.internal.inc"
.scall sys_futex,0x0a6053fffffff0ca,globl,hidden
.scall sys_futex,0x0a60531c6ffff0ca,globl,hidden

View file

@ -0,0 +1,2 @@
.include "o/libc/sysv/macros.internal.inc"
.scall sys_futex_cp,0x8a68539c6ffff8ca,globl,hidden

View file

@ -1826,7 +1826,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 0xfff 0xfff
syscon nr __NR_futex 0x00ca 0xfff 0x1c6 0x0053 0x0a6 0xfff
syscon nr __NR_set_robust_list 0x0111 0xfff 0xfff 0xfff 0x0a7 0xfff
syscon nr __NR_get_robust_list 0x0112 0xfff 0xfff 0xfff 0x0a8 0xfff
syscon nr __NR_uname 0x003f 0xfff 0x00a4 0xfff 0xfff 0xfff

View file

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

View file

@ -51,7 +51,7 @@ scall __sys_munmap 0x049049049204900b globl hidden
scall sys_sigaction 0x15402e1a0202e00d globl hidden # rt_sigaction on Lunix; __sigaction_sigtramp() on NetBSD
scall __sys_sigprocmask 0x125030154214900e globl hidden # a.k.a. rt_sigprocmask, openbsd:byvalue, a.k.a. pthread_sigmask
scall sys_ioctl 0x0360360362036010 globl hidden
scall sys_ioctl_cp 0x8368368362836810 globl hidden
scall sys_ioctl_cp 0x8368368362836810 globl hidden # intended for TCSBRK
scall sys_pread 0x8ad8ad9db2899811 globl hidden # a.k.a. pread64; netbsd+openbsd:pad
scall sys_pwrite 0x8ae8ae9dc289a812 globl hidden # a.k.a. pwrite64; netbsd+openbsd:pad
scall sys_readv 0x8788788782878813 globl hidden
@ -101,7 +101,8 @@ scall sys_kill 0x02507a025202503e globl hidden # kill(pid, sig, 1) b/c xnu
scall sys_killpg 0x092fff092fffffff globl hidden
scall sys_clone 0x11fffffffffff038 globl hidden
scall sys_tkill 0x13e0771b121480c8 globl hidden # thr_kill() on freebsd; _lwp_kill() on netbsd; thrkill() on openbsd where arg3 should be 0; __pthread_kill() on XNU
scall sys_futex 0x0a6053fffffff0ca globl hidden # raises SIGSYS on NetBSD
scall sys_futex 0x0a60531c6ffff0ca globl hidden # raises SIGSYS on NetBSD; _umtx_op() on FreeBSD
scall sys_futex_cp 0x8a68539c6ffff8ca globl hidden # intended for futex wait ops
scall sys_set_robust_list 0x0a7ffffffffff111 globl # no wrapper
scall sys_get_robust_list 0x0a8ffffffffff112 globl # no wrapper
scall sys_uname 0x0a4fff0a4ffff03f globl hidden
@ -114,7 +115,7 @@ scall sys_msgsnd 0x8e28e28e22904845 globl # no wrapper; won't polyfill for wind
scall sys_msgrcv 0x8e38e38e32905846 globl # no wrapper; won't polyfill for windows
scall sys_msgctl 0x1bc1291ff2102047 globl # no wrapper; won't polyfill for windows
scall __sys_fcntl 0x05c05c05c205c048 globl hidden
scall __sys_fcntl_cp 0x85c85c85c285c848 globl hidden
scall __sys_fcntl_cp 0x85c85c85c285c848 globl hidden # intended for F_SETLKW and F_OFD_SETLKW
scall sys_flock 0x8838838832883849 globl hidden
scall sys_fsync 0x85f85f85f285f84a globl hidden
scall sys_fdatasync 0x8f185fa2628bb84b globl hidden # fsync() on openbsd

View file

@ -225,6 +225,7 @@ void testlib_runtestcases(testfn_t *start, testfn_t *end, testfn_t warmup) {
if (!IsWindows()) sys_getpid();
if (warmup) warmup();
testlib_clearxmmregisters();
STRACE("running test %t", fn);
(*fn)();
if (!IsWindows()) sys_getpid();
if (_weaken(TearDown)) _weaken(TearDown)();

View file

@ -48,7 +48,6 @@ struct _umtx_time {
uint32_t _clockid;
};
int sys_umtx_op(void *, int, unsigned long, void *, void *);
int sys_umtx_timedwait_uint(_Atomic(int) *, int, bool, const struct timespec *);
COSMOPOLITAN_C_END_

View file

@ -15,8 +15,8 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
// LEGAL TRANSITIONS ┌──> TERMINATED
// pthread_create ─┬─> JOINABLE ─┴┬─> DETACHED ──> ZOMBIE
// LEGAL TRANSITIONS ┌──> TERMINATED ─┐
// pthread_create ─┬─> JOINABLE ─┴┬─> DETACHED ───┴─> ZOMBIE
// └──────────────┘
enum PosixThreadStatus {
@ -47,6 +47,8 @@ enum PosixThreadStatus {
//
// - kPosixThreadTerminated -> _pthread_free() will happen when
// pthread_join() is called by the user.
// - kPosixThreadTerminated -> kPosixThreadZombie will happen when
// pthread_detach() is called by the user.
kPosixThreadTerminated,
// this is a detached thread that terminated.
@ -83,10 +85,10 @@ extern _Atomic(pthread_key_dtor) _pthread_key_dtor[PTHREAD_KEYS_MAX] hidden;
int _pthread_atfork(atfork_f, atfork_f, atfork_f) hidden;
int _pthread_reschedule(struct PosixThread *) hidden;
int _pthread_setschedparam_freebsd(int, int, const struct sched_param *) hidden;
int _pthread_wait(struct PosixThread *) hidden;
void _pthread_free(struct PosixThread *) hidden;
void _pthread_cleanup(struct PosixThread *) hidden;
void _pthread_ungarbage(void) hidden;
void _pthread_wait(struct PosixThread *) hidden;
void _pthread_zombies_add(struct PosixThread *) hidden;
void _pthread_zombies_purge(void) hidden;
void _pthread_zombies_decimate(void) hidden;

View file

@ -65,8 +65,11 @@ void _pthread_onfork_parent(void) {
}
void _pthread_onfork_child(void) {
pthread_mutexattr_t attr;
extern pthread_mutex_t __mmi_lock_obj;
bzero(&__mmi_lock_obj, sizeof(__mmi_lock_obj));
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&__mmi_lock_obj, &attr);
__kmalloc_unlock();
_pthread_onfork(2);
}

View file

@ -21,8 +21,10 @@
/**
* Destroys pthread attributes.
*
* @return 0 on success, or errno on error
*/
int pthread_attr_destroy(pthread_attr_t *attr) {
errno_t pthread_attr_destroy(pthread_attr_t *attr) {
memset(attr, -1, sizeof(*attr));
return 0;
}

View file

@ -26,7 +26,8 @@
* - `PTHREAD_CREATE_DETACHED`
* @return 0 on success, or error on failure
*/
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) {
errno_t pthread_attr_getdetachstate(const pthread_attr_t *attr,
int *detachstate) {
*detachstate = attr->__detachstate;
return 0;
}

View file

@ -21,7 +21,7 @@
/**
* Returns thread inherit schedule attribute.
*/
int pthread_attr_getinheritsched(const pthread_attr_t *attr,
errno_t pthread_attr_getinheritsched(const pthread_attr_t *attr,
int *inheritsched) {
*inheritsched = attr->__inheritsched;
return 0;

View file

@ -21,7 +21,7 @@
/**
* Gets thread scheduler parameter attribute.
*/
int pthread_attr_getschedparam(const pthread_attr_t *attr,
errno_t pthread_attr_getschedparam(const pthread_attr_t *attr,
struct sched_param *param) {
*param = (struct sched_param){attr->__schedparam};
return 0;

View file

@ -21,7 +21,7 @@
/**
* Gets thread scheduler policy attribute
*/
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) {
errno_t pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) {
*policy = attr->__schedpolicy;
return 0;
}

View file

@ -23,7 +23,8 @@
*
* @return 0 on success, or errno on error
*/
int pthread_attr_getscope(const pthread_attr_t *attr, int *contentionscope) {
errno_t pthread_attr_getscope(const pthread_attr_t *attr,
int *contentionscope) {
*contentionscope = attr->__contentionscope;
return 0;
}

View file

@ -34,7 +34,7 @@
* @return 0 on success, or error on failure
* @raises EINVAL if `detachstate` is invalid
*/
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) {
errno_t pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) {
switch (detachstate) {
case PTHREAD_CREATE_JOINABLE:
case PTHREAD_CREATE_DETACHED:

View file

@ -40,7 +40,7 @@
* @see sched_get_priority_max()
* @see sched_setparam()
*/
int pthread_attr_setschedparam(pthread_attr_t *attr,
errno_t pthread_attr_setschedparam(pthread_attr_t *attr,
const struct sched_param *param) {
if (!param) return EINVAL;
attr->__schedparam = param->sched_priority;

View file

@ -41,7 +41,7 @@
* supported (Linux), otherwise it's treated as `SCHED_OTHER`
* @see sched_setscheduler()
*/
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) {
errno_t pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) {
attr->__schedpolicy = policy;
return 0;
}

View file

@ -30,7 +30,7 @@
* @raise ENOTSUP if `contentionscope` isn't supported on host OS
* @raise EINVAL if `contentionscope` was invalid
*/
int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope) {
errno_t pthread_attr_setscope(pthread_attr_t *attr, int contentionscope) {
switch (contentionscope) {
case PTHREAD_SCOPE_SYSTEM:
attr->__contentionscope = contentionscope;

View file

@ -26,7 +26,7 @@
* @return 0 on success, or error on failure
* @raise EINVAL if threads are still inside the barrier
*/
int pthread_barrier_destroy(pthread_barrier_t *barrier) {
errno_t pthread_barrier_destroy(pthread_barrier_t *barrier) {
if (barrier->_nsync) {
nsync_counter_free(barrier->_nsync);
barrier->_nsync = 0;

View file

@ -30,8 +30,9 @@
* @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) {
errno_t pthread_barrier_init(pthread_barrier_t *barrier,
const pthread_barrierattr_t *attr,
unsigned count) {
nsync_counter c;
if (!count) return EINVAL;
if (!(c = nsync_counter_new(count))) return ENOMEM;

View file

@ -16,15 +16,15 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/thread/thread.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
/**
* Destroys barrier attributes.
*
* @return 0 on success, or error on failure
*/
int pthread_barrierattr_destroy(pthread_barrierattr_t *attr) {
errno_t pthread_barrierattr_destroy(pthread_barrierattr_t *attr) {
memset(attr, -1, sizeof(*attr));
return 0;
}

View file

@ -26,7 +26,7 @@
* - `PTHREAD_PROCESS_SHARED` (unsupported)
* @return 0 on success, or error on failure
*/
int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr,
errno_t pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr,
int *pshared) {
*pshared = *attr;
return 0;

View file

@ -23,7 +23,7 @@
*
* @return 0 on success, or error on failure
*/
int pthread_barrierattr_init(pthread_barrierattr_t *attr) {
errno_t pthread_barrierattr_init(pthread_barrierattr_t *attr) {
*attr = 0;
return 0;
}

View file

@ -28,7 +28,8 @@
* @return 0 on success, or error on failure
* @raises EINVAL if `pshared` is invalid
*/
int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared) {
errno_t pthread_barrierattr_setpshared(pthread_barrierattr_t *attr,
int pshared) {
switch (pshared) {
case PTHREAD_PROCESS_PRIVATE:
*attr = pshared;

View file

@ -24,6 +24,7 @@
#include "libc/calls/ucontext.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
@ -85,12 +86,12 @@ static void ListenForSigCancel(void) {
* `writev`, `pwrite`, `pwritev`, `accept`, `connect`, `recvmsg`,
* `sendmsg`, `recv`, `send`, `tcdrain`, `clock_nanosleep`, `fsync`,
* `fdatasync`, `fcntl(F_SETLKW)`, `epoll`, `sigsuspend`, `msync`,
* `wait4`, `getrandom`, `pthread_cond_timedwait` are most cancellation
* points, plus many userspace libraries that call the above functions,
* unless they're using pthread_setcancelstate() to temporarily disable
* the cancellation mechanism. Some userspace functions, e.g. system()
* and popen() will eagerly call pthread_testcancel_np() to help avoid
* the potential for resource leaks later on.
* `wait4`, `getrandom`, `pthread_cond_timedwait`, and `sem_timedwait`
* are most cancellation points, plus many userspace libraries that call
* the above functions, unless they're using pthread_setcancelstate() to
* temporarily disable the cancellation mechanism. Some userspace
* functions, e.g. system() will eagerly call pthread_testcancel_np() to
* help avoid the potential for resource leaks later on.
*
* It's possible to put a thread in asynchronous cancellation mode using
* pthread_setcanceltype(), thus allowing a cancellation to occur at any
@ -110,7 +111,7 @@ static void ListenForSigCancel(void) {
* @return 0 on success, or errno on error
* @raise ESRCH if thread isn't alive
*/
int pthread_cancel(pthread_t thread) {
errno_t pthread_cancel(pthread_t thread) {
int e, rc, tid;
static bool once;
struct PosixThread *pt;
@ -124,9 +125,9 @@ int pthread_cancel(pthread_t thread) {
default:
break;
}
atomic_store_explicit(&pt->cancelled, 1, memory_order_release);
atomic_exchange_explicit(&pt->cancelled, 1, memory_order_release);
if (thread == __get_tls()->tib_pthread) {
if (!(pt->flags & PT_NOCANCEL) && (pt->flags & PT_ASYNC)) {
if (!(pt->flags & (PT_NOCANCEL | PT_MASKED)) && (pt->flags & PT_ASYNC)) {
pthread_exit(PTHREAD_CANCELED);
}
return 0;
@ -183,7 +184,7 @@ void pthread_testcancel(void) {
* @return 0 if not cancelled or cancellation is blocked or `ECANCELED`
* in masked mode when the calling thread has been cancelled
*/
int pthread_testcancel_np(void) {
errno_t pthread_testcancel_np(void) {
int rc;
struct PosixThread *pt;
if (!__tls_enabled) return 0;

View file

@ -25,7 +25,7 @@
* @return 0 on success, or error number on failure
* @raise EINVAL if threads are still waiting on condition
*/
int pthread_cond_destroy(pthread_cond_t *cond) {
errno_t pthread_cond_destroy(pthread_cond_t *cond) {
memset(cond, -1, sizeof(*cond));
return 0;
}

View file

@ -24,7 +24,8 @@
* @param attr may be null
* @return 0 on success, or error number on failure
*/
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) {
errno_t pthread_cond_init(pthread_cond_t *cond,
const pthread_condattr_t *attr) {
*cond = (pthread_cond_t){0};
return 0;
}

View file

@ -41,8 +41,9 @@
* @raise EPERM if `mutex` is `PTHREAD_MUTEX_ERRORCHECK` and the lock
* isn't owned by the current thread
* @raise EINVAL if `0 abstime->tv_nsec < 1000000000` wasn't the case
* @see pthread_cond_broadcast
* @see pthread_cond_signal
* @raise ECANCELED if calling thread was cancelled in masked mode
* @see pthread_cond_broadcast()
* @see pthread_cond_signal()
* @cancellationpoint
*/
errno_t pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,

View file

@ -31,6 +31,7 @@
*
* @param mutex needs to be held by thread when calling this function
* @return 0 on success, or errno on error
* @raise ECANCELED if calling thread was cancelled in masked mode
* @raise EPERM if `mutex` is `PTHREAD_MUTEX_ERRORCHECK` and the lock
* isn't owned by the current thread
* @see pthread_cond_timedwait

View file

@ -16,15 +16,15 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/thread/thread.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
/**
* Destroys condition attributes.
*
* @return 0 on success, or error on failure
*/
int pthread_condattr_destroy(pthread_condattr_t *attr) {
errno_t pthread_condattr_destroy(pthread_condattr_t *attr) {
memset(attr, -1, sizeof(*attr));
return 0;
}

View file

@ -26,7 +26,8 @@
* - `PTHREAD_PROCESS_SHARED`
* @return 0 on success, or error on failure
*/
int pthread_condattr_getpshared(const pthread_condattr_t *attr, int *pshared) {
errno_t pthread_condattr_getpshared(const pthread_condattr_t *attr,
int *pshared) {
*pshared = *attr;
return 0;
}

View file

@ -23,7 +23,7 @@
*
* @return 0 on success, or error on failure
*/
int pthread_condattr_init(pthread_condattr_t *attr) {
errno_t pthread_condattr_init(pthread_condattr_t *attr) {
*attr = 0;
return 0;
}

View file

@ -28,7 +28,7 @@
* @return 0 on success, or error on failure
* @raises EINVAL if `pshared` is invalid
*/
int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) {
errno_t pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) {
switch (pshared) {
case PTHREAD_PROCESS_PRIVATE:
*attr = pshared;

View file

@ -59,8 +59,8 @@ STATIC_YOINK("_pthread_atfork");
#define MAP_ANON_OPENBSD 0x1000
#define MAP_STACK_OPENBSD 0x4000
void _pthread_wait(struct PosixThread *pt) {
_wait0(&pt->tib->tib_tid);
errno_t _pthread_wait(struct PosixThread *pt) {
return _wait0(&pt->tib->tib_tid);
}
void _pthread_free(struct PosixThread *pt) {

View file

@ -32,36 +32,29 @@
* @returnserrno
* @threadsafe
*/
int pthread_detach(pthread_t thread) {
errno_t pthread_detach(pthread_t thread) {
struct PosixThread *pt;
enum PosixThreadStatus status;
if (!(pt = (struct PosixThread *)thread)) {
return EINVAL;
}
enum PosixThreadStatus status, transition;
if (!(pt = (struct PosixThread *)thread)) return EINVAL;
for (;;) {
status = atomic_load_explicit(&pt->status, memory_order_acquire);
if (status == kPosixThreadDetached || status == kPosixThreadZombie) {
// these two states indicate the thread was already detached, in
// which case it's already listed under _pthread_zombies.
return EINVAL;
} else if (status == kPosixThreadTerminated) {
// thread was joinable and finished running. since pthread_join
// won't be called, it's safe to free the thread resources now.
// POSIX says this could be reported as ESRCH but then our test
// code would be less elegant in order for it to avoid flaking.
_pthread_wait(pt);
_pthread_free(pt);
break;
} else if (status == kPosixThreadJoinable) {
if (atomic_compare_exchange_weak_explicit(
&pt->status, &status, kPosixThreadDetached, memory_order_release,
transition = kPosixThreadDetached;
} else if (status == kPosixThreadTerminated) {
transition = kPosixThreadZombie;
} else {
notpossible;
}
if (atomic_compare_exchange_weak_explicit(&pt->status, &status, transition,
memory_order_release,
memory_order_relaxed)) {
_pthread_zombies_add(pt);
break;
}
} else {
notpossible;
}
}
return 0;
}

View file

@ -47,7 +47,7 @@
* @return 0 on success, or errno on error
* @raise ENOMEM is listed as a possible result by LSB 5.0
*/
int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr) {
errno_t pthread_getattr_np(pthread_t thread, pthread_attr_t *attr) {
struct PosixThread *pt = (struct PosixThread *)thread;
memcpy(attr, &pt->attr, sizeof(pt->attr));
switch (atomic_load_explicit(&pt->status, memory_order_relaxed)) {

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
@ -28,23 +29,7 @@
#include "libc/sysv/consts/pr.h"
#include "libc/thread/posixthread.internal.h"
/**
* Gets name of thread registered with system, e.g.
*
* char name[64];
* pthread_getname_np(thread, name, sizeof(name));
*
* If the thread doesn't have a name, then empty string is returned.
* This implementation guarantees `buf` is always modified, even on
* error, and will always be nul-terminated. If `size` is 0 then this
* function returns 0. Your `buf` is also chomped to remove newlines.
*
* @return 0 on success, or errno on error
* @raise ERANGE if `size` wasn't large enough, in which case your
* result will still be returned truncated if possible
* @raise ENOSYS on MacOS, Windows, FreeBSD, and OpenBSD
*/
errno_t pthread_getname_np(pthread_t thread, char *name, size_t size) {
static errno_t pthread_getname_impl(pthread_t thread, char *name, size_t size) {
int fd, rc, tid, len, e = errno;
if (!size) return 0;
@ -113,3 +98,27 @@ errno_t pthread_getname_np(pthread_t thread, char *name, size_t size) {
return ENOSYS;
}
}
/**
* Gets name of thread registered with system, e.g.
*
* char name[64];
* pthread_getname_np(thread, name, sizeof(name));
*
* If the thread doesn't have a name, then empty string is returned.
* This implementation guarantees `buf` is always modified, even on
* error, and will always be nul-terminated. If `size` is 0 then this
* function returns 0. Your `buf` is also chomped to remove newlines.
*
* @return 0 on success, or errno on error
* @raise ERANGE if `size` wasn't large enough, in which case your
* result will still be returned truncated if possible
* @raise ENOSYS on MacOS, Windows, FreeBSD, and OpenBSD
*/
errno_t pthread_getname_np(pthread_t thread, char *name, size_t size) {
errno_t rc;
BLOCK_CANCELLATIONS;
rc = pthread_getname_impl(thread, name, size);
ALLOW_CANCELLATIONS;
return rc;
}

View file

@ -22,7 +22,7 @@
/**
* Gets most recently set scheduling of thread.
*/
int pthread_getschedparam(pthread_t thread, int *policy,
errno_t pthread_getschedparam(pthread_t thread, int *policy,
struct sched_param *param) {
struct PosixThread *pt = (struct PosixThread *)thread;
*policy = pt->attr.__schedpolicy;

View file

@ -27,14 +27,16 @@
* @param value_ptr if non-null will receive pthread_exit() argument
* if the thread called pthread_exit(), or `PTHREAD_CANCELED` if
* pthread_cancel() destroyed the thread instead
* @return 0 on success, or errno with error
* @return 0 on success, or errno on error
* @raise ECANCELED if calling thread was cancelled in masked mode
* @raise EDEADLK if `thread` is the current thread
* @raise EINVAL if `thread` is detached
* @cancellationpoint
* @returnserrno
* @threadsafe
*/
int pthread_join(pthread_t thread, void **value_ptr) {
errno_t pthread_join(pthread_t thread, void **value_ptr) {
errno_t rc;
struct PosixThread *pt;
if (thread == __get_tls()->tib_pthread) {
return EDEADLK;
@ -44,7 +46,9 @@ int pthread_join(pthread_t thread, void **value_ptr) {
pt->status == kPosixThreadDetached) {
return EINVAL;
}
_pthread_wait(pt);
if ((rc = _pthread_wait(pt))) {
return rc;
}
if (value_ptr) {
*value_ptr = pt->rc;
}

View file

@ -31,7 +31,7 @@
* @raise EPERM if permission was denied
* @asyncsignalsafe
*/
int pthread_kill(pthread_t thread, int sig) {
errno_t pthread_kill(pthread_t thread, int sig) {
int rc, e = errno;
struct PosixThread *pt = (struct PosixThread *)thread;
if (!tkill(pt->tid, sig)) {

View file

@ -25,7 +25,7 @@
* @return 0 on success, or error number on failure
* @raise EINVAL if mutex is locked in our implementation
*/
int pthread_mutex_destroy(pthread_mutex_t *mutex) {
errno_t pthread_mutex_destroy(pthread_mutex_t *mutex) {
memset(mutex, -1, sizeof(*mutex));
return 0;
}

View file

@ -16,14 +16,14 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/thread/thread.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
/**
* Destroys mutex attr.
* @return 0 on success, or error number on failure
*/
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) {
errno_t pthread_mutexattr_destroy(pthread_mutexattr_t *attr) {
memset(attr, -1, sizeof(*attr));
return 0;
}

View file

@ -26,7 +26,7 @@
* - `PTHREAD_PROCESS_SHARED`
* @return 0 on success, or error on failure
*/
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr,
errno_t pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr,
int *pshared) {
*pshared = attr->_pshared;
return 0;

View file

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

View file

@ -22,7 +22,7 @@
* Initializes mutex attr.
* @return 0 on success, or error number on failure
*/
int pthread_mutexattr_init(pthread_mutexattr_t *attr) {
errno_t pthread_mutexattr_init(pthread_mutexattr_t *attr) {
*attr = (pthread_mutexattr_t){0};
return 0;
}

View file

@ -28,7 +28,7 @@
* @return 0 on success, or error on failure
* @raises EINVAL if `pshared` is invalid
*/
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) {
errno_t pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) {
switch (pshared) {
case PTHREAD_PROCESS_SHARED:
case PTHREAD_PROCESS_PRIVATE:

View file

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

View file

@ -23,7 +23,7 @@
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
int _pthread_reschedule(struct PosixThread *pt) {
errno_t _pthread_reschedule(struct PosixThread *pt) {
int rc, e = errno;
int policy = pt->attr.__schedpolicy;
struct sched_param param = {pt->attr.__schedparam};

View file

@ -25,7 +25,7 @@
* @return 0 on success, or error number on failure
* @raise EINVAL if any threads still hold the lock
*/
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) {
errno_t pthread_rwlock_destroy(pthread_rwlock_t *rwlock) {
memset(rwlock, -1, sizeof(*rwlock));
return 0;
}

View file

@ -24,7 +24,7 @@
* @param attr may be null
* @return 0 on success, or error number on failure
*/
int pthread_rwlock_init(pthread_rwlock_t *rwlock,
errno_t pthread_rwlock_init(pthread_rwlock_t *rwlock,
const pthread_rwlockattr_t *attr) {
*rwlock = (pthread_rwlock_t){0};
return 0;

View file

@ -16,15 +16,15 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/thread/thread.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
/**
* Destroys read-write lock attributes.
*
* @return 0 on success, or error on failure
*/
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) {
errno_t pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) {
memset(attr, -1, sizeof(*attr));
return 0;
}

View file

@ -26,7 +26,7 @@
* - `PTHREAD_PROCESS_SHARED` (unsupported)
* @return 0 on success, or error on failure
*/
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr,
errno_t pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr,
int *pshared) {
*pshared = *attr;
return 0;

View file

@ -23,7 +23,7 @@
*
* @return 0 on success, or error on failure
*/
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) {
errno_t pthread_rwlockattr_init(pthread_rwlockattr_t *attr) {
*attr = 0;
return 0;
}

View file

@ -28,7 +28,7 @@
* @return 0 on success, or error on failure
* @raises EINVAL if `pshared` is invalid
*/
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) {
errno_t pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) {
switch (pshared) {
case PTHREAD_PROCESS_PRIVATE:
*attr = pshared;

View file

@ -32,7 +32,7 @@
* @raise EINVAL if `type` has bad value
* @see pthread_cancel() for docs
*/
int pthread_setcanceltype(int type, int *oldtype) {
errno_t pthread_setcanceltype(int type, int *oldtype) {
struct PosixThread *pt;
switch (type) {
case PTHREAD_CANCEL_DEFERRED:

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
@ -28,30 +29,7 @@
#include "libc/sysv/consts/pr.h"
#include "libc/thread/posixthread.internal.h"
/**
* Registers custom name of thread with system, e.g.
*
* void *worker(void *arg) {
* pthread_setname_np(pthread_self(), "justine");
* pause();
* return 0;
* }
*
* int main(int argc, char *argv[]) {
* pthread_t id;
* pthread_create(&id, 0, worker, 0);
* pthread_join(id, 0);
* }
*
* ProTip: The `htop` software is good at displaying thread names.
*
* @return 0 on success, or errno on error
* @raise ERANGE if length of `name` exceeded system limit, in which
* case the name may have still been set with os using truncation
* @raise ENOSYS on MacOS, Windows, and OpenBSD
* @see pthread_getname_np()
*/
errno_t pthread_setname_np(pthread_t thread, const char *name) {
static errno_t pthread_setname_impl(pthread_t thread, const char *name) {
char path[128], *p;
int fd, rc, tid, len, e = errno;
@ -113,3 +91,34 @@ errno_t pthread_setname_np(pthread_t thread, const char *name) {
return ENOSYS;
}
}
/**
* Registers custom name of thread with system, e.g.
*
* void *worker(void *arg) {
* pthread_setname_np(pthread_self(), "justine");
* pause();
* return 0;
* }
*
* int main(int argc, char *argv[]) {
* pthread_t id;
* pthread_create(&id, 0, worker, 0);
* pthread_join(id, 0);
* }
*
* ProTip: The `htop` software is good at displaying thread names.
*
* @return 0 on success, or errno on error
* @raise ERANGE if length of `name` exceeded system limit, in which
* case the name may have still been set with os using truncation
* @raise ENOSYS on MacOS, Windows, and OpenBSD
* @see pthread_getname_np()
*/
errno_t pthread_setname_np(pthread_t thread, const char *name) {
errno_t rc;
BLOCK_CANCELLATIONS;
rc = pthread_setname_impl(thread, name);
ALLOW_CANCELLATIONS;
return rc;
}

View file

@ -41,7 +41,7 @@
* @see sched_get_priority_max()
* @see sched_setscheduler()
*/
int pthread_setschedparam(pthread_t thread, int policy,
errno_t pthread_setschedparam(pthread_t thread, int policy,
const struct sched_param *param) {
struct PosixThread *pt = (struct PosixThread *)thread;
if (!param) return EINVAL;

View file

@ -22,7 +22,7 @@
/**
* Sets scheduler parameter on thread.
*/
int pthread_setschedprio(pthread_t thread, int prio) {
errno_t pthread_setschedprio(pthread_t thread, int prio) {
struct PosixThread *pt = (struct PosixThread *)thread;
pt->attr.__schedparam = prio;
return _pthread_reschedule(pt);

View file

@ -25,7 +25,7 @@
* @return 0 on success, or errno on error
* @asyncsignalsafe
*/
int pthread_sigmask(int how, const sigset_t *set, sigset_t *old) {
errno_t pthread_sigmask(int how, const sigset_t *set, sigset_t *old) {
int rc, e = errno;
if (!sigprocmask(how, set, old)) {
rc = 0;

View file

@ -44,6 +44,7 @@ void _pthread_zombies_add(struct PosixThread *pt) {
}
static void _pthread_zombies_collect(struct Zombie *z) {
// TODO(jart): We need a trywait() op here to avoid cancellation.
_pthread_wait(z->pt);
_pthread_free(z->pt);
free(z);

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
@ -195,6 +196,7 @@ sem_t *sem_open(const char *name, int oflag, ...) {
if (!(path = sem_path_np(name, pathbuf, sizeof(pathbuf)))) {
return SEM_FAILED;
}
BLOCK_CANCELLATIONS;
sem_open_init();
sem_open_lock();
if ((s = sem_open_reopen(path))) {
@ -233,6 +235,7 @@ sem_t *sem_open(const char *name, int oflag, ...) {
sem = SEM_FAILED;
}
sem_open_unlock();
ALLOW_CANCELLATIONS;
return sem;
}

View file

@ -24,6 +24,7 @@
#include "libc/limits.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/semaphore.h"
#include "libc/thread/thread.h"
#include "third_party/nsync/futex.internal.h"
static void sem_delay(int n) {
@ -51,11 +52,18 @@ static struct timespec *sem_timeout(struct timespec *memory,
}
}
static void sem_timedwait_cleanup(void *arg) {
sem_t *sem = arg;
_unassert(atomic_fetch_add_explicit(&sem->sem_waiters, -1,
memory_order_acq_rel) > 0);
}
/**
* Locks semaphore w/ deadline.
*
* @param abstime is absolute deadline or null to wait forever
* @return 0 on success, or -1 w/ errno
* @raise ECANCELED if calling thread was cancelled in masked mode
* @raise EINTR if signal was delivered instead
* @raise EDEADLK if deadlock was detected
* @raise ETIMEDOUT if deadline expired
@ -80,14 +88,16 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime) {
}
_unassert(atomic_fetch_add_explicit(&sem->sem_waiters, +1,
memory_order_acquire) >= 0);
memory_order_acq_rel) >= 0);
pthread_cleanup_push(sem_timedwait_cleanup, sem);
do {
if (!(v = atomic_load_explicit(&sem->sem_value, memory_order_relaxed))) {
rc = nsync_futex_wait_(&sem->sem_value, v, sem->sem_pshared,
sem_timeout(&ts, abstime));
if (rc == -EINTR) {
rc = eintr();
if (rc == -EINTR || rc == -ECANCELED) {
errno = -rc;
rc = -1;
} else if (rc == -EAGAIN || rc == -EWOULDBLOCK) {
rc = 0;
} else if (rc == -ETIMEDOUT) {
@ -111,8 +121,7 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime) {
&sem->sem_value, &v, v - 1, memory_order_acquire,
memory_order_relaxed)));
_unassert(atomic_fetch_add_explicit(&sem->sem_waiters, -1,
memory_order_release) > 0);
pthread_cleanup_pop(1);
return rc;
}

View file

@ -22,6 +22,7 @@
* Locks semaphore.
*
* @return 0 on success, or -1 w/ errno
* @raise ECANCELED if calling thread was cancelled in masked mode
* @raise EINTR if signal was delivered instead
* @raise EDEADLK if deadlock was detected
* @raise EINVAL if `sem` is invalid

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/thread/wait0.internal.h"
#include "third_party/nsync/futex.internal.h"
@ -29,10 +30,16 @@
* order to know when it's safe to free a thread's stack. This function
* uses futexes on Linux, FreeBSD, OpenBSD, and Windows. On other
* platforms this uses polling with exponential backoff.
*
* @return 0 on success, or errno on error
* @raise ECANCELED if calling thread was cancelled in masked mode
*/
void _wait0(const atomic_int *ctid) {
int x;
errno_t _wait0(const atomic_int *ctid) {
int x, rc;
while ((x = atomic_load_explicit(ctid, memory_order_acquire))) {
nsync_futex_wait_(ctid, x, !IsWindows(), 0);
if (nsync_futex_wait_(ctid, x, !IsWindows(), 0) == -ECANCELED) {
return ECANCELED;
}
}
return 0;
}

View file

@ -4,7 +4,7 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
void _wait0(const atomic_int *) hidden;
errno_t _wait0(const atomic_int *) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -24,6 +24,7 @@
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/sysinfo.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timeval.h"
#include "libc/dce.h"
@ -358,6 +359,13 @@ struct Claims {
} data[QUEUE_MAX];
} g_claims;
long GetTotalRam(void) {
struct sysinfo si;
si.totalram = 256 * 1024 * 1024;
sysinfo(&si);
return si.totalram;
}
// easy string sender
ssize_t Write(int fd, const char *s) {
return write(fd, s, strlen(s));
@ -397,8 +405,14 @@ bool CheckDb(const char *file, int line, int rc, sqlite3 *db) {
// we need to do is wait a little bit, and use exponential backoff
int DbOpen(const char *path, sqlite3 **db) {
int i, rc;
char sql[128];
rc = sqlite3_open(path, db);
if (rc != SQLITE_OK) return rc;
if (!IsWindows() && !IsOpenbsd()) {
ksnprintf(sql, sizeof(sql), "PRAGMA mmap_size=%ld", GetTotalRam());
rc = sqlite3_exec(*db, sql, 0, 0, 0);
if (rc != SQLITE_OK) return rc;
}
for (i = 0; i < 7; ++i) {
rc = sqlite3_exec(*db, "PRAGMA journal_mode=WAL", 0, 0, 0);
if (rc == SQLITE_OK) break;
@ -410,6 +424,7 @@ int DbOpen(const char *path, sqlite3 **db) {
// why not make the statement prepare api a little less hairy too
int DbPrepare(sqlite3 *db, sqlite3_stmt **stmt, const char *sql) {
kprintf("%s\n", sql);
return sqlite3_prepare_v2(db, sql, -1, stmt, 0);
}

View file

@ -194,7 +194,7 @@ TEST(sigaction, autoZombieSlayer) {
if (!pid) _Exit(0);
ASSERT_SYS(0, pid, wait(0));
// enable automatic zombie slayer
sa.sa_handler = SIG_IGN;
sa.sa_handler = SIG_DFL; // POSIX.1 says no SIG_IGN
sa.sa_flags = SA_NOCLDWAIT; // seems to be optional
sigemptyset(&sa.sa_mask);
ASSERT_SYS(0, 0, sigaction(SIGCHLD, &sa, &sa));

View file

@ -24,8 +24,11 @@
#include "libc/runtime/runtime.h"
#include "libc/testlib/testlib.h"
#include "libc/thread/thread.h"
#include "libc/thread/thread2.h"
int pfds[2];
pthread_cond_t cv;
pthread_mutex_t mu;
atomic_bool gotcleanup;
char testlib_enable_tmp_setup_teardown;
@ -37,6 +40,49 @@ void OnCleanup(void *arg) {
gotcleanup = true;
}
void *CancelSelfWorkerDeferred(void *arg) {
char buf[8];
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, 0);
pthread_cleanup_push(OnCleanup, 0);
pthread_cancel(pthread_self());
read(pfds[0], buf, sizeof(buf));
pthread_cleanup_pop(0);
return 0;
}
TEST(pthread_cancel, self_deferred_waitsForCancellationPoint) {
void *rc;
pthread_t th;
ASSERT_SYS(0, 0, pipe(pfds));
ASSERT_EQ(0, pthread_create(&th, 0, CancelSelfWorkerDeferred, 0));
ASSERT_EQ(0, pthread_join(th, &rc));
ASSERT_EQ(PTHREAD_CANCELED, rc);
ASSERT_TRUE(gotcleanup);
ASSERT_SYS(0, 0, close(pfds[1]));
ASSERT_SYS(0, 0, close(pfds[0]));
}
void *CancelSelfWorkerAsync(void *arg) {
char buf[8];
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
pthread_cleanup_push(OnCleanup, 0);
pthread_cancel(pthread_self());
pthread_cleanup_pop(0);
return 0;
}
TEST(pthread_cancel, self_asynchronous_takesImmediateEffect) {
void *rc;
pthread_t th;
ASSERT_SYS(0, 0, pipe(pfds));
ASSERT_EQ(0, pthread_create(&th, 0, CancelSelfWorkerAsync, 0));
ASSERT_EQ(0, pthread_join(th, &rc));
ASSERT_EQ(PTHREAD_CANCELED, rc);
ASSERT_TRUE(gotcleanup);
ASSERT_SYS(0, 0, close(pfds[1]));
ASSERT_SYS(0, 0, close(pfds[0]));
}
void *Worker(void *arg) {
int n;
char buf[8];
@ -74,7 +120,6 @@ TEST(pthread_cancel, synchronous_delayed) {
}
void *DisabledWorker(void *arg) {
int n;
char buf[8];
pthread_setcancelstate(PTHREAD_CANCEL_MASKED, 0);
pthread_cleanup_push(OnCleanup, 0);
@ -109,3 +154,61 @@ TEST(pthread_cancel, masked_delayed) {
ASSERT_SYS(0, 0, close(pfds[1]));
ASSERT_SYS(0, 0, close(pfds[0]));
}
void *CondWaitMaskedWorker(void *arg) {
pthread_setcancelstate(PTHREAD_CANCEL_MASKED, 0);
ASSERT_EQ(0, pthread_mutex_lock(&mu));
ASSERT_EQ(ECANCELED, pthread_cond_timedwait(&cv, &mu, 0));
ASSERT_EQ(0, pthread_mutex_unlock(&mu));
return 0;
}
TEST(pthread_cancel, condMaskedWait) {
void *rc;
pthread_t th;
ASSERT_EQ(0, pthread_create(&th, 0, CondWaitMaskedWorker, 0));
ASSERT_EQ(0, pthread_cancel(th));
ASSERT_EQ(0, pthread_join(th, &rc));
ASSERT_EQ(0, rc);
}
TEST(pthread_cancel, condWaitMaskedDelayed) {
void *rc;
pthread_t th;
ASSERT_EQ(0, pthread_create(&th, 0, CondWaitMaskedWorker, 0));
usleep(10);
ASSERT_EQ(0, pthread_cancel(th));
ASSERT_EQ(0, pthread_join(th, &rc));
ASSERT_EQ(0, rc);
}
void *CondWaitDeferredWorker(void *arg) {
pthread_setcancelstate(PTHREAD_CANCEL_DEFERRED, 0);
ASSERT_EQ(0, pthread_mutex_lock(&mu));
ASSERT_EQ(ECANCELED, pthread_cond_timedwait(&cv, &mu, 0));
ASSERT_EQ(0, pthread_mutex_unlock(&mu));
return 0;
}
TEST(pthread_cancel, condDeferredWait) {
void *rc;
pthread_t th;
ASSERT_EQ(0, pthread_create(&th, 0, CondWaitDeferredWorker, 0));
ASSERT_EQ(0, pthread_cancel(th));
ASSERT_EQ(0, pthread_join(th, &rc));
ASSERT_EQ(PTHREAD_CANCELED, rc);
ASSERT_EQ(0, pthread_mutex_trylock(&mu));
ASSERT_EQ(0, pthread_mutex_unlock(&mu));
}
TEST(pthread_cancel, condDeferredWaitDelayed) {
void *rc;
pthread_t th;
ASSERT_EQ(0, pthread_create(&th, 0, CondWaitDeferredWorker, 0));
usleep(10);
ASSERT_EQ(0, pthread_cancel(th));
ASSERT_EQ(0, pthread_join(th, &rc));
ASSERT_EQ(PTHREAD_CANCELED, rc);
ASSERT_EQ(0, pthread_mutex_trylock(&mu));
ASSERT_EQ(0, pthread_mutex_unlock(&mu));
}

View file

@ -30,6 +30,7 @@
#include "libc/mem/mem.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
#include "third_party/musl/passwd.h"
asm(".ident\t\"\\n\\n\
@ -37,9 +38,6 @@ Musl libc (MIT License)\\n\
Copyright 2005-2014 Rich Felker, et. al.\"");
asm(".include \"libc/disclaimer.inc\"");
#define PTHREAD_CANCEL_DISABLE 0
#define pthread_setcancelstate(x, y) (void)y
static unsigned atou(char **s) {
unsigned x;
for (x = 0; **s - '0' < 10U; ++*s) x = 10 * x + (**s - '0');

View file

@ -32,6 +32,7 @@
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/s.h"
#include "libc/thread/thread.h"
#include "third_party/musl/ftw.h"
asm(".ident\t\"\\n\\n\
@ -44,9 +45,6 @@ asm(".include \"libc/disclaimer.inc\"");
should be changed to use realloc */
#define PATH_MAX2 2048
/* no cosmo pthreads support atm */
#define pthread_setcancelstate(...)
/* clang-format off */
struct history

View file

@ -30,6 +30,7 @@
#include "libc/mem/mem.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
#include "third_party/musl/passwd.h"
asm(".ident\t\"\\n\\n\
@ -38,9 +39,6 @@ Copyright 2005-2014 Rich Felker, et. al.\"");
asm(".include \"libc/disclaimer.inc\"");
/* clang-format off */
#define PTHREAD_CANCEL_DISABLE 0
#define pthread_setcancelstate(x, y) (void)y
static unsigned
atou(char **s)
{

View file

@ -17,3 +17,4 @@ LOCAL CHANGES
- nsync_malloc_() is implemented as kmalloc()
- nsync_mu_semaphore uses Cosmopolitan Futexes
- block pthread cancellations in nsync_mu_lock_slow_

View file

@ -23,17 +23,20 @@
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/limits.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/futex.h"
#include "libc/sysv/consts/timer.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/freebsd.internal.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
@ -45,7 +48,9 @@
#define FUTEX_WAIT_BITS_ FUTEX_BITSET_MATCH_ANY
int _futex (atomic_int *, int, int, const struct timespec *, int *, int);
errno_t _futex (atomic_int *, int, int, const struct timespec *, int *, int);
errno_t _futex_wake (atomic_int *, int, int) asm ("_futex");
int sys_futex_cp (atomic_int *, int, int, const struct timespec *, int *, int);
static int FUTEX_WAIT_;
static int FUTEX_PRIVATE_FLAG_;
@ -59,6 +64,7 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) {
if (IsWindows ()) {
FUTEX_IS_SUPPORTED = true;
FUTEX_TIMEOUT_IS_ABSOLUTE = true;
return;
}
@ -70,8 +76,6 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) {
}
if (!(FUTEX_IS_SUPPORTED = IsLinux () || IsOpenbsd ())) {
// we're using sched_yield() so let's
// avoid needless clock_gettime calls
FUTEX_TIMEOUT_IS_ABSOLUTE = true;
return;
}
@ -101,7 +105,7 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) {
FUTEX_TIMEOUT_IS_ABSOLUTE = true;
} else if (IsOpenbsd () ||
(!IsTiny () && IsLinux () &&
!_futex (&x, FUTEX_WAKE_PRIVATE, 1, 0, 0, 0))) {
!_futex_wake (&x, FUTEX_WAKE_PRIVATE, 1))) {
FUTEX_WAIT_ = FUTEX_WAIT;
FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
} else {
@ -110,13 +114,10 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) {
}
static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *timeout) {
int rc;
int64_t nanos, maxnanos;
struct timespec ts, deadline;
if (atomic_load_explicit (w, memory_order_relaxed) != expect) {
return -EAGAIN;
}
ts = nsync_time_now ();
if (!timeout) {
deadline = nsync_time_no_deadline;
@ -129,15 +130,15 @@ static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *ti
nanos = 100;
maxnanos = __SIG_POLLING_INTERVAL_MS * 1000L * 1000;
while (nsync_time_cmp (deadline, ts) > 0) {
if (atomic_load_explicit (w, memory_order_relaxed) != expect) {
return 0;
}
ts = nsync_time_add (ts, _timespec_fromnanos (nanos));
if (nsync_time_cmp (ts, deadline) > 0) {
ts = deadline;
}
if (nsync_time_sleep_until (ts)) {
return -EINTR;
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
return 0;
}
if ((rc = nsync_time_sleep_until (ts))) {
return -rc;
}
if (nanos < maxnanos) {
nanos <<= 1;
@ -150,10 +151,46 @@ static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *ti
return -ETIMEDOUT;
}
int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, struct timespec *timeout) {
static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare, struct timespec *timeout) {
int rc;
uint32_t ms;
struct timespec deadline, interval, remain, wait, now;
if (timeout) {
deadline = *timeout;
} else {
deadline = nsync_time_no_deadline;
}
while (!(rc = _check_interrupts (false, 0))) {
now = nsync_time_now ();
if (nsync_time_cmp (now, deadline) > 0) {
rc = etimedout();
break;
}
remain = nsync_time_sub (deadline, now);
interval = _timespec_frommillis (__SIG_POLLING_INTERVAL_MS);
wait = nsync_time_cmp (remain, interval) > 0 ? interval : remain;
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
break;
}
if (WaitOnAddress (w, &expect, sizeof(int), _timespec_tomillis (wait))) {
break;
} else {
ASSERT (GetLastError () == ETIMEDOUT);
}
}
return rc;
}
int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, struct timespec *timeout) {
int e, rc, op, fop;
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
return -EAGAIN;
}
op = FUTEX_WAIT_;
if (pshare == PTHREAD_PROCESS_PRIVATE) {
op |= FUTEX_PRIVATE_FLAG_;
@ -165,36 +202,34 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, struct timespec *
DescribeTimespec (0, timeout));
if (FUTEX_IS_SUPPORTED) {
e = errno;
if (IsWindows ()) {
// Windows 8 futexes don't support multiple processes :(
if (pshare) {
goto Polyfill;
if (pshare) goto Polyfill;
rc = nsync_futex_wait_win32_ (w, expect, pshare, timeout);
} else if (IsFreebsd ()) {
rc = sys_umtx_timedwait_uint (w, expect, pshare, timeout);
} else {
rc = sys_futex_cp (w, op, expect, timeout, 0, FUTEX_WAIT_BITS_);
if (IsOpenbsd() && rc > 0) {
// OpenBSD futex() returns errors as
// positive numbers without setting the
// carry flag. It's an irregular miracle
// because OpenBSD returns ECANCELED if
// futex() is interrupted w/ SA_RESTART
// so we're able to tell it apart from
// PT_MASKED which causes the wrapper
// to put ECANCELED into errno.
if (rc == ECANCELED) {
rc = EINTR;
}
e = errno;
if (_check_interrupts (false, 0)) {
errno = rc;
rc = -1;
}
}
if (rc == -1) {
rc = -errno;
errno = e;
} else {
if (timeout) {
ms = _timespec_tomillis (*timeout);
} else {
ms = -1;
}
if (WaitOnAddress (w, &expect, sizeof(int), ms)) {
rc = 0;
} else {
rc = -GetLastError ();
}
}
} else if (IsFreebsd ()) {
rc = sys_umtx_timedwait_uint (
w, expect, pshare, timeout);
} else {
rc = _futex (w, op, expect, timeout, 0,
FUTEX_WAIT_BITS_);
if (IsOpenbsd() && rc > 0) {
rc = -rc;
}
}
} else {
Polyfill:
@ -214,7 +249,6 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, struct timespec *
int nsync_futex_wake_ (atomic_int *w, int count, char pshare) {
int e, rc, op, fop;
int wake (atomic_int *, int, int) asm ("_futex");
ASSERT (count == 1 || count == INT_MAX);
@ -240,9 +274,9 @@ int nsync_futex_wake_ (atomic_int *w, int count, char pshare) {
} else {
fop = UMTX_OP_WAKE_PRIVATE;
}
rc = sys_umtx_op (w, fop, count, 0, 0);
rc = _futex_wake (w, fop, count);
} else {
rc = wake (w, op, count);
rc = _futex_wake (w, op, count);
}
} else {
Polyfill:

View file

@ -16,6 +16,7 @@
limitations under the License.
*/
#include "libc/str/str.h"
#include "libc/thread/thread.h"
#include "third_party/nsync/atomic.internal.h"
#include "third_party/nsync/common.internal.h"
#include "third_party/nsync/cv.h"
@ -202,6 +203,7 @@ int nsync_cv_wait_with_deadline_generic (nsync_cv *pcv, void *pmu,
waiter *w;
IGNORE_RACES_START ();
w = nsync_waiter_new_ ();
pthread_cleanup_push((void *)nsync_waiter_free_, w);
ATM_STORE (&w->nw.waiting, 1);
w->cond.f = NULL; /* Not using a conditional critical section. */
w->cond.v = NULL;
@ -312,6 +314,7 @@ int nsync_cv_wait_with_deadline_generic (nsync_cv *pcv, void *pmu,
(*lock) (pmu);
}
}
pthread_cleanup_pop(0);
IGNORE_RACES_END ();
return (outcome);
}

View file

@ -15,6 +15,7 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "libc/calls/blockcancel.internal.h"
#include "third_party/nsync/atomic.h"
#include "third_party/nsync/common.internal.h"
#include "third_party/nsync/dll.h"
@ -153,6 +154,7 @@ int nsync_mu_wait_with_deadline (nsync_mu *mu,
/* Work out in which mode the lock is held. */
uint32_t old_word;
IGNORE_RACES_START ();
BLOCK_CANCELLATIONS; /* not supported yet */
old_word = ATM_LOAD (&mu->word);
if ((old_word & MU_ANY_LOCK) == 0) {
nsync_panic_ ("nsync_mu not held in some mode when calling "
@ -265,6 +267,7 @@ int nsync_mu_wait_with_deadline (nsync_mu *mu,
if (condition_is_true) {
outcome = 0; /* condition is true trumps other outcomes. */
}
ALLOW_CANCELLATIONS;
IGNORE_RACES_END ();
return (outcome);
}

View file

@ -15,6 +15,7 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/mem/mem.h"
#include "third_party/nsync/atomic.h"
#include "third_party/nsync/atomic.internal.h"
@ -36,6 +37,7 @@ int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *),
int count, struct nsync_waitable_s *waitable[]) {
int ready;
IGNORE_RACES_START ();
BLOCK_CANCELLATIONS; /* TODO(jart): Does this need pthread cancellations? */
for (ready = 0; ready != count &&
nsync_time_cmp ((*waitable[ready]->funcs->ready_time) (
waitable[ready]->v, NULL),
@ -102,6 +104,7 @@ int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *),
(*lock) (mu);
}
}
ALLOW_CANCELLATIONS;
IGNORE_RACES_END ();
return (ready);
}

View file

@ -15,6 +15,7 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/str/str.h"
#include "third_party/nsync/atomic.h"
#include "third_party/nsync/common.internal.h"
@ -52,6 +53,7 @@ void nsync_mu_lock_slow_ (nsync_mu *mu, waiter *w, uint32_t clear, lock_type *l_
uint32_t wait_count;
uint32_t long_wait;
unsigned attempts = 0; /* attempt count; used for spinloop backoff */
BLOCK_CANCELLATIONS;
w->cv_mu = NULL; /* not a cv wait */
w->cond.f = NULL; /* Not using a conditional critical section. */
w->cond.v = NULL;
@ -72,7 +74,7 @@ void nsync_mu_lock_slow_ (nsync_mu *mu, waiter *w, uint32_t clear, lock_type *l_
if (ATM_CAS_ACQ (&mu->word, old_word,
(old_word+l_type->add_to_acquire) &
~(clear|long_wait|l_type->clear_on_acquire))) {
return;
break;
}
} else if ((old_word&MU_SPINLOCK) == 0 &&
ATM_CAS_ACQ (&mu->word, old_word,
@ -126,6 +128,7 @@ void nsync_mu_lock_slow_ (nsync_mu *mu, waiter *w, uint32_t clear, lock_type *l_
}
attempts = nsync_spin_delay_ (attempts);
}
ALLOW_CANCELLATIONS;
}
/* Attempt to acquire *mu in writer mode without blocking, and return non-zero

View file

@ -17,6 +17,8 @@
*/
#include "libc/assert.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/weaken.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
#include "third_party/nsync/atomic.h"
@ -51,25 +53,32 @@ void nsync_mu_semaphore_init (nsync_semaphore *s) {
}
/* Wait until the count of *s exceeds 0, and decrement it. */
void nsync_mu_semaphore_p (nsync_semaphore *s) {
errno_t nsync_mu_semaphore_p (nsync_semaphore *s) {
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 = nsync_futex_wait_ ((atomic_int *)&f->i, i, PTHREAD_PROCESS_PRIVATE, NULL);
int futex_result = nsync_futex_wait_ (
(atomic_int *)&f->i, i, PTHREAD_PROCESS_PRIVATE, NULL);
ASSERT (futex_result == 0 ||
futex_result == -EINTR ||
futex_result == -EAGAIN ||
futex_result == -ECANCELED ||
futex_result == -EWOULDBLOCK);
if (futex_result == -ECANCELED) {
result = ECANCELED;
}
} while (i == 0 || !ATM_CAS_ACQ ((nsync_atomic_uint32_ *) &f->i, i, i-1));
}
} while (result == 0 && (i == 0 || !ATM_CAS_ACQ ((nsync_atomic_uint32_ *) &f->i, i, i-1)));
return result;
}
/* 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) {
errno_t nsync_mu_semaphore_p_with_deadline (nsync_semaphore *s, nsync_time abs_deadline) {
struct futex *f = (struct futex *)s;
int i;
int result = 0;
@ -103,6 +112,7 @@ int nsync_mu_semaphore_p_with_deadline (nsync_semaphore *s, nsync_time abs_deadl
ASSERT (futex_result == 0 ||
futex_result == -EINTR ||
futex_result == -EAGAIN ||
futex_result == -ECANCELED ||
futex_result == -ETIMEDOUT ||
futex_result == -EWOULDBLOCK);
/* Some systems don't wait as long as they are told. */
@ -110,6 +120,9 @@ int nsync_mu_semaphore_p_with_deadline (nsync_semaphore *s, nsync_time abs_deadl
nsync_time_cmp (abs_deadline, nsync_time_now ()) <= 0) {
result = ETIMEDOUT;
}
if (futex_result == -ECANCELED) {
result = ECANCELED;
}
}
} while (result == 0 && (i == 0 || !ATM_CAS_ACQ ((nsync_atomic_uint32_ *) &f->i, i, i - 1)));
return (result);

View file

@ -12,12 +12,12 @@ typedef struct nsync_semaphore_s_ {
void nsync_mu_semaphore_init(nsync_semaphore *s);
/* Wait until the count of *s exceeds 0, and decrement it. */
void nsync_mu_semaphore_p(nsync_semaphore *s);
errno_t nsync_mu_semaphore_p(nsync_semaphore *s);
/* 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,
errno_t nsync_mu_semaphore_p_with_deadline(nsync_semaphore *s,
nsync_time abs_deadline);
/* Ensure that the count of *s is at least 1. */

View file

@ -17,4 +17,5 @@ LOCAL CHANGES
- Added `--strace` system call tracing flag to SQLite shell
- Added `--strace` function call logging flag to SQLite shell
- Configured fsync() using runtime magnums rather than ifdefs
- Modify preprocessor macro for enabling pread() and pwrite()
- Save and restore errno in some places to avoid log pollution

View file

@ -84,7 +84,7 @@
#endif
/* Use pread() and pwrite() if they are available */
#if defined(__APPLE__) || defined(__COSMOPOLITAN__)
#if defined(__APPLE__) || defined(__COSMOPOLITAN__) /* [jart] */
# define HAVE_PREAD 1
# define HAVE_PWRITE 1
#endif

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