mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-06 19:28:29 +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,
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
_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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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)) {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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,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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,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;
|
||||
|
|
|
@ -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,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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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,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};
|
||||
|
|
|
@ -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,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;
|
||||
}
|
||||
|
|
|
@ -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,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;
|
||||
}
|
||||
|
|
|
@ -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,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;
|
||||
}
|
||||
|
|
|
@ -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,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;
|
||||
|
|
|
@ -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) */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue