diff --git a/libc/calls/_timespec_sleep.c b/libc/calls/_timespec_sleep.c index 472241876..d660712c1 100644 --- a/libc/calls/_timespec_sleep.c +++ b/libc/calls/_timespec_sleep.c @@ -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; } } diff --git a/libc/calls/_timespec_sleep_until.c b/libc/calls/_timespec_sleep_until.c index 088cdb6e8..f4c5adfe3 100644 --- a/libc/calls/_timespec_sleep_until.c +++ b/libc/calls/_timespec_sleep_until.c @@ -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; } diff --git a/libc/calls/blockcancel.internal.h b/libc/calls/blockcancel.internal.h new file mode 100644 index 000000000..e3baf981e --- /dev/null +++ b/libc/calls/blockcancel.internal.h @@ -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_ */ diff --git a/libc/calls/blocksigs.internal.h b/libc/calls/blocksigs.internal.h index 1886f02c1..b4747498f 100644 --- a/libc/calls/blocksigs.internal.h +++ b/libc/calls/blocksigs.internal.h @@ -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_ */ diff --git a/libc/calls/fcntl.c b/libc/calls/fcntl.c index f27beb4e9..8a5adfd89 100644 --- a/libc/calls/fcntl.c +++ b/libc/calls/fcntl.c @@ -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); diff --git a/libc/calls/openpty.c b/libc/calls/openpty.c index 779512cf3..13bedb0dd 100644 --- a/libc/calls/openpty.c +++ b/libc/calls/openpty.c @@ -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, - const struct winsize *wsz) { +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; +} diff --git a/libc/intrin/assertfail.greg.c b/libc/intrin/assertfail.greg.c index 725842ea2..fe80c9271 100644 --- a/libc/intrin/assertfail.greg.c +++ b/libc/intrin/assertfail.greg.c @@ -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( diff --git a/libc/thread/pthread_cleanup_pop.c b/libc/intrin/pthread_cleanup_pop.c similarity index 92% rename from libc/thread/pthread_cleanup_pop.c rename to libc/intrin/pthread_cleanup_pop.c index f8bb41e0f..fba3b853e 100644 --- a/libc/thread/pthread_cleanup_pop.c +++ b/libc/intrin/pthread_cleanup_pop.c @@ -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; - _unassert(cb == pt->cleanup); - pt->cleanup = cb->__prev; + 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); } diff --git a/libc/thread/pthread_cleanup_push.c b/libc/intrin/pthread_cleanup_push.c similarity index 91% rename from libc/thread/pthread_cleanup_push.c rename to libc/intrin/pthread_cleanup_push.c index dd5428968..519345072 100644 --- a/libc/thread/pthread_cleanup_push.c +++ b/libc/intrin/pthread_cleanup_push.c @@ -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; - cb->__prev = pt->cleanup; - pt->cleanup = cb; + if (__tls_enabled && (pt = (struct PosixThread *)__get_tls()->tib_pthread)) { + cb->__prev = pt->cleanup; + pt->cleanup = cb; + } } diff --git a/libc/thread/pthread_setcancelstate.c b/libc/intrin/pthread_setcancelstate.c similarity index 76% rename from libc/thread/pthread_setcancelstate.c rename to libc/intrin/pthread_setcancelstate.c index 7889c07a2..97364ae94 100644 --- a/libc/thread/pthread_setcancelstate.c +++ b/libc/intrin/pthread_setcancelstate.c @@ -43,30 +43,36 @@ * @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; - 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; - } else if (pt->flags & PT_MASKED) { - *oldstate = PTHREAD_CANCEL_MASKED; - } else { - *oldstate = PTHREAD_CANCEL_ENABLE; + if (__tls_enabled && (pt = (struct PosixThread *)__get_tls()->tib_pthread)) { + switch (state) { + case PTHREAD_CANCEL_ENABLE: + case PTHREAD_CANCEL_DISABLE: + case PTHREAD_CANCEL_MASKED: + if (oldstate) { + if (pt->flags & PT_NOCANCEL) { + *oldstate = PTHREAD_CANCEL_DISABLE; + } else if (pt->flags & PT_MASKED) { + *oldstate = PTHREAD_CANCEL_MASKED; + } else { + *oldstate = PTHREAD_CANCEL_ENABLE; + } } - } - pt->flags &= ~(PT_NOCANCEL | PT_MASKED); - if (state == PTHREAD_CANCEL_MASKED) { - pt->flags |= PT_MASKED; - } else if (state == PTHREAD_CANCEL_DISABLE) { - pt->flags |= PT_NOCANCEL; - } - return 0; - default: - return EINVAL; + pt->flags &= ~(PT_NOCANCEL | PT_MASKED); + if (state == PTHREAD_CANCEL_MASKED) { + pt->flags |= PT_MASKED; + } else if (state == PTHREAD_CANCEL_DISABLE) { + pt->flags |= PT_NOCANCEL; + } + return 0; + default: + return EINVAL; + } + } else { + if (oldstate) { + *oldstate = 0; + } + return 0; } } diff --git a/libc/intrin/sys_umtx_op.S b/libc/intrin/sys_umtx_op.S deleted file mode 100644 index 677990917..000000000 --- a/libc/intrin/sys_umtx_op.S +++ /dev/null @@ -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 diff --git a/libc/intrin/sys_umtx_timedwait_uint.c b/libc/intrin/sys_umtx_timedwait_uint.c index 4b33a3422..85567eca2 100644 --- a/libc/intrin/sys_umtx_timedwait_uint.c +++ b/libc/intrin/sys_umtx_timedwait_uint.c @@ -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); } diff --git a/libc/log/die.c b/libc/log/die.c index a69acf3f1..ce371564b 100644 --- a/libc/log/die.c +++ b/libc/log/die.c @@ -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)) { diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index 8a17e1435..2d4a79db3 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -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) { diff --git a/libc/log/startfatal.c b/libc/log/startfatal.c index 3fe945c48..f3334ccef 100644 --- a/libc/log/startfatal.c +++ b/libc/log/startfatal.c @@ -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, diff --git a/libc/sock/syslog.c b/libc/sock/syslog.c index 0308fd326..4b2f8a255 100644 --- a/libc/sock/syslog.c +++ b/libc/sock/syslog.c @@ -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 "[]" * 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)) { - int fd = open("/dev/console", O_WRONLY|O_NOCTTY); + 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); + 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,13 +310,12 @@ void closelog(void) { if (log_facility == -1) { __initlog(); } - if (log_fd == -1) { - return; + if (log_fd != -1) { + if (IsWindows()) { + DeregisterEventSource(log_fd); + } else { + close(log_fd); + } + log_fd = -1; } - if (IsWindows()) { - DeregisterEventSource(log_fd); - } else { - close(log_fd); - } - log_fd = -1; } diff --git a/libc/stdio/getentropy.c b/libc/stdio/getentropy.c index 816189b85..6e1070beb 100644 --- a/libc/stdio/getentropy.c +++ b/libc/stdio/getentropy.c @@ -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; } diff --git a/libc/stdio/getrandom.c b/libc/stdio/getrandom.c index 1acef9f25..6050cde77 100644 --- a/libc/stdio/getrandom.c +++ b/libc/stdio/getrandom.c @@ -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; diff --git a/libc/sysv/calls/sys_futex.s b/libc/sysv/calls/sys_futex.s index db56b252d..0248bff55 100644 --- a/libc/sysv/calls/sys_futex.s +++ b/libc/sysv/calls/sys_futex.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_futex,0x0a6053fffffff0ca,globl,hidden +.scall sys_futex,0x0a60531c6ffff0ca,globl,hidden diff --git a/libc/sysv/calls/sys_futex_cp.s b/libc/sysv/calls/sys_futex_cp.s new file mode 100644 index 000000000..0538a5cfa --- /dev/null +++ b/libc/sysv/calls/sys_futex_cp.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_futex_cp,0x8a68539c6ffff8ca,globl,hidden diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 20334d0d5..075963a2a 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -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 diff --git a/libc/sysv/consts/__NR_futex.s b/libc/sysv/consts/__NR_futex.s index a7bd77d79..61c7a2ff5 100644 --- a/libc/sysv/consts/__NR_futex.s +++ b/libc/sysv/consts/__NR_futex.s @@ -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 diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 8cab83c6a..2d338097b 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -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 diff --git a/libc/testlib/testrunner.c b/libc/testlib/testrunner.c index 8e3f44166..92aa966b3 100644 --- a/libc/testlib/testrunner.c +++ b/libc/testlib/testrunner.c @@ -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)(); diff --git a/libc/thread/freebsd.internal.h b/libc/thread/freebsd.internal.h index 5134a108d..d6473c231 100644 --- a/libc/thread/freebsd.internal.h +++ b/libc/thread/freebsd.internal.h @@ -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_ diff --git a/libc/thread/posixthread.internal.h b/libc/thread/posixthread.internal.h index b63df05af..e09f50a28 100644 --- a/libc/thread/posixthread.internal.h +++ b/libc/thread/posixthread.internal.h @@ -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; diff --git a/libc/thread/pthread_atfork.c b/libc/thread/pthread_atfork.c index 2b3a69324..a19b86e05 100644 --- a/libc/thread/pthread_atfork.c +++ b/libc/thread/pthread_atfork.c @@ -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); } diff --git a/libc/thread/pthread_attr_destroy.c b/libc/thread/pthread_attr_destroy.c index 299e737a1..9145233c5 100644 --- a/libc/thread/pthread_attr_destroy.c +++ b/libc/thread/pthread_attr_destroy.c @@ -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; } diff --git a/libc/thread/pthread_attr_getdetachstate.c b/libc/thread/pthread_attr_getdetachstate.c index 2ae2d1c0d..baf6ff126 100644 --- a/libc/thread/pthread_attr_getdetachstate.c +++ b/libc/thread/pthread_attr_getdetachstate.c @@ -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; } diff --git a/libc/thread/pthread_attr_getinheritsched.c b/libc/thread/pthread_attr_getinheritsched.c index 97e757961..097c80825 100644 --- a/libc/thread/pthread_attr_getinheritsched.c +++ b/libc/thread/pthread_attr_getinheritsched.c @@ -21,8 +21,8 @@ /** * Returns thread inherit schedule attribute. */ -int pthread_attr_getinheritsched(const pthread_attr_t *attr, - int *inheritsched) { +errno_t pthread_attr_getinheritsched(const pthread_attr_t *attr, + int *inheritsched) { *inheritsched = attr->__inheritsched; return 0; } diff --git a/libc/thread/pthread_attr_getschedparam.c b/libc/thread/pthread_attr_getschedparam.c index 694e9629b..3f53b0c97 100644 --- a/libc/thread/pthread_attr_getschedparam.c +++ b/libc/thread/pthread_attr_getschedparam.c @@ -21,8 +21,8 @@ /** * Gets thread scheduler parameter attribute. */ -int pthread_attr_getschedparam(const pthread_attr_t *attr, - struct sched_param *param) { +errno_t pthread_attr_getschedparam(const pthread_attr_t *attr, + struct sched_param *param) { *param = (struct sched_param){attr->__schedparam}; return 0; } diff --git a/libc/thread/pthread_attr_getschedpolicy.c b/libc/thread/pthread_attr_getschedpolicy.c index b07164899..791ad4b90 100644 --- a/libc/thread/pthread_attr_getschedpolicy.c +++ b/libc/thread/pthread_attr_getschedpolicy.c @@ -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; } diff --git a/libc/thread/pthread_attr_getscope.c b/libc/thread/pthread_attr_getscope.c index 2556481fe..e3fd27cd6 100644 --- a/libc/thread/pthread_attr_getscope.c +++ b/libc/thread/pthread_attr_getscope.c @@ -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; } diff --git a/libc/thread/pthread_attr_setdetachstate.c b/libc/thread/pthread_attr_setdetachstate.c index 55d9edf26..f6d807eb8 100644 --- a/libc/thread/pthread_attr_setdetachstate.c +++ b/libc/thread/pthread_attr_setdetachstate.c @@ -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: diff --git a/libc/thread/pthread_attr_setschedparam.c b/libc/thread/pthread_attr_setschedparam.c index 0c09a6a26..f0871c481 100644 --- a/libc/thread/pthread_attr_setschedparam.c +++ b/libc/thread/pthread_attr_setschedparam.c @@ -40,8 +40,8 @@ * @see sched_get_priority_max() * @see sched_setparam() */ -int pthread_attr_setschedparam(pthread_attr_t *attr, - const struct sched_param *param) { +errno_t pthread_attr_setschedparam(pthread_attr_t *attr, + const struct sched_param *param) { if (!param) return EINVAL; attr->__schedparam = param->sched_priority; return 0; diff --git a/libc/thread/pthread_attr_setschedpolicy.c b/libc/thread/pthread_attr_setschedpolicy.c index 294573173..d839741de 100644 --- a/libc/thread/pthread_attr_setschedpolicy.c +++ b/libc/thread/pthread_attr_setschedpolicy.c @@ -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; } diff --git a/libc/thread/pthread_attr_setscope.c b/libc/thread/pthread_attr_setscope.c index 648198523..9f8d4b307 100644 --- a/libc/thread/pthread_attr_setscope.c +++ b/libc/thread/pthread_attr_setscope.c @@ -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; diff --git a/libc/thread/pthread_barrier_destroy.c b/libc/thread/pthread_barrier_destroy.c index c249bbe60..400a21198 100644 --- a/libc/thread/pthread_barrier_destroy.c +++ b/libc/thread/pthread_barrier_destroy.c @@ -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; diff --git a/libc/thread/pthread_barrier_init.c b/libc/thread/pthread_barrier_init.c index b83a35230..041cf0023 100644 --- a/libc/thread/pthread_barrier_init.c +++ b/libc/thread/pthread_barrier_init.c @@ -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; diff --git a/libc/thread/pthread_barrierattr_destroy.c b/libc/thread/pthread_barrierattr_destroy.c index 3777f2b3f..6d288c8a6 100644 --- a/libc/thread/pthread_barrierattr_destroy.c +++ b/libc/thread/pthread_barrierattr_destroy.c @@ -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; } diff --git a/libc/thread/pthread_barrierattr_getpshared.c b/libc/thread/pthread_barrierattr_getpshared.c index bd2c547b0..2fcb380f9 100644 --- a/libc/thread/pthread_barrierattr_getpshared.c +++ b/libc/thread/pthread_barrierattr_getpshared.c @@ -26,8 +26,8 @@ * - `PTHREAD_PROCESS_SHARED` (unsupported) * @return 0 on success, or error on failure */ -int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr, - int *pshared) { +errno_t pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr, + int *pshared) { *pshared = *attr; return 0; } diff --git a/libc/thread/pthread_barrierattr_init.c b/libc/thread/pthread_barrierattr_init.c index b009911e2..142a906a6 100644 --- a/libc/thread/pthread_barrierattr_init.c +++ b/libc/thread/pthread_barrierattr_init.c @@ -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; } diff --git a/libc/thread/pthread_barrierattr_setpshared.c b/libc/thread/pthread_barrierattr_setpshared.c index 4ae2adbb6..594c69ac3 100644 --- a/libc/thread/pthread_barrierattr_setpshared.c +++ b/libc/thread/pthread_barrierattr_setpshared.c @@ -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; diff --git a/libc/thread/pthread_cancel.c b/libc/thread/pthread_cancel.c index 79371dd93..4bd51ff2a 100644 --- a/libc/thread/pthread_cancel.c +++ b/libc/thread/pthread_cancel.c @@ -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; diff --git a/libc/thread/pthread_cond_destroy.c b/libc/thread/pthread_cond_destroy.c index 579b2399a..99b3bb81b 100644 --- a/libc/thread/pthread_cond_destroy.c +++ b/libc/thread/pthread_cond_destroy.c @@ -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; } diff --git a/libc/thread/pthread_cond_init.c b/libc/thread/pthread_cond_init.c index aa5e8e8b7..5619a7999 100644 --- a/libc/thread/pthread_cond_init.c +++ b/libc/thread/pthread_cond_init.c @@ -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; } diff --git a/libc/thread/pthread_cond_timedwait.c b/libc/thread/pthread_cond_timedwait.c index 03de7b369..b213c040f 100644 --- a/libc/thread/pthread_cond_timedwait.c +++ b/libc/thread/pthread_cond_timedwait.c @@ -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, diff --git a/libc/thread/pthread_cond_wait.c b/libc/thread/pthread_cond_wait.c index 6606f7787..b7a120fd1 100644 --- a/libc/thread/pthread_cond_wait.c +++ b/libc/thread/pthread_cond_wait.c @@ -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 diff --git a/libc/thread/pthread_condattr_destroy.c b/libc/thread/pthread_condattr_destroy.c index abf437dd5..a1376ad0f 100644 --- a/libc/thread/pthread_condattr_destroy.c +++ b/libc/thread/pthread_condattr_destroy.c @@ -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; } diff --git a/libc/thread/pthread_condattr_getpshared.c b/libc/thread/pthread_condattr_getpshared.c index dab698903..f9791291d 100644 --- a/libc/thread/pthread_condattr_getpshared.c +++ b/libc/thread/pthread_condattr_getpshared.c @@ -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; } diff --git a/libc/thread/pthread_condattr_init.c b/libc/thread/pthread_condattr_init.c index 7e61998fd..ad617215c 100644 --- a/libc/thread/pthread_condattr_init.c +++ b/libc/thread/pthread_condattr_init.c @@ -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; } diff --git a/libc/thread/pthread_condattr_setpshared.c b/libc/thread/pthread_condattr_setpshared.c index ccd7ae242..e6c261c2c 100644 --- a/libc/thread/pthread_condattr_setpshared.c +++ b/libc/thread/pthread_condattr_setpshared.c @@ -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; diff --git a/libc/thread/pthread_create.c b/libc/thread/pthread_create.c index af8396466..339d32cb6 100644 --- a/libc/thread/pthread_create.c +++ b/libc/thread/pthread_create.c @@ -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) { diff --git a/libc/thread/pthread_detach.c b/libc/thread/pthread_detach.c index fdf06dd62..24493ddf5 100644 --- a/libc/thread/pthread_detach.c +++ b/libc/thread/pthread_detach.c @@ -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, - memory_order_relaxed)) { - _pthread_zombies_add(pt); - break; - } + 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; + } } return 0; } diff --git a/libc/thread/pthread_getattr_np.c b/libc/thread/pthread_getattr_np.c index 49e730696..d9742c985 100644 --- a/libc/thread/pthread_getattr_np.c +++ b/libc/thread/pthread_getattr_np.c @@ -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)) { diff --git a/libc/thread/pthread_getname_np.c b/libc/thread/pthread_getname_np.c index eeb81688a..02eeb89f7 100644 --- a/libc/thread/pthread_getname_np.c +++ b/libc/thread/pthread_getname_np.c @@ -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; +} diff --git a/libc/thread/pthread_getschedparam.c b/libc/thread/pthread_getschedparam.c index bd3093aa1..91ead4efc 100644 --- a/libc/thread/pthread_getschedparam.c +++ b/libc/thread/pthread_getschedparam.c @@ -22,8 +22,8 @@ /** * Gets most recently set scheduling of thread. */ -int pthread_getschedparam(pthread_t thread, int *policy, - struct sched_param *param) { +errno_t pthread_getschedparam(pthread_t thread, int *policy, + struct sched_param *param) { struct PosixThread *pt = (struct PosixThread *)thread; *policy = pt->attr.__schedpolicy; *param = (struct sched_param){pt->attr.__schedparam}; diff --git a/libc/thread/pthread_join.c b/libc/thread/pthread_join.c index 082854970..6477f6a9c 100644 --- a/libc/thread/pthread_join.c +++ b/libc/thread/pthread_join.c @@ -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; } diff --git a/libc/thread/pthread_kill.c b/libc/thread/pthread_kill.c index 61e39716d..615340b71 100644 --- a/libc/thread/pthread_kill.c +++ b/libc/thread/pthread_kill.c @@ -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)) { diff --git a/libc/thread/pthread_mutex_destroy.c b/libc/thread/pthread_mutex_destroy.c index 87f4efa72..8199b6b81 100644 --- a/libc/thread/pthread_mutex_destroy.c +++ b/libc/thread/pthread_mutex_destroy.c @@ -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; } diff --git a/libc/thread/pthread_mutexattr_destroy.c b/libc/thread/pthread_mutexattr_destroy.c index 49df496c1..6405b9144 100644 --- a/libc/thread/pthread_mutexattr_destroy.c +++ b/libc/thread/pthread_mutexattr_destroy.c @@ -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; } diff --git a/libc/thread/pthread_mutexattr_getpshared.c b/libc/thread/pthread_mutexattr_getpshared.c index c521b91ce..fff1450a2 100644 --- a/libc/thread/pthread_mutexattr_getpshared.c +++ b/libc/thread/pthread_mutexattr_getpshared.c @@ -26,8 +26,8 @@ * - `PTHREAD_PROCESS_SHARED` * @return 0 on success, or error on failure */ -int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, - int *pshared) { +errno_t pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, + int *pshared) { *pshared = attr->_pshared; return 0; } diff --git a/libc/thread/pthread_mutexattr_gettype.c b/libc/thread/pthread_mutexattr_gettype.c index 5d1e9a8d7..117c10b63 100644 --- a/libc/thread/pthread_mutexattr_gettype.c +++ b/libc/thread/pthread_mutexattr_gettype.c @@ -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; } diff --git a/libc/thread/pthread_mutexattr_init.c b/libc/thread/pthread_mutexattr_init.c index a12bec7d8..115420480 100644 --- a/libc/thread/pthread_mutexattr_init.c +++ b/libc/thread/pthread_mutexattr_init.c @@ -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; } diff --git a/libc/thread/pthread_mutexattr_setpshared.c b/libc/thread/pthread_mutexattr_setpshared.c index c575cbd95..062bcdffc 100644 --- a/libc/thread/pthread_mutexattr_setpshared.c +++ b/libc/thread/pthread_mutexattr_setpshared.c @@ -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: diff --git a/libc/thread/pthread_mutexattr_settype.c b/libc/thread/pthread_mutexattr_settype.c index 766a4b7a4..2e5cdfe10 100644 --- a/libc/thread/pthread_mutexattr_settype.c +++ b/libc/thread/pthread_mutexattr_settype.c @@ -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: diff --git a/libc/thread/pthread_reschedule.c b/libc/thread/pthread_reschedule.c index 1319a7dc2..4aca1c16e 100644 --- a/libc/thread/pthread_reschedule.c +++ b/libc/thread/pthread_reschedule.c @@ -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}; diff --git a/libc/thread/pthread_rwlock_destroy.c b/libc/thread/pthread_rwlock_destroy.c index 47bc63c10..2e5d2bb60 100644 --- a/libc/thread/pthread_rwlock_destroy.c +++ b/libc/thread/pthread_rwlock_destroy.c @@ -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; } diff --git a/libc/thread/pthread_rwlock_init.c b/libc/thread/pthread_rwlock_init.c index c9790a9b7..313daca38 100644 --- a/libc/thread/pthread_rwlock_init.c +++ b/libc/thread/pthread_rwlock_init.c @@ -24,8 +24,8 @@ * @param attr may be null * @return 0 on success, or error number on failure */ -int pthread_rwlock_init(pthread_rwlock_t *rwlock, - const pthread_rwlockattr_t *attr) { +errno_t pthread_rwlock_init(pthread_rwlock_t *rwlock, + const pthread_rwlockattr_t *attr) { *rwlock = (pthread_rwlock_t){0}; return 0; } diff --git a/libc/thread/pthread_rwlockattr_destroy.c b/libc/thread/pthread_rwlockattr_destroy.c index 624b5fb5d..853f973dd 100644 --- a/libc/thread/pthread_rwlockattr_destroy.c +++ b/libc/thread/pthread_rwlockattr_destroy.c @@ -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; } diff --git a/libc/thread/pthread_rwlockattr_getpshared.c b/libc/thread/pthread_rwlockattr_getpshared.c index efafcb927..afcb140fd 100644 --- a/libc/thread/pthread_rwlockattr_getpshared.c +++ b/libc/thread/pthread_rwlockattr_getpshared.c @@ -26,8 +26,8 @@ * - `PTHREAD_PROCESS_SHARED` (unsupported) * @return 0 on success, or error on failure */ -int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, - int *pshared) { +errno_t pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, + int *pshared) { *pshared = *attr; return 0; } diff --git a/libc/thread/pthread_rwlockattr_init.c b/libc/thread/pthread_rwlockattr_init.c index dee6fb305..01807d180 100644 --- a/libc/thread/pthread_rwlockattr_init.c +++ b/libc/thread/pthread_rwlockattr_init.c @@ -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; } diff --git a/libc/thread/pthread_rwlockattr_setpshared.c b/libc/thread/pthread_rwlockattr_setpshared.c index 5904c4d6c..e634edcc9 100644 --- a/libc/thread/pthread_rwlockattr_setpshared.c +++ b/libc/thread/pthread_rwlockattr_setpshared.c @@ -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; diff --git a/libc/thread/pthread_setcanceltype.c b/libc/thread/pthread_setcanceltype.c index 65c7d7f2a..b61b8fcfa 100644 --- a/libc/thread/pthread_setcanceltype.c +++ b/libc/thread/pthread_setcanceltype.c @@ -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: diff --git a/libc/thread/pthread_setname_np.c b/libc/thread/pthread_setname_np.c index 0d10ae7d7..3123dce94 100644 --- a/libc/thread/pthread_setname_np.c +++ b/libc/thread/pthread_setname_np.c @@ -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; +} diff --git a/libc/thread/pthread_setschedparam.c b/libc/thread/pthread_setschedparam.c index 7126ba710..7a5d36c13 100644 --- a/libc/thread/pthread_setschedparam.c +++ b/libc/thread/pthread_setschedparam.c @@ -41,8 +41,8 @@ * @see sched_get_priority_max() * @see sched_setscheduler() */ -int pthread_setschedparam(pthread_t thread, int policy, - const struct sched_param *param) { +errno_t pthread_setschedparam(pthread_t thread, int policy, + const struct sched_param *param) { struct PosixThread *pt = (struct PosixThread *)thread; if (!param) return EINVAL; pt->attr.__schedpolicy = policy; diff --git a/libc/thread/pthread_setschedprio.c b/libc/thread/pthread_setschedprio.c index 8bb96c24b..6763aedce 100644 --- a/libc/thread/pthread_setschedprio.c +++ b/libc/thread/pthread_setschedprio.c @@ -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); diff --git a/libc/thread/pthread_sigmask.c b/libc/thread/pthread_sigmask.c index 03093c8dc..2d84d8094 100644 --- a/libc/thread/pthread_sigmask.c +++ b/libc/thread/pthread_sigmask.c @@ -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; diff --git a/libc/thread/pthread_zombies.c b/libc/thread/pthread_zombies.c index 0fe40f220..8ae0e9c28 100644 --- a/libc/thread/pthread_zombies.c +++ b/libc/thread/pthread_zombies.c @@ -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); diff --git a/libc/thread/sem_open.c b/libc/thread/sem_open.c index 16ddf0cf6..f235d026e 100644 --- a/libc/thread/sem_open.c +++ b/libc/thread/sem_open.c @@ -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; } diff --git a/libc/thread/sem_timedwait.c b/libc/thread/sem_timedwait.c index dd098c044..c57cf7ebb 100644 --- a/libc/thread/sem_timedwait.c +++ b/libc/thread/sem_timedwait.c @@ -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; } diff --git a/libc/thread/sem_wait.c b/libc/thread/sem_wait.c index e32b41ed3..90c2052ef 100644 --- a/libc/thread/sem_wait.c +++ b/libc/thread/sem_wait.c @@ -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 diff --git a/libc/thread/wait0.c b/libc/thread/wait0.c index f3ec6ea07..1d9d34ec2 100644 --- a/libc/thread/wait0.c +++ b/libc/thread/wait0.c @@ -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; } diff --git a/libc/thread/wait0.internal.h b/libc/thread/wait0.internal.h index ec89485a9..2aeaa7478 100644 --- a/libc/thread/wait0.internal.h +++ b/libc/thread/wait0.internal.h @@ -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) */ diff --git a/net/turfwar/turfwar.c b/net/turfwar/turfwar.c index 8a82e40ff..5947d8d44 100644 --- a/net/turfwar/turfwar.c +++ b/net/turfwar/turfwar.c @@ -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); } diff --git a/test/libc/calls/sigaction_test.c b/test/libc/calls/sigaction_test.c index 86806b071..fb102d7bf 100644 --- a/test/libc/calls/sigaction_test.c +++ b/test/libc/calls/sigaction_test.c @@ -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)); diff --git a/test/libc/thread/pthread_cancel_test.c b/test/libc/thread/pthread_cancel_test.c index 16eb3e0bf..e59f99e4c 100644 --- a/test/libc/thread/pthread_cancel_test.c +++ b/test/libc/thread/pthread_cancel_test.c @@ -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)); +} diff --git a/third_party/musl/grp.c b/third_party/musl/grp.c index d97a2e293..9a1e92699 100644 --- a/third_party/musl/grp.c +++ b/third_party/musl/grp.c @@ -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'); diff --git a/third_party/musl/nftw.c b/third_party/musl/nftw.c index 159a3144c..48145e705 100644 --- a/third_party/musl/nftw.c +++ b/third_party/musl/nftw.c @@ -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 diff --git a/third_party/musl/pwd.c b/third_party/musl/pwd.c index b56aafa25..67ad73658 100644 --- a/third_party/musl/pwd.c +++ b/third_party/musl/pwd.c @@ -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) { diff --git a/third_party/nsync/README.cosmo b/third_party/nsync/README.cosmo index 2ebdf744b..159da96ef 100644 --- a/third_party/nsync/README.cosmo +++ b/third_party/nsync/README.cosmo @@ -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_ diff --git a/third_party/nsync/futex.c b/third_party/nsync/futex.c index 8f5c5c0b0..c69504e9c 100644 --- a/third_party/nsync/futex.c +++ b/third_party/nsync/futex.c @@ -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,37 +202,35 @@ 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; - } - e = errno; - if (_check_interrupts (false, 0)) { - 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 (); - } - } + 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); + rc = sys_umtx_timedwait_uint (w, expect, pshare, timeout); } else { - rc = _futex (w, op, expect, timeout, 0, - FUTEX_WAIT_BITS_); + rc = sys_futex_cp (w, op, expect, timeout, 0, FUTEX_WAIT_BITS_); if (IsOpenbsd() && rc > 0) { - rc = -rc; + // 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; + } + errno = rc; + rc = -1; } } + if (rc == -1) { + rc = -errno; + errno = e; + } } else { Polyfill: __get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL; @@ -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: diff --git a/third_party/nsync/mem/nsync_cv.c b/third_party/nsync/mem/nsync_cv.c index 7e2cf1428..fb42829b4 100644 --- a/third_party/nsync/mem/nsync_cv.c +++ b/third_party/nsync/mem/nsync_cv.c @@ -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); } diff --git a/third_party/nsync/mem/nsync_mu_wait.c b/third_party/nsync/mem/nsync_mu_wait.c index 9b409e239..693839fbe 100644 --- a/third_party/nsync/mem/nsync_mu_wait.c +++ b/third_party/nsync/mem/nsync_mu_wait.c @@ -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); } diff --git a/third_party/nsync/mem/nsync_wait.c b/third_party/nsync/mem/nsync_wait.c index de75ab165..71cffaece 100644 --- a/third_party/nsync/mem/nsync_wait.c +++ b/third_party/nsync/mem/nsync_wait.c @@ -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); } diff --git a/third_party/nsync/mu.c b/third_party/nsync/mu.c index bda08494d..28fd8d7a8 100644 --- a/third_party/nsync/mu.c +++ b/third_party/nsync/mu.c @@ -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 diff --git a/third_party/nsync/mu_semaphore.c b/third_party/nsync/mu_semaphore.c index a43b04432..8a903c5b6 100644 --- a/third_party/nsync/mu_semaphore.c +++ b/third_party/nsync/mu_semaphore.c @@ -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); diff --git a/third_party/nsync/mu_semaphore.h b/third_party/nsync/mu_semaphore.h index 3cf252808..a2c9c6b45 100644 --- a/third_party/nsync/mu_semaphore.h +++ b/third_party/nsync/mu_semaphore.h @@ -12,13 +12,13 @@ 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, - nsync_time abs_deadline); +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. */ void nsync_mu_semaphore_v(nsync_semaphore *s); diff --git a/third_party/sqlite3/README.cosmo b/third_party/sqlite3/README.cosmo index df74597f3..d61fcf0e9 100644 --- a/third_party/sqlite3/README.cosmo +++ b/third_party/sqlite3/README.cosmo @@ -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 diff --git a/third_party/sqlite3/os_unix.c b/third_party/sqlite3/os_unix.c index 5b0350517..00281e861 100644 --- a/third_party/sqlite3/os_unix.c +++ b/third_party/sqlite3/os_unix.c @@ -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 diff --git a/third_party/sqlite3/sqlite3.mk b/third_party/sqlite3/sqlite3.mk index a1c984505..85acb556d 100644 --- a/third_party/sqlite3/sqlite3.mk +++ b/third_party/sqlite3/sqlite3.mk @@ -126,7 +126,7 @@ THIRD_PARTY_SQLITE3_FLAGS = \ -DSQLITE_HAVE_C99_MATH_FUNCS \ -DSQLITE_ENABLE_MATH_FUNCTIONS \ -DSQLITE_ENABLE_JSON1 \ - -DSQLITE_ENABLE_DESERIALIZE \ + -DSQLITE_ENABLE_DESERIALIZE ifeq ($(MODE),dbg) THIRD_PARTY_SQLITE3_CPPFLAGS_DEBUG = -DSQLITE_DEBUG