mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +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))) {
|
if (!(rc = clock_nanosleep(CLOCK_REALTIME, 0, &delay, &remain))) {
|
||||||
return (struct timespec){0};
|
return (struct timespec){0};
|
||||||
} else {
|
} else {
|
||||||
_npassert(rc == EINTR);
|
_npassert(rc == EINTR || rc == ECANCELED);
|
||||||
return remain;
|
return remain;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,9 @@
|
||||||
*
|
*
|
||||||
* @return 0 on success, or EINTR if interrupted
|
* @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;
|
int rc;
|
||||||
rc = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &abs_deadline, 0);
|
rc = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &abs_deadline, 0);
|
||||||
_npassert(!rc || rc == EINTR);
|
_npassert(!rc || rc == EINTR || rc == ECANCELED);
|
||||||
return rc;
|
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_
|
#ifndef COSMOPOLITAN_LIBC_CALLS_BLOCKSIGS_H_
|
||||||
#define COSMOPOLITAN_LIBC_CALLS_BLOCKSIGS_INTERNAL_H_
|
#define COSMOPOLITAN_LIBC_CALLS_BLOCKSIGS_H_
|
||||||
#include "libc/calls/struct/sigset.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
@ -16,4 +16,4 @@ COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#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) {
|
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||||
rc = _weaken(__zipos_fcntl)(fd, cmd, arg);
|
rc = _weaken(__zipos_fcntl)(fd, cmd, arg);
|
||||||
} else if (!IsWindows()) {
|
} else if (!IsWindows()) {
|
||||||
if (cmd == F_SETLKW) {
|
if (cmd == F_SETLKW || cmd == F_OFD_SETLKW) {
|
||||||
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl_cp);
|
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl_cp);
|
||||||
} else {
|
} else {
|
||||||
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl);
|
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
|
#include "libc/calls/blockcancel.internal.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/ioctl.h"
|
#include "libc/calls/ioctl.h"
|
||||||
#include "libc/calls/struct/metatermios.internal.h"
|
#include "libc/calls/struct/metatermios.internal.h"
|
||||||
|
@ -43,31 +44,12 @@ struct IoctlPtmGet {
|
||||||
char sname[16];
|
char sname[16];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
static int openpty_impl(int *mfd, int *sfd, char *name,
|
||||||
* Opens new pseudo teletypewriter.
|
const struct termios *tio, //
|
||||||
*
|
const struct winsize *wsz) {
|
||||||
* @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 m, s, p;
|
int m, s, p;
|
||||||
union metatermios mt;
|
union metatermios mt;
|
||||||
struct IoctlPtmGet t;
|
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)));
|
RETURN_ON_ERROR((m = posix_openpt(O_RDWR | O_NOCTTY)));
|
||||||
if (!IsOpenbsd()) {
|
if (!IsOpenbsd()) {
|
||||||
RETURN_ON_ERROR(grantpt(m));
|
RETURN_ON_ERROR(grantpt(m));
|
||||||
|
@ -90,3 +72,33 @@ OnError:
|
||||||
if (m != -1) sys_close(m);
|
if (m != -1) sys_close(m);
|
||||||
return -1;
|
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/log/internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/runtime/symbols.internal.h"
|
#include "libc/runtime/symbols.internal.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
relegated void __assert_fail(const char *expr, const char *file, int line) {
|
relegated void __assert_fail(const char *expr, const char *file, int line) {
|
||||||
int me, owner;
|
int me, owner;
|
||||||
|
@ -36,6 +37,7 @@ relegated void __assert_fail(const char *expr, const char *file, int line) {
|
||||||
ftrace_enabled(-1);
|
ftrace_enabled(-1);
|
||||||
owner = 0;
|
owner = 0;
|
||||||
me = sys_gettid();
|
me = sys_gettid();
|
||||||
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
|
||||||
kprintf("%s:%d: assert(%s) failed (tid %d)\n", file, line, expr, me);
|
kprintf("%s:%d: assert(%s) failed (tid %d)\n", file, line, expr, me);
|
||||||
if (__vforked ||
|
if (__vforked ||
|
||||||
atomic_compare_exchange_strong_explicit(
|
atomic_compare_exchange_strong_explicit(
|
||||||
|
|
|
@ -22,9 +22,11 @@
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
|
|
||||||
void _pthread_cleanup_pop(struct _pthread_cleanup_buffer *cb, int execute) {
|
void _pthread_cleanup_pop(struct _pthread_cleanup_buffer *cb, int execute) {
|
||||||
struct PosixThread *pt = (struct PosixThread *)__get_tls()->tib_pthread;
|
struct PosixThread *pt;
|
||||||
_unassert(cb == pt->cleanup);
|
if (__tls_enabled && (pt = (struct PosixThread *)__get_tls()->tib_pthread)) {
|
||||||
pt->cleanup = cb->__prev;
|
_unassert(cb == pt->cleanup);
|
||||||
|
pt->cleanup = cb->__prev;
|
||||||
|
}
|
||||||
if (execute) {
|
if (execute) {
|
||||||
cb->__routine(cb->__arg);
|
cb->__routine(cb->__arg);
|
||||||
}
|
}
|
|
@ -18,12 +18,15 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
#include "libc/thread/tls.h"
|
||||||
|
|
||||||
void _pthread_cleanup_push(struct _pthread_cleanup_buffer *cb,
|
void _pthread_cleanup_push(struct _pthread_cleanup_buffer *cb,
|
||||||
void (*routine)(void *), void *arg) {
|
void (*routine)(void *), void *arg) {
|
||||||
struct PosixThread *pt = (struct PosixThread *)__get_tls()->tib_pthread;
|
struct PosixThread *pt;
|
||||||
cb->__routine = routine;
|
cb->__routine = routine;
|
||||||
cb->__arg = arg;
|
cb->__arg = arg;
|
||||||
cb->__prev = pt->cleanup;
|
if (__tls_enabled && (pt = (struct PosixThread *)__get_tls()->tib_pthread)) {
|
||||||
pt->cleanup = cb;
|
cb->__prev = pt->cleanup;
|
||||||
|
pt->cleanup = cb;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -43,30 +43,36 @@
|
||||||
* @raise EINVAL if `state` has bad value
|
* @raise EINVAL if `state` has bad value
|
||||||
* @asyncsignalsafe
|
* @asyncsignalsafe
|
||||||
*/
|
*/
|
||||||
int pthread_setcancelstate(int state, int *oldstate) {
|
errno_t pthread_setcancelstate(int state, int *oldstate) {
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
switch (state) {
|
if (__tls_enabled && (pt = (struct PosixThread *)__get_tls()->tib_pthread)) {
|
||||||
case PTHREAD_CANCEL_ENABLE:
|
switch (state) {
|
||||||
case PTHREAD_CANCEL_DISABLE:
|
case PTHREAD_CANCEL_ENABLE:
|
||||||
case PTHREAD_CANCEL_MASKED:
|
case PTHREAD_CANCEL_DISABLE:
|
||||||
pt = (struct PosixThread *)__get_tls()->tib_pthread;
|
case PTHREAD_CANCEL_MASKED:
|
||||||
if (oldstate) {
|
if (oldstate) {
|
||||||
if (pt->flags & PT_NOCANCEL) {
|
if (pt->flags & PT_NOCANCEL) {
|
||||||
*oldstate = PTHREAD_CANCEL_DISABLE;
|
*oldstate = PTHREAD_CANCEL_DISABLE;
|
||||||
} else if (pt->flags & PT_MASKED) {
|
} else if (pt->flags & PT_MASKED) {
|
||||||
*oldstate = PTHREAD_CANCEL_MASKED;
|
*oldstate = PTHREAD_CANCEL_MASKED;
|
||||||
} else {
|
} else {
|
||||||
*oldstate = PTHREAD_CANCEL_ENABLE;
|
*oldstate = PTHREAD_CANCEL_ENABLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
pt->flags &= ~(PT_NOCANCEL | PT_MASKED);
|
||||||
pt->flags &= ~(PT_NOCANCEL | PT_MASKED);
|
if (state == PTHREAD_CANCEL_MASKED) {
|
||||||
if (state == PTHREAD_CANCEL_MASKED) {
|
pt->flags |= PT_MASKED;
|
||||||
pt->flags |= PT_MASKED;
|
} else if (state == PTHREAD_CANCEL_DISABLE) {
|
||||||
} else if (state == PTHREAD_CANCEL_DISABLE) {
|
pt->flags |= PT_NOCANCEL;
|
||||||
pt->flags |= PT_NOCANCEL;
|
}
|
||||||
}
|
return 0;
|
||||||
return 0;
|
default:
|
||||||
default:
|
return EINVAL;
|
||||||
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/sysv/consts/clock.h"
|
||||||
#include "libc/thread/freebsd.internal.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,
|
int sys_umtx_timedwait_uint(atomic_int *p, int expect, bool pshare,
|
||||||
const struct timespec *abstime) {
|
const struct timespec *abstime) {
|
||||||
int op;
|
int op;
|
||||||
|
@ -40,5 +43,5 @@ int sys_umtx_timedwait_uint(atomic_int *p, int expect, bool pshare,
|
||||||
} else {
|
} else {
|
||||||
op = UMTX_OP_WAIT_UINT_PRIVATE;
|
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/log/log.h"
|
||||||
#include "libc/runtime/internal.h"
|
#include "libc/runtime/internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
#if SupportsMetal()
|
#if SupportsMetal()
|
||||||
STATIC_YOINK("_idt");
|
STATIC_YOINK("_idt");
|
||||||
|
@ -45,6 +46,7 @@ relegated wontreturn void __die(void) {
|
||||||
static atomic_int once;
|
static atomic_int once;
|
||||||
owner = 0;
|
owner = 0;
|
||||||
me = sys_gettid();
|
me = sys_gettid();
|
||||||
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
|
||||||
if (__vforked ||
|
if (__vforked ||
|
||||||
atomic_compare_exchange_strong_explicit(
|
atomic_compare_exchange_strong_explicit(
|
||||||
&once, &owner, me, memory_order_relaxed, memory_order_relaxed)) {
|
&once, &owner, me, memory_order_relaxed, memory_order_relaxed)) {
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
#include "third_party/libcxx/math.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);
|
strace_enabled(-1);
|
||||||
owner = 0;
|
owner = 0;
|
||||||
me = sys_gettid();
|
me = sys_gettid();
|
||||||
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
|
||||||
if (atomic_compare_exchange_strong_explicit(
|
if (atomic_compare_exchange_strong_explicit(
|
||||||
&once, &owner, me, memory_order_relaxed, memory_order_relaxed)) {
|
&once, &owner, me, memory_order_relaxed, memory_order_relaxed)) {
|
||||||
if (!__vforked) {
|
if (!__vforked) {
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "libc/intrin/safemacros.internal.h"
|
#include "libc/intrin/safemacros.internal.h"
|
||||||
#include "libc/log/internal.h"
|
#include "libc/log/internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints initial part of fatal message.
|
* Prints initial part of fatal message.
|
||||||
|
@ -27,6 +28,7 @@
|
||||||
* @note this is support code for __check_fail(), __assert_fail(), etc.
|
* @note this is support code for __check_fail(), __assert_fail(), etc.
|
||||||
*/
|
*/
|
||||||
relegated void __start_fatal(const char *file, int line) {
|
relegated void __start_fatal(const char *file, int line) {
|
||||||
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
|
||||||
__restore_tty();
|
__restore_tty();
|
||||||
kprintf("%r%serror%s:%s:%d:%s%s: ", !__nocolor ? "\e[J\e[30;101m" : "",
|
kprintf("%r%serror%s:%s:%d:%s%s: ", !__nocolor ? "\e[J\e[30;101m" : "",
|
||||||
!__nocolor ? "\e[94;49m" : "", file, line,
|
!__nocolor ? "\e[94;49m" : "", file, line,
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/blockcancel.internal.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/dprintf.h"
|
#include "libc/calls/dprintf.h"
|
||||||
#include "libc/calls/struct/timespec.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 l, l2;
|
||||||
int hlen; /* If LOG_CONS is specified, use to store the point in
|
int hlen; /* If LOG_CONS is specified, use to store the point in
|
||||||
* the header message after the timestamp */
|
* the header message after the timestamp */
|
||||||
|
BLOCK_CANCELLATIONS;
|
||||||
if (log_fd < 0) __openlog();
|
if (log_fd < 0) __openlog();
|
||||||
|
|
||||||
if (!(priority & LOG_FACMASK)) priority |= log_facility;
|
if (!(priority & LOG_FACMASK)) priority |= log_facility;
|
||||||
|
|
||||||
/* Build the time string */
|
/* Build the time string */
|
||||||
now = Time(NULL);
|
now = Time(NULL);
|
||||||
gmtime_r(&now, &tm);
|
gmtime_r(&now, &tm);
|
||||||
strftime(timebuf, sizeof(timebuf), "%b %e %T", &tm);
|
strftime(timebuf, sizeof(timebuf), "%b %e %T", &tm);
|
||||||
|
|
||||||
pid = (log_opt & LOG_PID) ? getpid() : 0;
|
pid = (log_opt & LOG_PID) ? getpid() : 0;
|
||||||
/* This is a clever trick to optionally include "[<pid>]"
|
/* This is a clever trick to optionally include "[<pid>]"
|
||||||
* only if pid != 0. When pid==0, the while "[%.0d]" is skipped:
|
* 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,
|
l += snprintf(buf + l, sizeof(buf) - l, "%hs%s%.0d%s: ", log_ident,
|
||||||
"[" + !pid, pid, "]" + !pid);
|
"[" + !pid, pid, "]" + !pid);
|
||||||
errno = errno_save;
|
errno = errno_save;
|
||||||
|
|
||||||
/* Append user message */
|
/* Append user message */
|
||||||
l2 = vsnprintf(buf + l, sizeof(buf) - l, message, ap);
|
l2 = vsnprintf(buf + l, sizeof(buf) - l, message, ap);
|
||||||
if (l2 >= 0) {
|
if (l2 >= 0) {
|
||||||
|
@ -166,37 +163,17 @@ void vsyslog(int priority, const char *message, va_list ap) {
|
||||||
* - First try to send it to syslogd
|
* - First try to send it to syslogd
|
||||||
* - If fails and LOG_CONS is provided, writes to /dev/console
|
* - If fails and LOG_CONS is provided, writes to /dev/console
|
||||||
*/
|
*/
|
||||||
|
if (send(log_fd, buf, l, 0) < 0 &&
|
||||||
#if 0
|
(!is_lost_conn(errno) ||
|
||||||
if (send(log_fd, buf, l, 0) < 0 && (!is_lost_conn(errno)
|
connect(log_fd, (void *)&log_addr, sizeof(log_addr)) < 0 ||
|
||||||
|| connect(log_fd, (void *)&log_addr, sizeof(log_addr)) < 0
|
send(log_fd, buf, l, 0) < 0) &&
|
||||||
|| send(log_fd, buf, l, 0) < 0)
|
(log_opt & LOG_CONS)) {
|
||||||
&& (log_opt & LOG_CONS)) {
|
int fd = open("/dev/console", O_WRONLY | O_NOCTTY);
|
||||||
int fd = open("/dev/console", O_WRONLY|O_NOCTTY);
|
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
dprintf(fd, "%.*s", l-hlen, buf+hlen);
|
dprintf(fd, "%.*s", l - hlen, buf + hlen);
|
||||||
close(fd);
|
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 {
|
} else {
|
||||||
uint16_t evtType;
|
uint16_t evtType;
|
||||||
uint32_t evtID;
|
uint32_t evtID;
|
||||||
|
@ -229,11 +206,11 @@ void vsyslog(int priority, const char *message, va_list ap) {
|
||||||
NULL /* Arguments */);
|
NULL /* Arguments */);
|
||||||
++log_id;
|
++log_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log_opt & LOG_PERROR) {
|
if (log_opt & LOG_PERROR) {
|
||||||
dprintf(2, "%.*s", l - hlen, buf + hlen);
|
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 setlogmask(int maskpri) {
|
||||||
int ret;
|
int ret;
|
||||||
if (log_facility == -1) {
|
if (log_facility == -1) __initlog();
|
||||||
__initlog();
|
|
||||||
}
|
|
||||||
ret = log_mask;
|
ret = log_mask;
|
||||||
if (maskpri) log_mask = LOG_PRI(maskpri);
|
if (maskpri) log_mask = LOG_PRI(maskpri);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -287,19 +262,15 @@ int setlogmask(int maskpri) {
|
||||||
*/
|
*/
|
||||||
void openlog(const char *ident, int opt, int facility) {
|
void openlog(const char *ident, int opt, int facility) {
|
||||||
size_t n;
|
size_t n;
|
||||||
|
BLOCK_CANCELLATIONS;
|
||||||
if (log_facility == -1) {
|
if (log_facility == -1) __initlog();
|
||||||
__initlog();
|
if (!ident) ident = firstnonnull(program_invocation_short_name, "unknown");
|
||||||
}
|
|
||||||
if (!ident) {
|
|
||||||
ident = firstnonnull(program_invocation_short_name, "unknown");
|
|
||||||
}
|
|
||||||
tprecode8to16(log_ident, ARRAYLEN(log_ident), ident);
|
tprecode8to16(log_ident, ARRAYLEN(log_ident), ident);
|
||||||
log_opt = opt;
|
log_opt = opt;
|
||||||
log_facility = facility;
|
log_facility = facility;
|
||||||
log_id = 0;
|
log_id = 0;
|
||||||
|
|
||||||
if ((opt & LOG_NDELAY) && log_fd < 0) __openlog();
|
if ((opt & LOG_NDELAY) && log_fd < 0) __openlog();
|
||||||
|
ALLOW_CANCELLATIONS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -339,13 +310,12 @@ void closelog(void) {
|
||||||
if (log_facility == -1) {
|
if (log_facility == -1) {
|
||||||
__initlog();
|
__initlog();
|
||||||
}
|
}
|
||||||
if (log_fd == -1) {
|
if (log_fd != -1) {
|
||||||
return;
|
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 │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/blockcancel.internal.h"
|
||||||
#include "libc/stdio/rand.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.
|
* 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()
|
* @see getrandom()
|
||||||
*/
|
*/
|
||||||
int getentropy(void *buf, size_t size) {
|
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/o.h"
|
||||||
#include "libc/sysv/consts/sig.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
static bool have_getrandom;
|
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.
|
* Returns cryptographic random data.
|
||||||
*
|
*
|
||||||
|
@ -76,37 +111,17 @@ static bool have_getrandom;
|
||||||
* @vforksafe
|
* @vforksafe
|
||||||
*/
|
*/
|
||||||
ssize_t getrandom(void *p, size_t n, unsigned f) {
|
ssize_t getrandom(void *p, size_t n, unsigned f) {
|
||||||
char cf;
|
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
uint64_t x;
|
|
||||||
int fd, cmd[2];
|
|
||||||
size_t i, j, m;
|
|
||||||
const char *via;
|
const char *via;
|
||||||
sigset_t neu, old;
|
|
||||||
if (n > 256) n = 256;
|
|
||||||
if ((f & ~(GRND_RANDOM | GRND_NONBLOCK))) {
|
if ((f & ~(GRND_RANDOM | GRND_NONBLOCK))) {
|
||||||
rc = einval();
|
rc = einval();
|
||||||
via = "n/a";
|
via = "n/a";
|
||||||
} else if (IsWindows()) {
|
} else if (IsWindows()) {
|
||||||
via = "RtlGenRandom";
|
via = "RtlGenRandom";
|
||||||
if (RtlGenRandom(p, n)) {
|
rc = RtlGenRandom(p, n) ? n : __winerr();
|
||||||
rc = n;
|
|
||||||
} else {
|
|
||||||
rc = __winerr();
|
|
||||||
}
|
|
||||||
} else if (IsFreebsd() || IsNetbsd()) {
|
} else if (IsFreebsd() || IsNetbsd()) {
|
||||||
via = "KERN_ARND";
|
via = "KERN_ARND";
|
||||||
if (IsFreebsd()) {
|
rc = GetKernArnd(p, n);
|
||||||
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;
|
|
||||||
}
|
|
||||||
} else if (have_getrandom) {
|
} else if (have_getrandom) {
|
||||||
via = "getrandom";
|
via = "getrandom";
|
||||||
if ((rc = sys_getrandom(p, n, f & (GRND_RANDOM | GRND_NONBLOCK))) != -1) {
|
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;
|
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 {
|
} else {
|
||||||
rc = enosys();
|
via = "/dev/urandom";
|
||||||
|
rc = GetDevRandom(p, n);
|
||||||
}
|
}
|
||||||
STRACE("getrandom(%p, %'zu, %#x) via %s → %'ld% m", p, n, f, via, rc);
|
STRACE("getrandom(%p, %'zu, %#x) via %s → %'ld% m", p, n, f, via, rc);
|
||||||
return rc;
|
return rc;
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
.include "o/libc/sysv/macros.internal.inc"
|
.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_killpg 0xfff 0xfff 0x0092 0xfff 0xfff 0xfff
|
||||||
syscon nr __NR_clone 0x0038 0xfff 0xfff 0xfff 0x11f 0xfff
|
syscon nr __NR_clone 0x0038 0xfff 0xfff 0xfff 0x11f 0xfff
|
||||||
syscon nr __NR_tkill 0x00c8 0xfff 0xfff 0xfff 0xfff 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_set_robust_list 0x0111 0xfff 0xfff 0xfff 0x0a7 0xfff
|
||||||
syscon nr __NR_get_robust_list 0x0112 0xfff 0xfff 0xfff 0x0a8 0xfff
|
syscon nr __NR_get_robust_list 0x0112 0xfff 0xfff 0xfff 0x0a8 0xfff
|
||||||
syscon nr __NR_uname 0x003f 0xfff 0x00a4 0xfff 0xfff 0xfff
|
syscon nr __NR_uname 0x003f 0xfff 0x00a4 0xfff 0xfff 0xfff
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
.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_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_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 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_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_pwrite 0x8ae8ae9dc289a812 globl hidden # a.k.a. pwrite64; netbsd+openbsd:pad
|
||||||
scall sys_readv 0x8788788782878813 globl hidden
|
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_killpg 0x092fff092fffffff globl hidden
|
||||||
scall sys_clone 0x11fffffffffff038 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_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_set_robust_list 0x0a7ffffffffff111 globl # no wrapper
|
||||||
scall sys_get_robust_list 0x0a8ffffffffff112 globl # no wrapper
|
scall sys_get_robust_list 0x0a8ffffffffff112 globl # no wrapper
|
||||||
scall sys_uname 0x0a4fff0a4ffff03f globl hidden
|
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_msgrcv 0x8e38e38e32905846 globl # no wrapper; won't polyfill for windows
|
||||||
scall sys_msgctl 0x1bc1291ff2102047 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 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_flock 0x8838838832883849 globl hidden
|
||||||
scall sys_fsync 0x85f85f85f285f84a globl hidden
|
scall sys_fsync 0x85f85f85f285f84a globl hidden
|
||||||
scall sys_fdatasync 0x8f185fa2628bb84b globl hidden # fsync() on openbsd
|
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 (!IsWindows()) sys_getpid();
|
||||||
if (warmup) warmup();
|
if (warmup) warmup();
|
||||||
testlib_clearxmmregisters();
|
testlib_clearxmmregisters();
|
||||||
|
STRACE("running test %t", fn);
|
||||||
(*fn)();
|
(*fn)();
|
||||||
if (!IsWindows()) sys_getpid();
|
if (!IsWindows()) sys_getpid();
|
||||||
if (_weaken(TearDown)) _weaken(TearDown)();
|
if (_weaken(TearDown)) _weaken(TearDown)();
|
||||||
|
|
|
@ -48,7 +48,6 @@ struct _umtx_time {
|
||||||
uint32_t _clockid;
|
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 *);
|
int sys_umtx_timedwait_uint(_Atomic(int) *, int, bool, const struct timespec *);
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
// LEGAL TRANSITIONS ┌──> TERMINATED
|
// LEGAL TRANSITIONS ┌──> TERMINATED ─┐
|
||||||
// pthread_create ─┬─> JOINABLE ─┴┬─> DETACHED ──> ZOMBIE
|
// pthread_create ─┬─> JOINABLE ─┴┬─> DETACHED ───┴─> ZOMBIE
|
||||||
// └──────────────┘
|
// └──────────────┘
|
||||||
enum PosixThreadStatus {
|
enum PosixThreadStatus {
|
||||||
|
|
||||||
|
@ -47,6 +47,8 @@ enum PosixThreadStatus {
|
||||||
//
|
//
|
||||||
// - kPosixThreadTerminated -> _pthread_free() will happen when
|
// - kPosixThreadTerminated -> _pthread_free() will happen when
|
||||||
// pthread_join() is called by the user.
|
// pthread_join() is called by the user.
|
||||||
|
// - kPosixThreadTerminated -> kPosixThreadZombie will happen when
|
||||||
|
// pthread_detach() is called by the user.
|
||||||
kPosixThreadTerminated,
|
kPosixThreadTerminated,
|
||||||
|
|
||||||
// this is a detached thread that terminated.
|
// 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_atfork(atfork_f, atfork_f, atfork_f) hidden;
|
||||||
int _pthread_reschedule(struct PosixThread *) hidden;
|
int _pthread_reschedule(struct PosixThread *) hidden;
|
||||||
int _pthread_setschedparam_freebsd(int, int, const struct sched_param *) 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_free(struct PosixThread *) hidden;
|
||||||
void _pthread_cleanup(struct PosixThread *) hidden;
|
void _pthread_cleanup(struct PosixThread *) hidden;
|
||||||
void _pthread_ungarbage(void) hidden;
|
void _pthread_ungarbage(void) hidden;
|
||||||
void _pthread_wait(struct PosixThread *) hidden;
|
|
||||||
void _pthread_zombies_add(struct PosixThread *) hidden;
|
void _pthread_zombies_add(struct PosixThread *) hidden;
|
||||||
void _pthread_zombies_purge(void) hidden;
|
void _pthread_zombies_purge(void) hidden;
|
||||||
void _pthread_zombies_decimate(void) hidden;
|
void _pthread_zombies_decimate(void) hidden;
|
||||||
|
|
|
@ -65,8 +65,11 @@ void _pthread_onfork_parent(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _pthread_onfork_child(void) {
|
void _pthread_onfork_child(void) {
|
||||||
|
pthread_mutexattr_t attr;
|
||||||
extern pthread_mutex_t __mmi_lock_obj;
|
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();
|
__kmalloc_unlock();
|
||||||
_pthread_onfork(2);
|
_pthread_onfork(2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,10 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys pthread attributes.
|
* 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));
|
memset(attr, -1, sizeof(*attr));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
* - `PTHREAD_CREATE_DETACHED`
|
* - `PTHREAD_CREATE_DETACHED`
|
||||||
* @return 0 on success, or error on failure
|
* @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;
|
*detachstate = attr->__detachstate;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
/**
|
/**
|
||||||
* Returns thread inherit schedule attribute.
|
* 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) {
|
int *inheritsched) {
|
||||||
*inheritsched = attr->__inheritsched;
|
*inheritsched = attr->__inheritsched;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
/**
|
/**
|
||||||
* Gets thread scheduler parameter attribute.
|
* 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) {
|
struct sched_param *param) {
|
||||||
*param = (struct sched_param){attr->__schedparam};
|
*param = (struct sched_param){attr->__schedparam};
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
/**
|
/**
|
||||||
* Gets thread scheduler policy attribute
|
* 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;
|
*policy = attr->__schedpolicy;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,8 @@
|
||||||
*
|
*
|
||||||
* @return 0 on success, or errno on error
|
* @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;
|
*contentionscope = attr->__contentionscope;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
* @raises EINVAL if `detachstate` is invalid
|
* @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) {
|
switch (detachstate) {
|
||||||
case PTHREAD_CREATE_JOINABLE:
|
case PTHREAD_CREATE_JOINABLE:
|
||||||
case PTHREAD_CREATE_DETACHED:
|
case PTHREAD_CREATE_DETACHED:
|
||||||
|
|
|
@ -40,8 +40,8 @@
|
||||||
* @see sched_get_priority_max()
|
* @see sched_get_priority_max()
|
||||||
* @see sched_setparam()
|
* @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) {
|
const struct sched_param *param) {
|
||||||
if (!param) return EINVAL;
|
if (!param) return EINVAL;
|
||||||
attr->__schedparam = param->sched_priority;
|
attr->__schedparam = param->sched_priority;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
* supported (Linux), otherwise it's treated as `SCHED_OTHER`
|
* supported (Linux), otherwise it's treated as `SCHED_OTHER`
|
||||||
* @see sched_setscheduler()
|
* @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;
|
attr->__schedpolicy = policy;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
* @raise ENOTSUP if `contentionscope` isn't supported on host OS
|
* @raise ENOTSUP if `contentionscope` isn't supported on host OS
|
||||||
* @raise EINVAL if `contentionscope` was invalid
|
* @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) {
|
switch (contentionscope) {
|
||||||
case PTHREAD_SCOPE_SYSTEM:
|
case PTHREAD_SCOPE_SYSTEM:
|
||||||
attr->__contentionscope = contentionscope;
|
attr->__contentionscope = contentionscope;
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
* @raise EINVAL if threads are still inside the barrier
|
* @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) {
|
if (barrier->_nsync) {
|
||||||
nsync_counter_free(barrier->_nsync);
|
nsync_counter_free(barrier->_nsync);
|
||||||
barrier->_nsync = 0;
|
barrier->_nsync = 0;
|
||||||
|
|
|
@ -30,8 +30,9 @@
|
||||||
* @raise EINVAL if `count` isn't greater than zero
|
* @raise EINVAL if `count` isn't greater than zero
|
||||||
* @raise ENOMEM if insufficient memory exists
|
* @raise ENOMEM if insufficient memory exists
|
||||||
*/
|
*/
|
||||||
int pthread_barrier_init(pthread_barrier_t *barrier,
|
errno_t pthread_barrier_init(pthread_barrier_t *barrier,
|
||||||
const pthread_barrierattr_t *attr, unsigned count) {
|
const pthread_barrierattr_t *attr,
|
||||||
|
unsigned count) {
|
||||||
nsync_counter c;
|
nsync_counter c;
|
||||||
if (!count) return EINVAL;
|
if (!count) return EINVAL;
|
||||||
if (!(c = nsync_counter_new(count))) return ENOMEM;
|
if (!(c = nsync_counter_new(count))) return ENOMEM;
|
||||||
|
|
|
@ -16,15 +16,15 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/thread/thread.h"
|
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys barrier attributes.
|
* Destroys barrier attributes.
|
||||||
*
|
*
|
||||||
* @return 0 on success, or error on failure
|
* @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));
|
memset(attr, -1, sizeof(*attr));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,8 @@
|
||||||
* - `PTHREAD_PROCESS_SHARED` (unsupported)
|
* - `PTHREAD_PROCESS_SHARED` (unsupported)
|
||||||
* @return 0 on success, or error on failure
|
* @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) {
|
int *pshared) {
|
||||||
*pshared = *attr;
|
*pshared = *attr;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
*
|
*
|
||||||
* @return 0 on success, or error on failure
|
* @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;
|
*attr = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,8 @@
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
* @raises EINVAL if `pshared` is invalid
|
* @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) {
|
switch (pshared) {
|
||||||
case PTHREAD_PROCESS_PRIVATE:
|
case PTHREAD_PROCESS_PRIVATE:
|
||||||
*attr = pshared;
|
*attr = pshared;
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "libc/calls/ucontext.h"
|
#include "libc/calls/ucontext.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/sa.h"
|
#include "libc/sysv/consts/sa.h"
|
||||||
|
@ -85,12 +86,12 @@ static void ListenForSigCancel(void) {
|
||||||
* `writev`, `pwrite`, `pwritev`, `accept`, `connect`, `recvmsg`,
|
* `writev`, `pwrite`, `pwritev`, `accept`, `connect`, `recvmsg`,
|
||||||
* `sendmsg`, `recv`, `send`, `tcdrain`, `clock_nanosleep`, `fsync`,
|
* `sendmsg`, `recv`, `send`, `tcdrain`, `clock_nanosleep`, `fsync`,
|
||||||
* `fdatasync`, `fcntl(F_SETLKW)`, `epoll`, `sigsuspend`, `msync`,
|
* `fdatasync`, `fcntl(F_SETLKW)`, `epoll`, `sigsuspend`, `msync`,
|
||||||
* `wait4`, `getrandom`, `pthread_cond_timedwait` are most cancellation
|
* `wait4`, `getrandom`, `pthread_cond_timedwait`, and `sem_timedwait`
|
||||||
* points, plus many userspace libraries that call the above functions,
|
* are most cancellation points, plus many userspace libraries that call
|
||||||
* unless they're using pthread_setcancelstate() to temporarily disable
|
* the above functions, unless they're using pthread_setcancelstate() to
|
||||||
* the cancellation mechanism. Some userspace functions, e.g. system()
|
* temporarily disable the cancellation mechanism. Some userspace
|
||||||
* and popen() will eagerly call pthread_testcancel_np() to help avoid
|
* functions, e.g. system() will eagerly call pthread_testcancel_np() to
|
||||||
* the potential for resource leaks later on.
|
* help avoid the potential for resource leaks later on.
|
||||||
*
|
*
|
||||||
* It's possible to put a thread in asynchronous cancellation mode using
|
* It's possible to put a thread in asynchronous cancellation mode using
|
||||||
* pthread_setcanceltype(), thus allowing a cancellation to occur at any
|
* 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
|
* @return 0 on success, or errno on error
|
||||||
* @raise ESRCH if thread isn't alive
|
* @raise ESRCH if thread isn't alive
|
||||||
*/
|
*/
|
||||||
int pthread_cancel(pthread_t thread) {
|
errno_t pthread_cancel(pthread_t thread) {
|
||||||
int e, rc, tid;
|
int e, rc, tid;
|
||||||
static bool once;
|
static bool once;
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
|
@ -124,9 +125,9 @@ int pthread_cancel(pthread_t thread) {
|
||||||
default:
|
default:
|
||||||
break;
|
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 (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);
|
pthread_exit(PTHREAD_CANCELED);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -183,7 +184,7 @@ void pthread_testcancel(void) {
|
||||||
* @return 0 if not cancelled or cancellation is blocked or `ECANCELED`
|
* @return 0 if not cancelled or cancellation is blocked or `ECANCELED`
|
||||||
* in masked mode when the calling thread has been cancelled
|
* in masked mode when the calling thread has been cancelled
|
||||||
*/
|
*/
|
||||||
int pthread_testcancel_np(void) {
|
errno_t pthread_testcancel_np(void) {
|
||||||
int rc;
|
int rc;
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
if (!__tls_enabled) return 0;
|
if (!__tls_enabled) return 0;
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
* @return 0 on success, or error number on failure
|
* @return 0 on success, or error number on failure
|
||||||
* @raise EINVAL if threads are still waiting on condition
|
* @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));
|
memset(cond, -1, sizeof(*cond));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,8 @@
|
||||||
* @param attr may be null
|
* @param attr may be null
|
||||||
* @return 0 on success, or error number on failure
|
* @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};
|
*cond = (pthread_cond_t){0};
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,8 +41,9 @@
|
||||||
* @raise EPERM if `mutex` is `PTHREAD_MUTEX_ERRORCHECK` and the lock
|
* @raise EPERM if `mutex` is `PTHREAD_MUTEX_ERRORCHECK` and the lock
|
||||||
* isn't owned by the current thread
|
* isn't owned by the current thread
|
||||||
* @raise EINVAL if `0 ≤ abstime->tv_nsec < 1000000000` wasn't the case
|
* @raise EINVAL if `0 ≤ abstime->tv_nsec < 1000000000` wasn't the case
|
||||||
* @see pthread_cond_broadcast
|
* @raise ECANCELED if calling thread was cancelled in masked mode
|
||||||
* @see pthread_cond_signal
|
* @see pthread_cond_broadcast()
|
||||||
|
* @see pthread_cond_signal()
|
||||||
* @cancellationpoint
|
* @cancellationpoint
|
||||||
*/
|
*/
|
||||||
errno_t pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
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
|
* @param mutex needs to be held by thread when calling this function
|
||||||
* @return 0 on success, or errno on error
|
* @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
|
* @raise EPERM if `mutex` is `PTHREAD_MUTEX_ERRORCHECK` and the lock
|
||||||
* isn't owned by the current thread
|
* isn't owned by the current thread
|
||||||
* @see pthread_cond_timedwait
|
* @see pthread_cond_timedwait
|
||||||
|
|
|
@ -16,15 +16,15 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/thread/thread.h"
|
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys condition attributes.
|
* Destroys condition attributes.
|
||||||
*
|
*
|
||||||
* @return 0 on success, or error on failure
|
* @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));
|
memset(attr, -1, sizeof(*attr));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
* - `PTHREAD_PROCESS_SHARED`
|
* - `PTHREAD_PROCESS_SHARED`
|
||||||
* @return 0 on success, or error on failure
|
* @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;
|
*pshared = *attr;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
*
|
*
|
||||||
* @return 0 on success, or error on failure
|
* @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;
|
*attr = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
* @raises EINVAL if `pshared` is invalid
|
* @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) {
|
switch (pshared) {
|
||||||
case PTHREAD_PROCESS_PRIVATE:
|
case PTHREAD_PROCESS_PRIVATE:
|
||||||
*attr = pshared;
|
*attr = pshared;
|
||||||
|
|
|
@ -59,8 +59,8 @@ STATIC_YOINK("_pthread_atfork");
|
||||||
#define MAP_ANON_OPENBSD 0x1000
|
#define MAP_ANON_OPENBSD 0x1000
|
||||||
#define MAP_STACK_OPENBSD 0x4000
|
#define MAP_STACK_OPENBSD 0x4000
|
||||||
|
|
||||||
void _pthread_wait(struct PosixThread *pt) {
|
errno_t _pthread_wait(struct PosixThread *pt) {
|
||||||
_wait0(&pt->tib->tib_tid);
|
return _wait0(&pt->tib->tib_tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _pthread_free(struct PosixThread *pt) {
|
void _pthread_free(struct PosixThread *pt) {
|
||||||
|
|
|
@ -32,36 +32,29 @@
|
||||||
* @returnserrno
|
* @returnserrno
|
||||||
* @threadsafe
|
* @threadsafe
|
||||||
*/
|
*/
|
||||||
int pthread_detach(pthread_t thread) {
|
errno_t pthread_detach(pthread_t thread) {
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
enum PosixThreadStatus status;
|
enum PosixThreadStatus status, transition;
|
||||||
if (!(pt = (struct PosixThread *)thread)) {
|
if (!(pt = (struct PosixThread *)thread)) return EINVAL;
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
status = atomic_load_explicit(&pt->status, memory_order_acquire);
|
status = atomic_load_explicit(&pt->status, memory_order_acquire);
|
||||||
if (status == kPosixThreadDetached || status == kPosixThreadZombie) {
|
if (status == kPosixThreadDetached || status == kPosixThreadZombie) {
|
||||||
// these two states indicate the thread was already detached, in
|
// these two states indicate the thread was already detached, in
|
||||||
// which case it's already listed under _pthread_zombies.
|
// which case it's already listed under _pthread_zombies.
|
||||||
return EINVAL;
|
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) {
|
} else if (status == kPosixThreadJoinable) {
|
||||||
if (atomic_compare_exchange_weak_explicit(
|
transition = kPosixThreadDetached;
|
||||||
&pt->status, &status, kPosixThreadDetached, memory_order_release,
|
} else if (status == kPosixThreadTerminated) {
|
||||||
memory_order_relaxed)) {
|
transition = kPosixThreadZombie;
|
||||||
_pthread_zombies_add(pt);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
notpossible;
|
notpossible;
|
||||||
}
|
}
|
||||||
|
if (atomic_compare_exchange_weak_explicit(&pt->status, &status, transition,
|
||||||
|
memory_order_release,
|
||||||
|
memory_order_relaxed)) {
|
||||||
|
_pthread_zombies_add(pt);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
* @return 0 on success, or errno on error
|
* @return 0 on success, or errno on error
|
||||||
* @raise ENOMEM is listed as a possible result by LSB 5.0
|
* @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;
|
struct PosixThread *pt = (struct PosixThread *)thread;
|
||||||
memcpy(attr, &pt->attr, sizeof(pt->attr));
|
memcpy(attr, &pt->attr, sizeof(pt->attr));
|
||||||
switch (atomic_load_explicit(&pt->status, memory_order_relaxed)) {
|
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 │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/blockcancel.internal.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/syscall-sysv.internal.h"
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
@ -28,23 +29,7 @@
|
||||||
#include "libc/sysv/consts/pr.h"
|
#include "libc/sysv/consts/pr.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
|
|
||||||
/**
|
static errno_t pthread_getname_impl(pthread_t thread, char *name, size_t size) {
|
||||||
* 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) {
|
|
||||||
int fd, rc, tid, len, e = errno;
|
int fd, rc, tid, len, e = errno;
|
||||||
|
|
||||||
if (!size) return 0;
|
if (!size) return 0;
|
||||||
|
@ -113,3 +98,27 @@ errno_t pthread_getname_np(pthread_t thread, char *name, size_t size) {
|
||||||
return ENOSYS;
|
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.
|
* 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 sched_param *param) {
|
||||||
struct PosixThread *pt = (struct PosixThread *)thread;
|
struct PosixThread *pt = (struct PosixThread *)thread;
|
||||||
*policy = pt->attr.__schedpolicy;
|
*policy = pt->attr.__schedpolicy;
|
||||||
*param = (struct sched_param){pt->attr.__schedparam};
|
*param = (struct sched_param){pt->attr.__schedparam};
|
||||||
|
|
|
@ -27,14 +27,16 @@
|
||||||
* @param value_ptr if non-null will receive pthread_exit() argument
|
* @param value_ptr if non-null will receive pthread_exit() argument
|
||||||
* if the thread called pthread_exit(), or `PTHREAD_CANCELED` if
|
* if the thread called pthread_exit(), or `PTHREAD_CANCELED` if
|
||||||
* pthread_cancel() destroyed the thread instead
|
* 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 EDEADLK if `thread` is the current thread
|
||||||
* @raise EINVAL if `thread` is detached
|
* @raise EINVAL if `thread` is detached
|
||||||
* @cancellationpoint
|
* @cancellationpoint
|
||||||
* @returnserrno
|
* @returnserrno
|
||||||
* @threadsafe
|
* @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;
|
struct PosixThread *pt;
|
||||||
if (thread == __get_tls()->tib_pthread) {
|
if (thread == __get_tls()->tib_pthread) {
|
||||||
return EDEADLK;
|
return EDEADLK;
|
||||||
|
@ -44,7 +46,9 @@ int pthread_join(pthread_t thread, void **value_ptr) {
|
||||||
pt->status == kPosixThreadDetached) {
|
pt->status == kPosixThreadDetached) {
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
_pthread_wait(pt);
|
if ((rc = _pthread_wait(pt))) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
if (value_ptr) {
|
if (value_ptr) {
|
||||||
*value_ptr = pt->rc;
|
*value_ptr = pt->rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
* @raise EPERM if permission was denied
|
* @raise EPERM if permission was denied
|
||||||
* @asyncsignalsafe
|
* @asyncsignalsafe
|
||||||
*/
|
*/
|
||||||
int pthread_kill(pthread_t thread, int sig) {
|
errno_t pthread_kill(pthread_t thread, int sig) {
|
||||||
int rc, e = errno;
|
int rc, e = errno;
|
||||||
struct PosixThread *pt = (struct PosixThread *)thread;
|
struct PosixThread *pt = (struct PosixThread *)thread;
|
||||||
if (!tkill(pt->tid, sig)) {
|
if (!tkill(pt->tid, sig)) {
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
* @return 0 on success, or error number on failure
|
* @return 0 on success, or error number on failure
|
||||||
* @raise EINVAL if mutex is locked in our implementation
|
* @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));
|
memset(mutex, -1, sizeof(*mutex));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,14 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/thread/thread.h"
|
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys mutex attr.
|
* Destroys mutex attr.
|
||||||
* @return 0 on success, or error number on failure
|
* @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));
|
memset(attr, -1, sizeof(*attr));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,8 @@
|
||||||
* - `PTHREAD_PROCESS_SHARED`
|
* - `PTHREAD_PROCESS_SHARED`
|
||||||
* @return 0 on success, or error on failure
|
* @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) {
|
int *pshared) {
|
||||||
*pshared = attr->_pshared;
|
*pshared = attr->_pshared;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
* - `PTHREAD_MUTEX_ERRORCHECK`
|
* - `PTHREAD_MUTEX_ERRORCHECK`
|
||||||
* @return 0 on success, or error on failure
|
* @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;
|
*type = attr->_type;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
* Initializes mutex attr.
|
* Initializes mutex attr.
|
||||||
* @return 0 on success, or error number on failure
|
* @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};
|
*attr = (pthread_mutexattr_t){0};
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
* @raises EINVAL if `pshared` is invalid
|
* @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) {
|
switch (pshared) {
|
||||||
case PTHREAD_PROCESS_SHARED:
|
case PTHREAD_PROCESS_SHARED:
|
||||||
case PTHREAD_PROCESS_PRIVATE:
|
case PTHREAD_PROCESS_PRIVATE:
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
* @raises EINVAL if `type` is invalid
|
* @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) {
|
switch (type) {
|
||||||
case PTHREAD_MUTEX_NORMAL:
|
case PTHREAD_MUTEX_NORMAL:
|
||||||
case PTHREAD_MUTEX_RECURSIVE:
|
case PTHREAD_MUTEX_RECURSIVE:
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/posixthread.internal.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 rc, e = errno;
|
||||||
int policy = pt->attr.__schedpolicy;
|
int policy = pt->attr.__schedpolicy;
|
||||||
struct sched_param param = {pt->attr.__schedparam};
|
struct sched_param param = {pt->attr.__schedparam};
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
* @return 0 on success, or error number on failure
|
* @return 0 on success, or error number on failure
|
||||||
* @raise EINVAL if any threads still hold the lock
|
* @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));
|
memset(rwlock, -1, sizeof(*rwlock));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,8 @@
|
||||||
* @param attr may be null
|
* @param attr may be null
|
||||||
* @return 0 on success, or error number on failure
|
* @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) {
|
const pthread_rwlockattr_t *attr) {
|
||||||
*rwlock = (pthread_rwlock_t){0};
|
*rwlock = (pthread_rwlock_t){0};
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,15 +16,15 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/thread/thread.h"
|
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys read-write lock attributes.
|
* Destroys read-write lock attributes.
|
||||||
*
|
*
|
||||||
* @return 0 on success, or error on failure
|
* @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));
|
memset(attr, -1, sizeof(*attr));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,8 @@
|
||||||
* - `PTHREAD_PROCESS_SHARED` (unsupported)
|
* - `PTHREAD_PROCESS_SHARED` (unsupported)
|
||||||
* @return 0 on success, or error on failure
|
* @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) {
|
int *pshared) {
|
||||||
*pshared = *attr;
|
*pshared = *attr;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
*
|
*
|
||||||
* @return 0 on success, or error on failure
|
* @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;
|
*attr = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
* @raises EINVAL if `pshared` is invalid
|
* @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) {
|
switch (pshared) {
|
||||||
case PTHREAD_PROCESS_PRIVATE:
|
case PTHREAD_PROCESS_PRIVATE:
|
||||||
*attr = pshared;
|
*attr = pshared;
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
* @raise EINVAL if `type` has bad value
|
* @raise EINVAL if `type` has bad value
|
||||||
* @see pthread_cancel() for docs
|
* @see pthread_cancel() for docs
|
||||||
*/
|
*/
|
||||||
int pthread_setcanceltype(int type, int *oldtype) {
|
errno_t pthread_setcanceltype(int type, int *oldtype) {
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case PTHREAD_CANCEL_DEFERRED:
|
case PTHREAD_CANCEL_DEFERRED:
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/blockcancel.internal.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/syscall-sysv.internal.h"
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
@ -28,30 +29,7 @@
|
||||||
#include "libc/sysv/consts/pr.h"
|
#include "libc/sysv/consts/pr.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
|
|
||||||
/**
|
static errno_t pthread_setname_impl(pthread_t thread, const char *name) {
|
||||||
* 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) {
|
|
||||||
char path[128], *p;
|
char path[128], *p;
|
||||||
int fd, rc, tid, len, e = errno;
|
int fd, rc, tid, len, e = errno;
|
||||||
|
|
||||||
|
@ -113,3 +91,34 @@ errno_t pthread_setname_np(pthread_t thread, const char *name) {
|
||||||
return ENOSYS;
|
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_get_priority_max()
|
||||||
* @see sched_setscheduler()
|
* @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) {
|
const struct sched_param *param) {
|
||||||
struct PosixThread *pt = (struct PosixThread *)thread;
|
struct PosixThread *pt = (struct PosixThread *)thread;
|
||||||
if (!param) return EINVAL;
|
if (!param) return EINVAL;
|
||||||
pt->attr.__schedpolicy = policy;
|
pt->attr.__schedpolicy = policy;
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
/**
|
/**
|
||||||
* Sets scheduler parameter on thread.
|
* 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;
|
struct PosixThread *pt = (struct PosixThread *)thread;
|
||||||
pt->attr.__schedparam = prio;
|
pt->attr.__schedparam = prio;
|
||||||
return _pthread_reschedule(pt);
|
return _pthread_reschedule(pt);
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
* @return 0 on success, or errno on error
|
* @return 0 on success, or errno on error
|
||||||
* @asyncsignalsafe
|
* @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;
|
int rc, e = errno;
|
||||||
if (!sigprocmask(how, set, old)) {
|
if (!sigprocmask(how, set, old)) {
|
||||||
rc = 0;
|
rc = 0;
|
||||||
|
|
|
@ -44,6 +44,7 @@ void _pthread_zombies_add(struct PosixThread *pt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _pthread_zombies_collect(struct Zombie *z) {
|
static void _pthread_zombies_collect(struct Zombie *z) {
|
||||||
|
// TODO(jart): We need a trywait() op here to avoid cancellation.
|
||||||
_pthread_wait(z->pt);
|
_pthread_wait(z->pt);
|
||||||
_pthread_free(z->pt);
|
_pthread_free(z->pt);
|
||||||
free(z);
|
free(z);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
|
#include "libc/calls/blockcancel.internal.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/struct/stat.h"
|
#include "libc/calls/struct/stat.h"
|
||||||
#include "libc/dce.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)))) {
|
if (!(path = sem_path_np(name, pathbuf, sizeof(pathbuf)))) {
|
||||||
return SEM_FAILED;
|
return SEM_FAILED;
|
||||||
}
|
}
|
||||||
|
BLOCK_CANCELLATIONS;
|
||||||
sem_open_init();
|
sem_open_init();
|
||||||
sem_open_lock();
|
sem_open_lock();
|
||||||
if ((s = sem_open_reopen(path))) {
|
if ((s = sem_open_reopen(path))) {
|
||||||
|
@ -233,6 +235,7 @@ sem_t *sem_open(const char *name, int oflag, ...) {
|
||||||
sem = SEM_FAILED;
|
sem = SEM_FAILED;
|
||||||
}
|
}
|
||||||
sem_open_unlock();
|
sem_open_unlock();
|
||||||
|
ALLOW_CANCELLATIONS;
|
||||||
return sem;
|
return sem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/semaphore.h"
|
#include "libc/thread/semaphore.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
#include "third_party/nsync/futex.internal.h"
|
#include "third_party/nsync/futex.internal.h"
|
||||||
|
|
||||||
static void sem_delay(int n) {
|
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.
|
* Locks semaphore w/ deadline.
|
||||||
*
|
*
|
||||||
* @param abstime is absolute deadline or null to wait forever
|
* @param abstime is absolute deadline or null to wait forever
|
||||||
* @return 0 on success, or -1 w/ errno
|
* @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 EINTR if signal was delivered instead
|
||||||
* @raise EDEADLK if deadlock was detected
|
* @raise EDEADLK if deadlock was detected
|
||||||
* @raise ETIMEDOUT if deadline expired
|
* @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,
|
_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 {
|
do {
|
||||||
if (!(v = atomic_load_explicit(&sem->sem_value, memory_order_relaxed))) {
|
if (!(v = atomic_load_explicit(&sem->sem_value, memory_order_relaxed))) {
|
||||||
rc = nsync_futex_wait_(&sem->sem_value, v, sem->sem_pshared,
|
rc = nsync_futex_wait_(&sem->sem_value, v, sem->sem_pshared,
|
||||||
sem_timeout(&ts, abstime));
|
sem_timeout(&ts, abstime));
|
||||||
if (rc == -EINTR) {
|
if (rc == -EINTR || rc == -ECANCELED) {
|
||||||
rc = eintr();
|
errno = -rc;
|
||||||
|
rc = -1;
|
||||||
} else if (rc == -EAGAIN || rc == -EWOULDBLOCK) {
|
} else if (rc == -EAGAIN || rc == -EWOULDBLOCK) {
|
||||||
rc = 0;
|
rc = 0;
|
||||||
} else if (rc == -ETIMEDOUT) {
|
} 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,
|
&sem->sem_value, &v, v - 1, memory_order_acquire,
|
||||||
memory_order_relaxed)));
|
memory_order_relaxed)));
|
||||||
|
|
||||||
_unassert(atomic_fetch_add_explicit(&sem->sem_waiters, -1,
|
pthread_cleanup_pop(1);
|
||||||
memory_order_release) > 0);
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
* Locks semaphore.
|
* Locks semaphore.
|
||||||
*
|
*
|
||||||
* @return 0 on success, or -1 w/ errno
|
* @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 EINTR if signal was delivered instead
|
||||||
* @raise EDEADLK if deadlock was detected
|
* @raise EDEADLK if deadlock was detected
|
||||||
* @raise EINVAL if `sem` is invalid
|
* @raise EINVAL if `sem` is invalid
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/thread/wait0.internal.h"
|
#include "libc/thread/wait0.internal.h"
|
||||||
#include "third_party/nsync/futex.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
|
* 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
|
* uses futexes on Linux, FreeBSD, OpenBSD, and Windows. On other
|
||||||
* platforms this uses polling with exponential backoff.
|
* 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) {
|
errno_t _wait0(const atomic_int *ctid) {
|
||||||
int x;
|
int x, rc;
|
||||||
while ((x = atomic_load_explicit(ctid, memory_order_acquire))) {
|
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)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
void _wait0(const atomic_int *) hidden;
|
errno_t _wait0(const atomic_int *) hidden;
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "libc/calls/struct/sigaction.h"
|
#include "libc/calls/struct/sigaction.h"
|
||||||
#include "libc/calls/struct/sigset.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
#include "libc/calls/struct/stat.h"
|
#include "libc/calls/struct/stat.h"
|
||||||
|
#include "libc/calls/struct/sysinfo.h"
|
||||||
#include "libc/calls/struct/timespec.h"
|
#include "libc/calls/struct/timespec.h"
|
||||||
#include "libc/calls/struct/timeval.h"
|
#include "libc/calls/struct/timeval.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
@ -358,6 +359,13 @@ struct Claims {
|
||||||
} data[QUEUE_MAX];
|
} data[QUEUE_MAX];
|
||||||
} g_claims;
|
} g_claims;
|
||||||
|
|
||||||
|
long GetTotalRam(void) {
|
||||||
|
struct sysinfo si;
|
||||||
|
si.totalram = 256 * 1024 * 1024;
|
||||||
|
sysinfo(&si);
|
||||||
|
return si.totalram;
|
||||||
|
}
|
||||||
|
|
||||||
// easy string sender
|
// easy string sender
|
||||||
ssize_t Write(int fd, const char *s) {
|
ssize_t Write(int fd, const char *s) {
|
||||||
return write(fd, s, strlen(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
|
// we need to do is wait a little bit, and use exponential backoff
|
||||||
int DbOpen(const char *path, sqlite3 **db) {
|
int DbOpen(const char *path, sqlite3 **db) {
|
||||||
int i, rc;
|
int i, rc;
|
||||||
|
char sql[128];
|
||||||
rc = sqlite3_open(path, db);
|
rc = sqlite3_open(path, db);
|
||||||
if (rc != SQLITE_OK) return rc;
|
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) {
|
for (i = 0; i < 7; ++i) {
|
||||||
rc = sqlite3_exec(*db, "PRAGMA journal_mode=WAL", 0, 0, 0);
|
rc = sqlite3_exec(*db, "PRAGMA journal_mode=WAL", 0, 0, 0);
|
||||||
if (rc == SQLITE_OK) break;
|
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
|
// why not make the statement prepare api a little less hairy too
|
||||||
int DbPrepare(sqlite3 *db, sqlite3_stmt **stmt, const char *sql) {
|
int DbPrepare(sqlite3 *db, sqlite3_stmt **stmt, const char *sql) {
|
||||||
|
kprintf("%s\n", sql);
|
||||||
return sqlite3_prepare_v2(db, sql, -1, stmt, 0);
|
return sqlite3_prepare_v2(db, sql, -1, stmt, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -194,7 +194,7 @@ TEST(sigaction, autoZombieSlayer) {
|
||||||
if (!pid) _Exit(0);
|
if (!pid) _Exit(0);
|
||||||
ASSERT_SYS(0, pid, wait(0));
|
ASSERT_SYS(0, pid, wait(0));
|
||||||
// enable automatic zombie slayer
|
// 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
|
sa.sa_flags = SA_NOCLDWAIT; // seems to be optional
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
ASSERT_SYS(0, 0, sigaction(SIGCHLD, &sa, &sa));
|
ASSERT_SYS(0, 0, sigaction(SIGCHLD, &sa, &sa));
|
||||||
|
|
|
@ -24,8 +24,11 @@
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
#include "libc/thread/thread2.h"
|
||||||
|
|
||||||
int pfds[2];
|
int pfds[2];
|
||||||
|
pthread_cond_t cv;
|
||||||
|
pthread_mutex_t mu;
|
||||||
atomic_bool gotcleanup;
|
atomic_bool gotcleanup;
|
||||||
char testlib_enable_tmp_setup_teardown;
|
char testlib_enable_tmp_setup_teardown;
|
||||||
|
|
||||||
|
@ -37,6 +40,49 @@ void OnCleanup(void *arg) {
|
||||||
gotcleanup = true;
|
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) {
|
void *Worker(void *arg) {
|
||||||
int n;
|
int n;
|
||||||
char buf[8];
|
char buf[8];
|
||||||
|
@ -74,7 +120,6 @@ TEST(pthread_cancel, synchronous_delayed) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void *DisabledWorker(void *arg) {
|
void *DisabledWorker(void *arg) {
|
||||||
int n;
|
|
||||||
char buf[8];
|
char buf[8];
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_MASKED, 0);
|
pthread_setcancelstate(PTHREAD_CANCEL_MASKED, 0);
|
||||||
pthread_cleanup_push(OnCleanup, 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[1]));
|
||||||
ASSERT_SYS(0, 0, close(pfds[0]));
|
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/mem/mem.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
#include "third_party/musl/passwd.h"
|
#include "third_party/musl/passwd.h"
|
||||||
|
|
||||||
asm(".ident\t\"\\n\\n\
|
asm(".ident\t\"\\n\\n\
|
||||||
|
@ -37,9 +38,6 @@ Musl libc (MIT License)\\n\
|
||||||
Copyright 2005-2014 Rich Felker, et. al.\"");
|
Copyright 2005-2014 Rich Felker, et. al.\"");
|
||||||
asm(".include \"libc/disclaimer.inc\"");
|
asm(".include \"libc/disclaimer.inc\"");
|
||||||
|
|
||||||
#define PTHREAD_CANCEL_DISABLE 0
|
|
||||||
#define pthread_setcancelstate(x, y) (void)y
|
|
||||||
|
|
||||||
static unsigned atou(char **s) {
|
static unsigned atou(char **s) {
|
||||||
unsigned x;
|
unsigned x;
|
||||||
for (x = 0; **s - '0' < 10U; ++*s) x = 10 * x + (**s - '0');
|
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/str/str.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
#include "libc/sysv/consts/s.h"
|
#include "libc/sysv/consts/s.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
#include "third_party/musl/ftw.h"
|
#include "third_party/musl/ftw.h"
|
||||||
|
|
||||||
asm(".ident\t\"\\n\\n\
|
asm(".ident\t\"\\n\\n\
|
||||||
|
@ -44,9 +45,6 @@ asm(".include \"libc/disclaimer.inc\"");
|
||||||
should be changed to use realloc */
|
should be changed to use realloc */
|
||||||
#define PATH_MAX2 2048
|
#define PATH_MAX2 2048
|
||||||
|
|
||||||
/* no cosmo pthreads support atm */
|
|
||||||
#define pthread_setcancelstate(...)
|
|
||||||
|
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
|
|
||||||
struct history
|
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/mem/mem.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
#include "third_party/musl/passwd.h"
|
#include "third_party/musl/passwd.h"
|
||||||
|
|
||||||
asm(".ident\t\"\\n\\n\
|
asm(".ident\t\"\\n\\n\
|
||||||
|
@ -38,9 +39,6 @@ Copyright 2005-2014 Rich Felker, et. al.\"");
|
||||||
asm(".include \"libc/disclaimer.inc\"");
|
asm(".include \"libc/disclaimer.inc\"");
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
|
|
||||||
#define PTHREAD_CANCEL_DISABLE 0
|
|
||||||
#define pthread_setcancelstate(x, y) (void)y
|
|
||||||
|
|
||||||
static unsigned
|
static unsigned
|
||||||
atou(char **s)
|
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_malloc_() is implemented as kmalloc()
|
||||||
- nsync_mu_semaphore uses Cosmopolitan Futexes
|
- nsync_mu_semaphore uses Cosmopolitan Futexes
|
||||||
|
- block pthread cancellations in nsync_mu_lock_slow_
|
||||||
|
|
116
third_party/nsync/futex.c
vendored
116
third_party/nsync/futex.c
vendored
|
@ -23,17 +23,20 @@
|
||||||
#include "libc/calls/state.internal.h"
|
#include "libc/calls/state.internal.h"
|
||||||
#include "libc/calls/struct/timespec.h"
|
#include "libc/calls/struct/timespec.h"
|
||||||
#include "libc/calls/struct/timespec.internal.h"
|
#include "libc/calls/struct/timespec.internal.h"
|
||||||
|
#include "libc/calls/syscall_support-nt.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/describeflags.internal.h"
|
#include "libc/intrin/describeflags.internal.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
#include "libc/intrin/strace.internal.h"
|
||||||
|
#include "libc/intrin/weaken.h"
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/nt/runtime.h"
|
#include "libc/nt/runtime.h"
|
||||||
#include "libc/nt/synchronization.h"
|
#include "libc/nt/synchronization.h"
|
||||||
#include "libc/sysv/consts/clock.h"
|
#include "libc/sysv/consts/clock.h"
|
||||||
#include "libc/sysv/consts/futex.h"
|
#include "libc/sysv/consts/futex.h"
|
||||||
#include "libc/sysv/consts/timer.h"
|
#include "libc/sysv/consts/timer.h"
|
||||||
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/freebsd.internal.h"
|
#include "libc/thread/freebsd.internal.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
|
@ -45,7 +48,9 @@
|
||||||
|
|
||||||
#define FUTEX_WAIT_BITS_ FUTEX_BITSET_MATCH_ANY
|
#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_WAIT_;
|
||||||
static int FUTEX_PRIVATE_FLAG_;
|
static int FUTEX_PRIVATE_FLAG_;
|
||||||
|
@ -59,6 +64,7 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) {
|
||||||
|
|
||||||
if (IsWindows ()) {
|
if (IsWindows ()) {
|
||||||
FUTEX_IS_SUPPORTED = true;
|
FUTEX_IS_SUPPORTED = true;
|
||||||
|
FUTEX_TIMEOUT_IS_ABSOLUTE = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,8 +76,6 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(FUTEX_IS_SUPPORTED = IsLinux () || IsOpenbsd ())) {
|
if (!(FUTEX_IS_SUPPORTED = IsLinux () || IsOpenbsd ())) {
|
||||||
// we're using sched_yield() so let's
|
|
||||||
// avoid needless clock_gettime calls
|
|
||||||
FUTEX_TIMEOUT_IS_ABSOLUTE = true;
|
FUTEX_TIMEOUT_IS_ABSOLUTE = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -101,7 +105,7 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) {
|
||||||
FUTEX_TIMEOUT_IS_ABSOLUTE = true;
|
FUTEX_TIMEOUT_IS_ABSOLUTE = true;
|
||||||
} else if (IsOpenbsd () ||
|
} else if (IsOpenbsd () ||
|
||||||
(!IsTiny () && IsLinux () &&
|
(!IsTiny () && IsLinux () &&
|
||||||
!_futex (&x, FUTEX_WAKE_PRIVATE, 1, 0, 0, 0))) {
|
!_futex_wake (&x, FUTEX_WAKE_PRIVATE, 1))) {
|
||||||
FUTEX_WAIT_ = FUTEX_WAIT;
|
FUTEX_WAIT_ = FUTEX_WAIT;
|
||||||
FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
|
FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
|
||||||
} else {
|
} 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) {
|
static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *timeout) {
|
||||||
|
int rc;
|
||||||
int64_t nanos, maxnanos;
|
int64_t nanos, maxnanos;
|
||||||
struct timespec ts, deadline;
|
struct timespec ts, deadline;
|
||||||
|
|
||||||
if (atomic_load_explicit (w, memory_order_relaxed) != expect) {
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
ts = nsync_time_now ();
|
ts = nsync_time_now ();
|
||||||
if (!timeout) {
|
if (!timeout) {
|
||||||
deadline = nsync_time_no_deadline;
|
deadline = nsync_time_no_deadline;
|
||||||
|
@ -129,15 +130,15 @@ static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *ti
|
||||||
nanos = 100;
|
nanos = 100;
|
||||||
maxnanos = __SIG_POLLING_INTERVAL_MS * 1000L * 1000;
|
maxnanos = __SIG_POLLING_INTERVAL_MS * 1000L * 1000;
|
||||||
while (nsync_time_cmp (deadline, ts) > 0) {
|
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));
|
ts = nsync_time_add (ts, _timespec_fromnanos (nanos));
|
||||||
if (nsync_time_cmp (ts, deadline) > 0) {
|
if (nsync_time_cmp (ts, deadline) > 0) {
|
||||||
ts = deadline;
|
ts = deadline;
|
||||||
}
|
}
|
||||||
if (nsync_time_sleep_until (ts)) {
|
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
|
||||||
return -EINTR;
|
return 0;
|
||||||
|
}
|
||||||
|
if ((rc = nsync_time_sleep_until (ts))) {
|
||||||
|
return -rc;
|
||||||
}
|
}
|
||||||
if (nanos < maxnanos) {
|
if (nanos < maxnanos) {
|
||||||
nanos <<= 1;
|
nanos <<= 1;
|
||||||
|
@ -150,10 +151,46 @@ static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *ti
|
||||||
return -ETIMEDOUT;
|
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;
|
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;
|
int e, rc, op, fop;
|
||||||
|
|
||||||
|
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
op = FUTEX_WAIT_;
|
op = FUTEX_WAIT_;
|
||||||
if (pshare == PTHREAD_PROCESS_PRIVATE) {
|
if (pshare == PTHREAD_PROCESS_PRIVATE) {
|
||||||
op |= FUTEX_PRIVATE_FLAG_;
|
op |= FUTEX_PRIVATE_FLAG_;
|
||||||
|
@ -165,37 +202,35 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, struct timespec *
|
||||||
DescribeTimespec (0, timeout));
|
DescribeTimespec (0, timeout));
|
||||||
|
|
||||||
if (FUTEX_IS_SUPPORTED) {
|
if (FUTEX_IS_SUPPORTED) {
|
||||||
|
e = errno;
|
||||||
if (IsWindows ()) {
|
if (IsWindows ()) {
|
||||||
// Windows 8 futexes don't support multiple processes :(
|
// Windows 8 futexes don't support multiple processes :(
|
||||||
if (pshare) {
|
if (pshare) goto Polyfill;
|
||||||
goto Polyfill;
|
rc = nsync_futex_wait_win32_ (w, expect, pshare, timeout);
|
||||||
}
|
|
||||||
e = errno;
|
|
||||||
if (_check_interrupts (false, 0)) {
|
|
||||||
rc = -errno;
|
|
||||||
errno = e;
|
|
||||||
} else {
|
|
||||||
if (timeout) {
|
|
||||||
ms = _timespec_tomillis (*timeout);
|
|
||||||
} else {
|
|
||||||
ms = -1;
|
|
||||||
}
|
|
||||||
if (WaitOnAddress (w, &expect, sizeof(int), ms)) {
|
|
||||||
rc = 0;
|
|
||||||
} else {
|
|
||||||
rc = -GetLastError ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (IsFreebsd ()) {
|
} else if (IsFreebsd ()) {
|
||||||
rc = sys_umtx_timedwait_uint (
|
rc = sys_umtx_timedwait_uint (w, expect, pshare, timeout);
|
||||||
w, expect, pshare, timeout);
|
|
||||||
} else {
|
} else {
|
||||||
rc = _futex (w, op, expect, timeout, 0,
|
rc = sys_futex_cp (w, op, expect, timeout, 0, FUTEX_WAIT_BITS_);
|
||||||
FUTEX_WAIT_BITS_);
|
|
||||||
if (IsOpenbsd() && rc > 0) {
|
if (IsOpenbsd() && rc > 0) {
|
||||||
rc = -rc;
|
// OpenBSD futex() returns errors as
|
||||||
|
// positive numbers without setting the
|
||||||
|
// carry flag. It's an irregular miracle
|
||||||
|
// because OpenBSD returns ECANCELED if
|
||||||
|
// futex() is interrupted w/ SA_RESTART
|
||||||
|
// so we're able to tell it apart from
|
||||||
|
// PT_MASKED which causes the wrapper
|
||||||
|
// to put ECANCELED into errno.
|
||||||
|
if (rc == ECANCELED) {
|
||||||
|
rc = EINTR;
|
||||||
|
}
|
||||||
|
errno = rc;
|
||||||
|
rc = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (rc == -1) {
|
||||||
|
rc = -errno;
|
||||||
|
errno = e;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Polyfill:
|
Polyfill:
|
||||||
__get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
|
__get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
|
||||||
|
@ -214,7 +249,6 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, struct timespec *
|
||||||
|
|
||||||
int nsync_futex_wake_ (atomic_int *w, int count, char pshare) {
|
int nsync_futex_wake_ (atomic_int *w, int count, char pshare) {
|
||||||
int e, rc, op, fop;
|
int e, rc, op, fop;
|
||||||
int wake (atomic_int *, int, int) asm ("_futex");
|
|
||||||
|
|
||||||
ASSERT (count == 1 || count == INT_MAX);
|
ASSERT (count == 1 || count == INT_MAX);
|
||||||
|
|
||||||
|
@ -240,9 +274,9 @@ int nsync_futex_wake_ (atomic_int *w, int count, char pshare) {
|
||||||
} else {
|
} else {
|
||||||
fop = UMTX_OP_WAKE_PRIVATE;
|
fop = UMTX_OP_WAKE_PRIVATE;
|
||||||
}
|
}
|
||||||
rc = sys_umtx_op (w, fop, count, 0, 0);
|
rc = _futex_wake (w, fop, count);
|
||||||
} else {
|
} else {
|
||||||
rc = wake (w, op, count);
|
rc = _futex_wake (w, op, count);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Polyfill:
|
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. │
|
│ limitations under the License. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
#include "third_party/nsync/atomic.internal.h"
|
#include "third_party/nsync/atomic.internal.h"
|
||||||
#include "third_party/nsync/common.internal.h"
|
#include "third_party/nsync/common.internal.h"
|
||||||
#include "third_party/nsync/cv.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;
|
waiter *w;
|
||||||
IGNORE_RACES_START ();
|
IGNORE_RACES_START ();
|
||||||
w = nsync_waiter_new_ ();
|
w = nsync_waiter_new_ ();
|
||||||
|
pthread_cleanup_push((void *)nsync_waiter_free_, w);
|
||||||
ATM_STORE (&w->nw.waiting, 1);
|
ATM_STORE (&w->nw.waiting, 1);
|
||||||
w->cond.f = NULL; /* Not using a conditional critical section. */
|
w->cond.f = NULL; /* Not using a conditional critical section. */
|
||||||
w->cond.v = NULL;
|
w->cond.v = NULL;
|
||||||
|
@ -312,6 +314,7 @@ int nsync_cv_wait_with_deadline_generic (nsync_cv *pcv, void *pmu,
|
||||||
(*lock) (pmu);
|
(*lock) (pmu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pthread_cleanup_pop(0);
|
||||||
IGNORE_RACES_END ();
|
IGNORE_RACES_END ();
|
||||||
return (outcome);
|
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 │
|
│ See the License for the specific language governing permissions and │
|
||||||
│ limitations under the License. │
|
│ limitations under the License. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/blockcancel.internal.h"
|
||||||
#include "third_party/nsync/atomic.h"
|
#include "third_party/nsync/atomic.h"
|
||||||
#include "third_party/nsync/common.internal.h"
|
#include "third_party/nsync/common.internal.h"
|
||||||
#include "third_party/nsync/dll.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. */
|
/* Work out in which mode the lock is held. */
|
||||||
uint32_t old_word;
|
uint32_t old_word;
|
||||||
IGNORE_RACES_START ();
|
IGNORE_RACES_START ();
|
||||||
|
BLOCK_CANCELLATIONS; /* not supported yet */
|
||||||
old_word = ATM_LOAD (&mu->word);
|
old_word = ATM_LOAD (&mu->word);
|
||||||
if ((old_word & MU_ANY_LOCK) == 0) {
|
if ((old_word & MU_ANY_LOCK) == 0) {
|
||||||
nsync_panic_ ("nsync_mu not held in some mode when calling "
|
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) {
|
if (condition_is_true) {
|
||||||
outcome = 0; /* condition is true trumps other outcomes. */
|
outcome = 0; /* condition is true trumps other outcomes. */
|
||||||
}
|
}
|
||||||
|
ALLOW_CANCELLATIONS;
|
||||||
IGNORE_RACES_END ();
|
IGNORE_RACES_END ();
|
||||||
return (outcome);
|
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 │
|
│ See the License for the specific language governing permissions and │
|
||||||
│ limitations under the License. │
|
│ limitations under the License. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/blockcancel.internal.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "third_party/nsync/atomic.h"
|
#include "third_party/nsync/atomic.h"
|
||||||
#include "third_party/nsync/atomic.internal.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 count, struct nsync_waitable_s *waitable[]) {
|
||||||
int ready;
|
int ready;
|
||||||
IGNORE_RACES_START ();
|
IGNORE_RACES_START ();
|
||||||
|
BLOCK_CANCELLATIONS; /* TODO(jart): Does this need pthread cancellations? */
|
||||||
for (ready = 0; ready != count &&
|
for (ready = 0; ready != count &&
|
||||||
nsync_time_cmp ((*waitable[ready]->funcs->ready_time) (
|
nsync_time_cmp ((*waitable[ready]->funcs->ready_time) (
|
||||||
waitable[ready]->v, NULL),
|
waitable[ready]->v, NULL),
|
||||||
|
@ -102,6 +104,7 @@ int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *),
|
||||||
(*lock) (mu);
|
(*lock) (mu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ALLOW_CANCELLATIONS;
|
||||||
IGNORE_RACES_END ();
|
IGNORE_RACES_END ();
|
||||||
return (ready);
|
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 │
|
│ See the License for the specific language governing permissions and │
|
||||||
│ limitations under the License. │
|
│ limitations under the License. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/blockcancel.internal.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "third_party/nsync/atomic.h"
|
#include "third_party/nsync/atomic.h"
|
||||||
#include "third_party/nsync/common.internal.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 wait_count;
|
||||||
uint32_t long_wait;
|
uint32_t long_wait;
|
||||||
unsigned attempts = 0; /* attempt count; used for spinloop backoff */
|
unsigned attempts = 0; /* attempt count; used for spinloop backoff */
|
||||||
|
BLOCK_CANCELLATIONS;
|
||||||
w->cv_mu = NULL; /* not a cv wait */
|
w->cv_mu = NULL; /* not a cv wait */
|
||||||
w->cond.f = NULL; /* Not using a conditional critical section. */
|
w->cond.f = NULL; /* Not using a conditional critical section. */
|
||||||
w->cond.v = NULL;
|
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,
|
if (ATM_CAS_ACQ (&mu->word, old_word,
|
||||||
(old_word+l_type->add_to_acquire) &
|
(old_word+l_type->add_to_acquire) &
|
||||||
~(clear|long_wait|l_type->clear_on_acquire))) {
|
~(clear|long_wait|l_type->clear_on_acquire))) {
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
} else if ((old_word&MU_SPINLOCK) == 0 &&
|
} else if ((old_word&MU_SPINLOCK) == 0 &&
|
||||||
ATM_CAS_ACQ (&mu->word, old_word,
|
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);
|
attempts = nsync_spin_delay_ (attempts);
|
||||||
}
|
}
|
||||||
|
ALLOW_CANCELLATIONS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Attempt to acquire *mu in writer mode without blocking, and return non-zero
|
/* 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/assert.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
|
#include "libc/intrin/weaken.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
#include "third_party/nsync/atomic.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. */
|
/* 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;
|
struct futex *f = (struct futex *) s;
|
||||||
int i;
|
int i;
|
||||||
|
int result = 0;
|
||||||
do {
|
do {
|
||||||
i = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i);
|
i = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i);
|
||||||
if (i == 0) {
|
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 ||
|
ASSERT (futex_result == 0 ||
|
||||||
futex_result == -EINTR ||
|
futex_result == -EINTR ||
|
||||||
futex_result == -EAGAIN ||
|
futex_result == -EAGAIN ||
|
||||||
|
futex_result == -ECANCELED ||
|
||||||
futex_result == -EWOULDBLOCK);
|
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:
|
/* Wait until one of:
|
||||||
the count of *s is non-zero, in which case decrement *s and return 0;
|
the count of *s is non-zero, in which case decrement *s and return 0;
|
||||||
or abs_deadline expires, in which case return ETIMEDOUT. */
|
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;
|
struct futex *f = (struct futex *)s;
|
||||||
int i;
|
int i;
|
||||||
int result = 0;
|
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 ||
|
ASSERT (futex_result == 0 ||
|
||||||
futex_result == -EINTR ||
|
futex_result == -EINTR ||
|
||||||
futex_result == -EAGAIN ||
|
futex_result == -EAGAIN ||
|
||||||
|
futex_result == -ECANCELED ||
|
||||||
futex_result == -ETIMEDOUT ||
|
futex_result == -ETIMEDOUT ||
|
||||||
futex_result == -EWOULDBLOCK);
|
futex_result == -EWOULDBLOCK);
|
||||||
/* Some systems don't wait as long as they are told. */
|
/* 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) {
|
nsync_time_cmp (abs_deadline, nsync_time_now ()) <= 0) {
|
||||||
result = ETIMEDOUT;
|
result = ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
if (futex_result == -ECANCELED) {
|
||||||
|
result = ECANCELED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} while (result == 0 && (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);
|
return (result);
|
||||||
|
|
6
third_party/nsync/mu_semaphore.h
vendored
6
third_party/nsync/mu_semaphore.h
vendored
|
@ -12,13 +12,13 @@ typedef struct nsync_semaphore_s_ {
|
||||||
void nsync_mu_semaphore_init(nsync_semaphore *s);
|
void nsync_mu_semaphore_init(nsync_semaphore *s);
|
||||||
|
|
||||||
/* Wait until the count of *s exceeds 0, and decrement it. */
|
/* 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
|
/* 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
|
decrement *s and return 0; or abs_deadline expires, in which case
|
||||||
return ETIMEDOUT. */
|
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);
|
nsync_time abs_deadline);
|
||||||
|
|
||||||
/* Ensure that the count of *s is at least 1. */
|
/* Ensure that the count of *s is at least 1. */
|
||||||
void nsync_mu_semaphore_v(nsync_semaphore *s);
|
void nsync_mu_semaphore_v(nsync_semaphore *s);
|
||||||
|
|
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` system call tracing flag to SQLite shell
|
||||||
- Added `--strace` function call logging flag to SQLite shell
|
- Added `--strace` function call logging flag to SQLite shell
|
||||||
- Configured fsync() using runtime magnums rather than ifdefs
|
- 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
|
- 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
|
#endif
|
||||||
|
|
||||||
/* Use pread() and pwrite() if they are available */
|
/* 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_PREAD 1
|
||||||
# define HAVE_PWRITE 1
|
# define HAVE_PWRITE 1
|
||||||
#endif
|
#endif
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue