mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Make futexes cancellable by pthreads
This commit is contained in:
parent
2278327eba
commit
022536cab6
101 changed files with 627 additions and 391 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
19
libc/calls/blockcancel.internal.h
Normal file
19
libc/calls/blockcancel.internal.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_BLOCKCANCEL_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_BLOCKCANCEL_H_
|
||||
#include "libc/thread/thread.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#define BLOCK_CANCELLATIONS \
|
||||
do { \
|
||||
int _CancelState; \
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &_CancelState)
|
||||
|
||||
#define ALLOW_CANCELLATIONS \
|
||||
pthread_setcancelstate(_CancelState, 0); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_BLOCKCANCEL_H_ */
|
|
@ -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_ */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/ioctl.h"
|
||||
#include "libc/calls/struct/metatermios.internal.h"
|
||||
|
@ -43,31 +44,12 @@ struct IoctlPtmGet {
|
|||
char sname[16];
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens new pseudo teletypewriter.
|
||||
*
|
||||
* @param mfd receives controlling tty rw fd on success
|
||||
* @param sfd receives subordinate tty rw fd on success
|
||||
* @param tio may be passed to tune a century of legacy behaviors
|
||||
* @param wsz may be passed to set terminal display dimensions
|
||||
* @params flags is usually O_RDWR|O_NOCTTY
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
*/
|
||||
int openpty(int *mfd, int *sfd, char *name, const struct termios *tio,
|
||||
static int openpty_impl(int *mfd, int *sfd, char *name,
|
||||
const struct termios *tio, //
|
||||
const struct winsize *wsz) {
|
||||
int m, s, p;
|
||||
union metatermios mt;
|
||||
struct IoctlPtmGet t;
|
||||
if (IsWindows() || IsMetal()) {
|
||||
return enosys();
|
||||
}
|
||||
if (IsAsan() && (!__asan_is_valid(mfd, sizeof(int)) ||
|
||||
!__asan_is_valid(sfd, sizeof(int)) ||
|
||||
(name && !__asan_is_valid(name, 16)) ||
|
||||
(tio && !__asan_is_valid(tio, sizeof(*tio))) ||
|
||||
(wsz && !__asan_is_valid(wsz, sizeof(*wsz))))) {
|
||||
return efault();
|
||||
}
|
||||
RETURN_ON_ERROR((m = posix_openpt(O_RDWR | O_NOCTTY)));
|
||||
if (!IsOpenbsd()) {
|
||||
RETURN_ON_ERROR(grantpt(m));
|
||||
|
@ -90,3 +72,33 @@ OnError:
|
|||
if (m != -1) sys_close(m);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens new pseudo teletypewriter.
|
||||
*
|
||||
* @param mfd receives controlling tty rw fd on success
|
||||
* @param sfd receives subordinate tty rw fd on success
|
||||
* @param tio may be passed to tune a century of legacy behaviors
|
||||
* @param wsz may be passed to set terminal display dimensions
|
||||
* @params flags is usually O_RDWR|O_NOCTTY
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
*/
|
||||
int openpty(int *mfd, int *sfd, char *name, //
|
||||
const struct termios *tio, //
|
||||
const struct winsize *wsz) {
|
||||
int rc;
|
||||
if (IsWindows() || IsMetal()) {
|
||||
return enosys();
|
||||
}
|
||||
if (IsAsan() && (!__asan_is_valid(mfd, sizeof(int)) ||
|
||||
!__asan_is_valid(sfd, sizeof(int)) ||
|
||||
(name && !__asan_is_valid(name, 16)) ||
|
||||
(tio && !__asan_is_valid(tio, sizeof(*tio))) ||
|
||||
(wsz && !__asan_is_valid(wsz, sizeof(*wsz))))) {
|
||||
return efault();
|
||||
}
|
||||
BLOCK_CANCELLATIONS;
|
||||
rc = openpty(mfd, sfd, name, tio, wsz);
|
||||
ALLOW_CANCELLATIONS;
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -22,9 +22,11 @@
|
|||
#include "libc/thread/tls.h"
|
||||
|
||||
void _pthread_cleanup_pop(struct _pthread_cleanup_buffer *cb, int execute) {
|
||||
struct PosixThread *pt = (struct PosixThread *)__get_tls()->tib_pthread;
|
||||
struct PosixThread *pt;
|
||||
if (__tls_enabled && (pt = (struct PosixThread *)__get_tls()->tib_pthread)) {
|
||||
_unassert(cb == pt->cleanup);
|
||||
pt->cleanup = cb->__prev;
|
||||
}
|
||||
if (execute) {
|
||||
cb->__routine(cb->__arg);
|
||||
}
|
|
@ -18,12 +18,15 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
void _pthread_cleanup_push(struct _pthread_cleanup_buffer *cb,
|
||||
void (*routine)(void *), void *arg) {
|
||||
struct PosixThread *pt = (struct PosixThread *)__get_tls()->tib_pthread;
|
||||
struct PosixThread *pt;
|
||||
cb->__routine = routine;
|
||||
cb->__arg = arg;
|
||||
if (__tls_enabled && (pt = (struct PosixThread *)__get_tls()->tib_pthread)) {
|
||||
cb->__prev = pt->cleanup;
|
||||
pt->cleanup = cb;
|
||||
}
|
||||
}
|
|
@ -43,13 +43,13 @@
|
|||
* @raise EINVAL if `state` has bad value
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int pthread_setcancelstate(int state, int *oldstate) {
|
||||
errno_t pthread_setcancelstate(int state, int *oldstate) {
|
||||
struct PosixThread *pt;
|
||||
if (__tls_enabled && (pt = (struct PosixThread *)__get_tls()->tib_pthread)) {
|
||||
switch (state) {
|
||||
case PTHREAD_CANCEL_ENABLE:
|
||||
case PTHREAD_CANCEL_DISABLE:
|
||||
case PTHREAD_CANCEL_MASKED:
|
||||
pt = (struct PosixThread *)__get_tls()->tib_pthread;
|
||||
if (oldstate) {
|
||||
if (pt->flags & PT_NOCANCEL) {
|
||||
*oldstate = PTHREAD_CANCEL_DISABLE;
|
||||
|
@ -69,4 +69,10 @@ int pthread_setcancelstate(int state, int *oldstate) {
|
|||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
} else {
|
||||
if (oldstate) {
|
||||
*oldstate = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/dprintf.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
|
@ -124,16 +125,13 @@ void vsyslog(int priority, const char *message, va_list ap) {
|
|||
int l, l2;
|
||||
int hlen; /* If LOG_CONS is specified, use to store the point in
|
||||
* the header message after the timestamp */
|
||||
|
||||
BLOCK_CANCELLATIONS;
|
||||
if (log_fd < 0) __openlog();
|
||||
|
||||
if (!(priority & LOG_FACMASK)) priority |= log_facility;
|
||||
|
||||
/* Build the time string */
|
||||
now = Time(NULL);
|
||||
gmtime_r(&now, &tm);
|
||||
strftime(timebuf, sizeof(timebuf), "%b %e %T", &tm);
|
||||
|
||||
pid = (log_opt & LOG_PID) ? getpid() : 0;
|
||||
/* This is a clever trick to optionally include "[<pid>]"
|
||||
* only if pid != 0. When pid==0, the while "[%.0d]" is skipped:
|
||||
|
@ -149,7 +147,6 @@ void vsyslog(int priority, const char *message, va_list ap) {
|
|||
l += snprintf(buf + l, sizeof(buf) - l, "%hs%s%.0d%s: ", log_ident,
|
||||
"[" + !pid, pid, "]" + !pid);
|
||||
errno = errno_save;
|
||||
|
||||
/* Append user message */
|
||||
l2 = vsnprintf(buf + l, sizeof(buf) - l, message, ap);
|
||||
if (l2 >= 0) {
|
||||
|
@ -166,37 +163,17 @@ void vsyslog(int priority, const char *message, va_list ap) {
|
|||
* - First try to send it to syslogd
|
||||
* - If fails and LOG_CONS is provided, writes to /dev/console
|
||||
*/
|
||||
|
||||
#if 0
|
||||
if (send(log_fd, buf, l, 0) < 0 && (!is_lost_conn(errno)
|
||||
|| connect(log_fd, (void *)&log_addr, sizeof(log_addr)) < 0
|
||||
|| send(log_fd, buf, l, 0) < 0)
|
||||
&& (log_opt & LOG_CONS)) {
|
||||
if (send(log_fd, buf, l, 0) < 0 &&
|
||||
(!is_lost_conn(errno) ||
|
||||
connect(log_fd, (void *)&log_addr, sizeof(log_addr)) < 0 ||
|
||||
send(log_fd, buf, l, 0) < 0) &&
|
||||
(log_opt & LOG_CONS)) {
|
||||
int fd = open("/dev/console", O_WRONLY | O_NOCTTY);
|
||||
if (fd >= 0) {
|
||||
dprintf(fd, "%.*s", l - hlen, buf + hlen);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
#else
|
||||
int rc = send(log_fd, buf, l, 0);
|
||||
if (rc < 0) {
|
||||
printf("ERR: send(1) failed: %s (errno=%d)\n", strerror(errno), errno);
|
||||
if (!is_lost_conn(errno)) {
|
||||
rc = connect(log_fd, (void *)&log_addr, sizeof(log_addr));
|
||||
if (rc < 0) {
|
||||
printf("ERR: connect(syslog) failed: %s (errno=%d)\n",
|
||||
strerror(errno), errno);
|
||||
} else {
|
||||
rc = send(log_fd, buf, l, 0);
|
||||
if (rc < 0) {
|
||||
printf("ERR: send(2) failed: %s (errno=%d)\n", strerror(errno),
|
||||
errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
uint16_t evtType;
|
||||
uint32_t evtID;
|
||||
|
@ -229,11 +206,11 @@ void vsyslog(int priority, const char *message, va_list ap) {
|
|||
NULL /* Arguments */);
|
||||
++log_id;
|
||||
}
|
||||
|
||||
if (log_opt & LOG_PERROR) {
|
||||
dprintf(2, "%.*s", l - hlen, buf + hlen);
|
||||
}
|
||||
}
|
||||
ALLOW_CANCELLATIONS;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -250,9 +227,7 @@ void vsyslog(int priority, const char *message, va_list ap) {
|
|||
*/
|
||||
int setlogmask(int maskpri) {
|
||||
int ret;
|
||||
if (log_facility == -1) {
|
||||
__initlog();
|
||||
}
|
||||
if (log_facility == -1) __initlog();
|
||||
ret = log_mask;
|
||||
if (maskpri) log_mask = LOG_PRI(maskpri);
|
||||
return ret;
|
||||
|
@ -287,19 +262,15 @@ int setlogmask(int maskpri) {
|
|||
*/
|
||||
void openlog(const char *ident, int opt, int facility) {
|
||||
size_t n;
|
||||
|
||||
if (log_facility == -1) {
|
||||
__initlog();
|
||||
}
|
||||
if (!ident) {
|
||||
ident = firstnonnull(program_invocation_short_name, "unknown");
|
||||
}
|
||||
BLOCK_CANCELLATIONS;
|
||||
if (log_facility == -1) __initlog();
|
||||
if (!ident) ident = firstnonnull(program_invocation_short_name, "unknown");
|
||||
tprecode8to16(log_ident, ARRAYLEN(log_ident), ident);
|
||||
log_opt = opt;
|
||||
log_facility = facility;
|
||||
log_id = 0;
|
||||
|
||||
if ((opt & LOG_NDELAY) && log_fd < 0) __openlog();
|
||||
ALLOW_CANCELLATIONS;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -339,9 +310,7 @@ void closelog(void) {
|
|||
if (log_facility == -1) {
|
||||
__initlog();
|
||||
}
|
||||
if (log_fd == -1) {
|
||||
return;
|
||||
}
|
||||
if (log_fd != -1) {
|
||||
if (IsWindows()) {
|
||||
DeregisterEventSource(log_fd);
|
||||
} else {
|
||||
|
@ -349,3 +318,4 @@ void closelog(void) {
|
|||
}
|
||||
log_fd = -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
.include "o/libc/sysv/macros.internal.inc"
|
||||
.scall sys_futex,0x0a6053fffffff0ca,globl,hidden
|
||||
.scall sys_futex,0x0a60531c6ffff0ca,globl,hidden
|
||||
|
|
2
libc/sysv/calls/sys_futex_cp.s
Normal file
2
libc/sysv/calls/sys_futex_cp.s
Normal file
|
@ -0,0 +1,2 @@
|
|||
.include "o/libc/sysv/macros.internal.inc"
|
||||
.scall sys_futex_cp,0x8a68539c6ffff8ca,globl,hidden
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)();
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
/**
|
||||
* Returns thread inherit schedule attribute.
|
||||
*/
|
||||
int pthread_attr_getinheritsched(const pthread_attr_t *attr,
|
||||
errno_t pthread_attr_getinheritsched(const pthread_attr_t *attr,
|
||||
int *inheritsched) {
|
||||
*inheritsched = attr->__inheritsched;
|
||||
return 0;
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
/**
|
||||
* Gets thread scheduler parameter attribute.
|
||||
*/
|
||||
int pthread_attr_getschedparam(const pthread_attr_t *attr,
|
||||
errno_t pthread_attr_getschedparam(const pthread_attr_t *attr,
|
||||
struct sched_param *param) {
|
||||
*param = (struct sched_param){attr->__schedparam};
|
||||
return 0;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
* @see sched_get_priority_max()
|
||||
* @see sched_setparam()
|
||||
*/
|
||||
int pthread_attr_setschedparam(pthread_attr_t *attr,
|
||||
errno_t pthread_attr_setschedparam(pthread_attr_t *attr,
|
||||
const struct sched_param *param) {
|
||||
if (!param) return EINVAL;
|
||||
attr->__schedparam = param->sched_priority;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
* - `PTHREAD_PROCESS_SHARED` (unsupported)
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr,
|
||||
errno_t pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr,
|
||||
int *pshared) {
|
||||
*pshared = *attr;
|
||||
return 0;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -32,36 +32,29 @@
|
|||
* @returnserrno
|
||||
* @threadsafe
|
||||
*/
|
||||
int pthread_detach(pthread_t thread) {
|
||||
errno_t pthread_detach(pthread_t thread) {
|
||||
struct PosixThread *pt;
|
||||
enum PosixThreadStatus status;
|
||||
if (!(pt = (struct PosixThread *)thread)) {
|
||||
return EINVAL;
|
||||
}
|
||||
enum PosixThreadStatus status, transition;
|
||||
if (!(pt = (struct PosixThread *)thread)) return EINVAL;
|
||||
for (;;) {
|
||||
status = atomic_load_explicit(&pt->status, memory_order_acquire);
|
||||
if (status == kPosixThreadDetached || status == kPosixThreadZombie) {
|
||||
// these two states indicate the thread was already detached, in
|
||||
// which case it's already listed under _pthread_zombies.
|
||||
return EINVAL;
|
||||
} else if (status == kPosixThreadTerminated) {
|
||||
// thread was joinable and finished running. since pthread_join
|
||||
// won't be called, it's safe to free the thread resources now.
|
||||
// POSIX says this could be reported as ESRCH but then our test
|
||||
// code would be less elegant in order for it to avoid flaking.
|
||||
_pthread_wait(pt);
|
||||
_pthread_free(pt);
|
||||
break;
|
||||
} else if (status == kPosixThreadJoinable) {
|
||||
if (atomic_compare_exchange_weak_explicit(
|
||||
&pt->status, &status, kPosixThreadDetached, memory_order_release,
|
||||
transition = kPosixThreadDetached;
|
||||
} else if (status == kPosixThreadTerminated) {
|
||||
transition = kPosixThreadZombie;
|
||||
} else {
|
||||
notpossible;
|
||||
}
|
||||
if (atomic_compare_exchange_weak_explicit(&pt->status, &status, transition,
|
||||
memory_order_release,
|
||||
memory_order_relaxed)) {
|
||||
_pthread_zombies_add(pt);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
notpossible;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
/**
|
||||
* Gets most recently set scheduling of thread.
|
||||
*/
|
||||
int pthread_getschedparam(pthread_t thread, int *policy,
|
||||
errno_t pthread_getschedparam(pthread_t thread, int *policy,
|
||||
struct sched_param *param) {
|
||||
struct PosixThread *pt = (struct PosixThread *)thread;
|
||||
*policy = pt->attr.__schedpolicy;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
* - `PTHREAD_PROCESS_SHARED`
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr,
|
||||
errno_t pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr,
|
||||
int *pshared) {
|
||||
*pshared = attr->_pshared;
|
||||
return 0;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* @param attr may be null
|
||||
* @return 0 on success, or error number on failure
|
||||
*/
|
||||
int pthread_rwlock_init(pthread_rwlock_t *rwlock,
|
||||
errno_t pthread_rwlock_init(pthread_rwlock_t *rwlock,
|
||||
const pthread_rwlockattr_t *attr) {
|
||||
*rwlock = (pthread_rwlock_t){0};
|
||||
return 0;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
* - `PTHREAD_PROCESS_SHARED` (unsupported)
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr,
|
||||
errno_t pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr,
|
||||
int *pshared) {
|
||||
*pshared = *attr;
|
||||
return 0;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
* @see sched_get_priority_max()
|
||||
* @see sched_setscheduler()
|
||||
*/
|
||||
int pthread_setschedparam(pthread_t thread, int policy,
|
||||
errno_t pthread_setschedparam(pthread_t thread, int policy,
|
||||
const struct sched_param *param) {
|
||||
struct PosixThread *pt = (struct PosixThread *)thread;
|
||||
if (!param) return EINVAL;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
4
third_party/musl/grp.c
vendored
4
third_party/musl/grp.c
vendored
|
@ -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');
|
||||
|
|
4
third_party/musl/nftw.c
vendored
4
third_party/musl/nftw.c
vendored
|
@ -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
|
||||
|
|
4
third_party/musl/pwd.c
vendored
4
third_party/musl/pwd.c
vendored
|
@ -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)
|
||||
{
|
||||
|
|
1
third_party/nsync/README.cosmo
vendored
1
third_party/nsync/README.cosmo
vendored
|
@ -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_
|
||||
|
|
118
third_party/nsync/futex.c
vendored
118
third_party/nsync/futex.c
vendored
|
@ -23,17 +23,20 @@
|
|||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/futex.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/freebsd.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
@ -45,7 +48,9 @@
|
|||
|
||||
#define FUTEX_WAIT_BITS_ FUTEX_BITSET_MATCH_ANY
|
||||
|
||||
int _futex (atomic_int *, int, int, const struct timespec *, int *, int);
|
||||
errno_t _futex (atomic_int *, int, int, const struct timespec *, int *, int);
|
||||
errno_t _futex_wake (atomic_int *, int, int) asm ("_futex");
|
||||
int sys_futex_cp (atomic_int *, int, int, const struct timespec *, int *, int);
|
||||
|
||||
static int FUTEX_WAIT_;
|
||||
static int FUTEX_PRIVATE_FLAG_;
|
||||
|
@ -59,6 +64,7 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) {
|
|||
|
||||
if (IsWindows ()) {
|
||||
FUTEX_IS_SUPPORTED = true;
|
||||
FUTEX_TIMEOUT_IS_ABSOLUTE = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -70,8 +76,6 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) {
|
|||
}
|
||||
|
||||
if (!(FUTEX_IS_SUPPORTED = IsLinux () || IsOpenbsd ())) {
|
||||
// we're using sched_yield() so let's
|
||||
// avoid needless clock_gettime calls
|
||||
FUTEX_TIMEOUT_IS_ABSOLUTE = true;
|
||||
return;
|
||||
}
|
||||
|
@ -101,7 +105,7 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) {
|
|||
FUTEX_TIMEOUT_IS_ABSOLUTE = true;
|
||||
} else if (IsOpenbsd () ||
|
||||
(!IsTiny () && IsLinux () &&
|
||||
!_futex (&x, FUTEX_WAKE_PRIVATE, 1, 0, 0, 0))) {
|
||||
!_futex_wake (&x, FUTEX_WAKE_PRIVATE, 1))) {
|
||||
FUTEX_WAIT_ = FUTEX_WAIT;
|
||||
FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
|
||||
} else {
|
||||
|
@ -110,13 +114,10 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) {
|
|||
}
|
||||
|
||||
static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *timeout) {
|
||||
int rc;
|
||||
int64_t nanos, maxnanos;
|
||||
struct timespec ts, deadline;
|
||||
|
||||
if (atomic_load_explicit (w, memory_order_relaxed) != expect) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
ts = nsync_time_now ();
|
||||
if (!timeout) {
|
||||
deadline = nsync_time_no_deadline;
|
||||
|
@ -129,15 +130,15 @@ static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *ti
|
|||
nanos = 100;
|
||||
maxnanos = __SIG_POLLING_INTERVAL_MS * 1000L * 1000;
|
||||
while (nsync_time_cmp (deadline, ts) > 0) {
|
||||
if (atomic_load_explicit (w, memory_order_relaxed) != expect) {
|
||||
return 0;
|
||||
}
|
||||
ts = nsync_time_add (ts, _timespec_fromnanos (nanos));
|
||||
if (nsync_time_cmp (ts, deadline) > 0) {
|
||||
ts = deadline;
|
||||
}
|
||||
if (nsync_time_sleep_until (ts)) {
|
||||
return -EINTR;
|
||||
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
|
||||
return 0;
|
||||
}
|
||||
if ((rc = nsync_time_sleep_until (ts))) {
|
||||
return -rc;
|
||||
}
|
||||
if (nanos < maxnanos) {
|
||||
nanos <<= 1;
|
||||
|
@ -150,10 +151,46 @@ static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *ti
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, struct timespec *timeout) {
|
||||
static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare, struct timespec *timeout) {
|
||||
int rc;
|
||||
uint32_t ms;
|
||||
struct timespec deadline, interval, remain, wait, now;
|
||||
|
||||
if (timeout) {
|
||||
deadline = *timeout;
|
||||
} else {
|
||||
deadline = nsync_time_no_deadline;
|
||||
}
|
||||
|
||||
while (!(rc = _check_interrupts (false, 0))) {
|
||||
now = nsync_time_now ();
|
||||
if (nsync_time_cmp (now, deadline) > 0) {
|
||||
rc = etimedout();
|
||||
break;
|
||||
}
|
||||
remain = nsync_time_sub (deadline, now);
|
||||
interval = _timespec_frommillis (__SIG_POLLING_INTERVAL_MS);
|
||||
wait = nsync_time_cmp (remain, interval) > 0 ? interval : remain;
|
||||
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
|
||||
break;
|
||||
}
|
||||
if (WaitOnAddress (w, &expect, sizeof(int), _timespec_tomillis (wait))) {
|
||||
break;
|
||||
} else {
|
||||
ASSERT (GetLastError () == ETIMEDOUT);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, struct timespec *timeout) {
|
||||
int e, rc, op, fop;
|
||||
|
||||
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
op = FUTEX_WAIT_;
|
||||
if (pshare == PTHREAD_PROCESS_PRIVATE) {
|
||||
op |= FUTEX_PRIVATE_FLAG_;
|
||||
|
@ -165,36 +202,34 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, struct timespec *
|
|||
DescribeTimespec (0, timeout));
|
||||
|
||||
if (FUTEX_IS_SUPPORTED) {
|
||||
e = errno;
|
||||
if (IsWindows ()) {
|
||||
// Windows 8 futexes don't support multiple processes :(
|
||||
if (pshare) {
|
||||
goto Polyfill;
|
||||
if (pshare) goto Polyfill;
|
||||
rc = nsync_futex_wait_win32_ (w, expect, pshare, timeout);
|
||||
} else if (IsFreebsd ()) {
|
||||
rc = sys_umtx_timedwait_uint (w, expect, pshare, timeout);
|
||||
} else {
|
||||
rc = sys_futex_cp (w, op, expect, timeout, 0, FUTEX_WAIT_BITS_);
|
||||
if (IsOpenbsd() && rc > 0) {
|
||||
// OpenBSD futex() returns errors as
|
||||
// positive numbers without setting the
|
||||
// carry flag. It's an irregular miracle
|
||||
// because OpenBSD returns ECANCELED if
|
||||
// futex() is interrupted w/ SA_RESTART
|
||||
// so we're able to tell it apart from
|
||||
// PT_MASKED which causes the wrapper
|
||||
// to put ECANCELED into errno.
|
||||
if (rc == ECANCELED) {
|
||||
rc = EINTR;
|
||||
}
|
||||
e = errno;
|
||||
if (_check_interrupts (false, 0)) {
|
||||
errno = rc;
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
if (rc == -1) {
|
||||
rc = -errno;
|
||||
errno = e;
|
||||
} else {
|
||||
if (timeout) {
|
||||
ms = _timespec_tomillis (*timeout);
|
||||
} else {
|
||||
ms = -1;
|
||||
}
|
||||
if (WaitOnAddress (w, &expect, sizeof(int), ms)) {
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = -GetLastError ();
|
||||
}
|
||||
}
|
||||
} else if (IsFreebsd ()) {
|
||||
rc = sys_umtx_timedwait_uint (
|
||||
w, expect, pshare, timeout);
|
||||
} else {
|
||||
rc = _futex (w, op, expect, timeout, 0,
|
||||
FUTEX_WAIT_BITS_);
|
||||
if (IsOpenbsd() && rc > 0) {
|
||||
rc = -rc;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Polyfill:
|
||||
|
@ -214,7 +249,6 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, struct timespec *
|
|||
|
||||
int nsync_futex_wake_ (atomic_int *w, int count, char pshare) {
|
||||
int e, rc, op, fop;
|
||||
int wake (atomic_int *, int, int) asm ("_futex");
|
||||
|
||||
ASSERT (count == 1 || count == INT_MAX);
|
||||
|
||||
|
@ -240,9 +274,9 @@ int nsync_futex_wake_ (atomic_int *w, int count, char pshare) {
|
|||
} else {
|
||||
fop = UMTX_OP_WAKE_PRIVATE;
|
||||
}
|
||||
rc = sys_umtx_op (w, fop, count, 0, 0);
|
||||
rc = _futex_wake (w, fop, count);
|
||||
} else {
|
||||
rc = wake (w, op, count);
|
||||
rc = _futex_wake (w, op, count);
|
||||
}
|
||||
} else {
|
||||
Polyfill:
|
||||
|
|
3
third_party/nsync/mem/nsync_cv.c
vendored
3
third_party/nsync/mem/nsync_cv.c
vendored
|
@ -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);
|
||||
}
|
||||
|
|
3
third_party/nsync/mem/nsync_mu_wait.c
vendored
3
third_party/nsync/mem/nsync_mu_wait.c
vendored
|
@ -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);
|
||||
}
|
||||
|
|
3
third_party/nsync/mem/nsync_wait.c
vendored
3
third_party/nsync/mem/nsync_wait.c
vendored
|
@ -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);
|
||||
}
|
||||
|
|
5
third_party/nsync/mu.c
vendored
5
third_party/nsync/mu.c
vendored
|
@ -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
|
||||
|
|
21
third_party/nsync/mu_semaphore.c
vendored
21
third_party/nsync/mu_semaphore.c
vendored
|
@ -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);
|
||||
|
|
4
third_party/nsync/mu_semaphore.h
vendored
4
third_party/nsync/mu_semaphore.h
vendored
|
@ -12,12 +12,12 @@ typedef struct nsync_semaphore_s_ {
|
|||
void nsync_mu_semaphore_init(nsync_semaphore *s);
|
||||
|
||||
/* Wait until the count of *s exceeds 0, and decrement it. */
|
||||
void nsync_mu_semaphore_p(nsync_semaphore *s);
|
||||
errno_t nsync_mu_semaphore_p(nsync_semaphore *s);
|
||||
|
||||
/* Wait until one of: the count of *s is non-zero, in which case
|
||||
decrement *s and return 0; or abs_deadline expires, in which case
|
||||
return ETIMEDOUT. */
|
||||
int nsync_mu_semaphore_p_with_deadline(nsync_semaphore *s,
|
||||
errno_t nsync_mu_semaphore_p_with_deadline(nsync_semaphore *s,
|
||||
nsync_time abs_deadline);
|
||||
|
||||
/* Ensure that the count of *s is at least 1. */
|
||||
|
|
1
third_party/sqlite3/README.cosmo
vendored
1
third_party/sqlite3/README.cosmo
vendored
|
@ -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
|
||||
|
|
2
third_party/sqlite3/os_unix.c
vendored
2
third_party/sqlite3/os_unix.c
vendored
|
@ -84,7 +84,7 @@
|
|||
#endif
|
||||
|
||||
/* Use pread() and pwrite() if they are available */
|
||||
#if defined(__APPLE__) || defined(__COSMOPOLITAN__)
|
||||
#if defined(__APPLE__) || defined(__COSMOPOLITAN__) /* [jart] */
|
||||
# define HAVE_PREAD 1
|
||||
# define HAVE_PWRITE 1
|
||||
#endif
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue