diff --git a/Makefile b/Makefile index 42661a907..7f22266a0 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,9 @@ o/$(MODE): \ rw:/dev/null \ w:o/stack.log \ /etc/hosts \ - ~/.runit.psk + ~/.runit.psk \ + /proc/self/status \ + /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor PKGS = diff --git a/libc/bits/atomic.h b/libc/bits/atomic.h new file mode 100755 index 000000000..e69de29bb diff --git a/libc/intrin/describeflags.internal.h b/libc/intrin/describeflags.internal.h index 2cd1b6312..34a526948 100644 --- a/libc/intrin/describeflags.internal.h +++ b/libc/intrin/describeflags.internal.h @@ -16,6 +16,7 @@ const char *DescribeCapability(char[20], int); const char *DescribeClockName(char[32], int); const char *DescribeDirfd(char[12], int); const char *DescribeFrame(char[32], int); +const char *DescribeFutexOp(int); const char *DescribeFutexResult(char[12], int); const char *DescribeHow(char[12], int); const char *DescribeMapFlags(char[64], int); diff --git a/libc/intrin/describefutexop.c b/libc/intrin/describefutexop.c index 5cf5be3af..f956658d0 100644 --- a/libc/intrin/describefutexop.c +++ b/libc/intrin/describefutexop.c @@ -17,21 +17,15 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/describeflags.internal.h" -#include "libc/macros.internal.h" -#include "libc/nt/enum/consolemodeflags.h" #include "libc/sysv/consts/futex.h" -const char *DescribeNtFutexOp(int x) { - const struct DescribeFlags kFutexOp[] = { - {FUTEX_WAIT_PRIVATE, "WAIT_PRIVATE"}, // - {FUTEX_WAKE_PRIVATE, "WAKE_PRIVATE"}, // - {FUTEX_REQUEUE_PRIVATE, "REQUEUE_PRIVATE"}, // - {FUTEX_PRIVATE_FLAG, "PRIVATE_FLAG"}, // - {FUTEX_REQUEUE, "REQUEUE"}, // - {FUTEX_WAIT, "WAIT"}, // - {FUTEX_WAKE, "WAKE"}, // - }; - _Alignas(char) static char futexop[32]; - return DescribeFlags(futexop, sizeof(futexop), kFutexOp, ARRAYLEN(kFutexOp), - "FUTEX_", x); +const char *DescribeFutexOp(int x) { + if (x == FUTEX_WAIT) return "FUTEX_WAIT"; + if (x == FUTEX_WAKE) return "FUTEX_WAKE"; + if (x == FUTEX_REQUEUE) return "FUTEX_REQUEUE"; + // order matters (the private bit might be zero) + if (x == FUTEX_WAIT_PRIVATE) return "FUTEX_WAIT_PRIVATE"; + if (x == FUTEX_WAKE_PRIVATE) return "FUTEX_WAKE_PRIVATE"; + if (x == FUTEX_REQUEUE_PRIVATE) return "FUTEX_REQUEUE_PRIVATE"; + return "FUTEX_???"; } diff --git a/libc/intrin/futex.internal.h b/libc/intrin/futex.internal.h index b67b9014f..79840e1d3 100644 --- a/libc/intrin/futex.internal.h +++ b/libc/intrin/futex.internal.h @@ -4,8 +4,10 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -int _futex_wait(void *, int, struct timespec *) hidden; -int _futex_wake(void *, int) hidden; +int _futex_wait_public(void *, int, struct timespec *) hidden; +int _futex_wait_private(void *, int, struct timespec *) hidden; +int _futex_wake_public(void *, int) hidden; +int _futex_wake_private(void *, int) hidden; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/intrin/futex_wait.c b/libc/intrin/futex_wait.c index 6a52d708c..75f396920 100644 --- a/libc/intrin/futex_wait.c +++ b/libc/intrin/futex_wait.c @@ -20,15 +20,33 @@ #include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timespec.internal.h" #include "libc/dce.h" +#include "libc/errno.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/futex.internal.h" #include "libc/sysv/consts/futex.h" int _futex(void *, int, int, struct timespec *) hidden; -int _futex_wait(void *addr, int expect, struct timespec *timeout) { - int ax = _futex(addr, FUTEX_WAIT, expect, timeout); + +static dontinline int _futex_wait_impl(void *addr, int expect, + struct timespec *timeout, int private) { + int op, ax; + op = FUTEX_WAIT | private; + ax = _futex(addr, op, expect, timeout); + if (SupportsLinux() && private && ax == -ENOSYS) { + // RHEL5 doesn't support FUTEX_PRIVATE_FLAG + op = FUTEX_WAIT; + ax = _futex(addr, op, expect, timeout); + } if (IsOpenbsd() && ax > 0) ax = -ax; // yes openbsd does this w/o cf - STRACE("futex(%t, FUTEX_WAIT, %d, %s) → %s", addr, expect, + STRACE("futex(%t, %s, %d, %s) → %s", addr, DescribeFutexOp(op), expect, DescribeTimespec(0, timeout), DescribeFutexResult(ax)); return ax; } + +int _futex_wait_public(void *addr, int expect, struct timespec *timeout) { + return _futex_wait_impl(addr, expect, timeout, 0); +} + +int _futex_wait_private(void *addr, int expect, struct timespec *timeout) { + return _futex_wait_impl(addr, expect, timeout, FUTEX_PRIVATE_FLAG); +} diff --git a/libc/intrin/futex_wake.c b/libc/intrin/futex_wake.c index 7e1602b07..67ab9a0a7 100644 --- a/libc/intrin/futex_wake.c +++ b/libc/intrin/futex_wake.c @@ -17,14 +17,32 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/strace.internal.h" +#include "libc/dce.h" +#include "libc/errno.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/futex.internal.h" #include "libc/sysv/consts/futex.h" int _futex(void *, int, int) hidden; -int _futex_wake(void *addr, int count) { - int ax = _futex(addr, FUTEX_WAKE, count); - STRACE("futex(%t, FUTEX_WAKE, %d) → %s", addr, count, + +static dontinline int _futex_wake_impl(void *addr, int count, int private) { + int op, ax; + op = FUTEX_WAKE | private; + ax = _futex(addr, op, count); + if (SupportsLinux() && private && ax == -ENOSYS) { + // RHEL5 doesn't support FUTEX_PRIVATE_FLAG + op = FUTEX_WAKE; + ax = _futex(addr, op, count); + } + STRACE("futex(%t, %s, %d) → %s", addr, DescribeFutexOp(op), count, DescribeFutexResult(ax)); return ax; } + +int _futex_wake_public(void *addr, int count) { + return _futex_wake_impl(addr, count, 0); +} + +int _futex_wake_private(void *addr, int count) { + return _futex_wake_impl(addr, count, FUTEX_PRIVATE_FLAG); +} diff --git a/libc/intrin/intrin.h b/libc/intrin/intrin.h new file mode 100755 index 000000000..e69de29bb diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index 5e5fa616d..891ae56c0 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -73,6 +73,7 @@ o/$(MODE)/libc/intrin/kprintf.greg.o: private \ -fno-sanitize=all \ -fno-stack-protector +# TODO(jart): Do we really need these? # synchronization primitives are intended to be magic free o/$(MODE)/libc/intrin/futex_wait.o \ o/$(MODE)/libc/intrin/futex_wake.o \ diff --git a/libc/intrin/once.h b/libc/intrin/once.h index 52eb74f2d..4ce924ea5 100644 --- a/libc/intrin/once.h +++ b/libc/intrin/once.h @@ -2,6 +2,8 @@ #define COSMOPOLITAN_LIBC_INTRIN_ONCE_H_ #include "libc/intrin/spinlock.h" +/* TODO(jart): DELETE */ + #define _once(x) \ ({ \ typeof(x) oncerc; \ diff --git a/libc/intrin/pthread.h b/libc/intrin/pthread.h index 403bf6d03..d0462c25b 100644 --- a/libc/intrin/pthread.h +++ b/libc/intrin/pthread.h @@ -1,9 +1,11 @@ #ifndef COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_ #define COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_ +#define PTHREAD_ONCE_INIT 0 + #define PTHREAD_KEYS_MAX 64 -#define PTHREAD_ONCE_INIT 0 +#define PTHREAD_BARRIER_SERIAL_THREAD 31337 #define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL #define PTHREAD_MUTEX_NORMAL 0 @@ -12,18 +14,29 @@ #define PTHREAD_MUTEX_STALLED 0 #define PTHREAD_MUTEX_ROBUST 1 +#define PTHREAD_PROCESS_DEFAULT PTHREAD_PROCESS_PRIVATE +#define PTHREAD_PROCESS_PRIVATE 0 +#define PTHREAD_PROCESS_SHARED 1 + #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ /* clang-format off */ #define PTHREAD_MUTEX_INITIALIZER {PTHREAD_MUTEX_DEFAULT} -#define PTHREAD_RWLOCK_INITIALIZER {{{0}}} -#define PTHREAD_COND_INITIALIZER {{{0}}} +#define PTHREAD_COND_INITIALIZER {PTHREAD_PROCESS_DEFAULT} +#define PTHREAD_BARRIER_INITIALIZER {PTHREAD_PROCESS_DEFAULT} +#define PTHREAD_RWLOCK_INITIALIZER {PTHREAD_PROCESS_DEFAULT} /* clang-format on */ -typedef unsigned long *pthread_t; -typedef int pthread_once_t; +typedef void *pthread_t; +typedef int pthread_id_np_t; +typedef int pthread_condattr_t; +typedef int pthread_mutexattr_t; +typedef int pthread_rwlockattr_t; +typedef int pthread_barrierattr_t; typedef unsigned pthread_key_t; +typedef _Atomic(char) pthread_once_t; +typedef _Atomic(char) pthread_spinlock_t; typedef void (*pthread_key_dtor)(void *); typedef struct { @@ -35,65 +48,88 @@ typedef struct { typedef struct { int attr; -} pthread_mutexattr_t; - -typedef struct { - int attr; -} pthread_condattr_t; - -typedef struct { - int attr[2]; -} pthread_rwlockattr_t; - -typedef struct { - union { - int __i[9]; - volatile int __vi[9]; - unsigned __s[9]; - } __u; -} pthread_attr_t; - -typedef struct { - union { - int __i[12]; - volatile int __vi[12]; - void *__p[12]; - } __u; + _Atomic(int) waits; + _Atomic(unsigned) seq; } pthread_cond_t; typedef struct { - union { - int __i[8]; - volatile int __vi[8]; - void *__p[8]; - } __u; + int attr; + int count; + _Atomic(int) waits; + _Atomic(int) popped; +} pthread_barrier_t; + +typedef struct { + int attr; + _Atomic(int) lock; + _Atomic(int) waits; } pthread_rwlock_t; +typedef struct { + int scope; + int schedpolicy; + int detachstate; + int inheritsched; + size_t guardsize; + size_t stacksize; +} pthread_attr_t; + +int pthread_yield(void); void pthread_exit(void *) wontreturn; pthread_t pthread_self(void) pureconst; +pthread_id_np_t pthread_getthreadid_np(void); +int64_t pthread_getunique_np(pthread_t); +int pthread_attr_init(pthread_attr_t *); +int pthread_attr_destroy(pthread_attr_t *); +int pthread_attr_getdetachstate(const pthread_attr_t *, int *); +int pthread_attr_setdetachstate(pthread_attr_t *, int); +int pthread_attr_getguardsize(const pthread_attr_t *, size_t *); +int pthread_attr_setguardsize(pthread_attr_t *, size_t); +int pthread_attr_getinheritsched(const pthread_attr_t *, int *); +int pthread_attr_setinheritsched(pthread_attr_t *, int); +int pthread_attr_getschedpolicy(const pthread_attr_t *, int *); +int pthread_attr_setschedpolicy(pthread_attr_t *, int); +int pthread_attr_getscope(const pthread_attr_t *, int *); +int pthread_attr_setscope(pthread_attr_t *, int); +int pthread_attr_getstack(const pthread_attr_t *, void **, size_t *); +int pthread_attr_setstack(pthread_attr_t *, void *, size_t); +int pthread_attr_getstacksize(const pthread_attr_t *, size_t *); +int pthread_attr_setstacksize(pthread_attr_t *, size_t); int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *); -int pthread_yield(void); int pthread_detach(pthread_t); +int pthread_cancel(pthread_t); int pthread_join(pthread_t, void **); int pthread_equal(pthread_t, pthread_t); int pthread_once(pthread_once_t *, void (*)(void)); +int pthread_spin_init(pthread_spinlock_t *, int); +int pthread_spin_destroy(pthread_spinlock_t *); +int pthread_spin_lock(pthread_spinlock_t *); +int pthread_spin_unlock(pthread_spinlock_t *); +int pthread_spin_trylock(pthread_spinlock_t *); +int pthread_mutexattr_init(pthread_mutexattr_t *); +int pthread_mutexattr_destroy(pthread_mutexattr_t *); +int pthread_mutexattr_gettype(const pthread_mutexattr_t *, int *); +int pthread_mutexattr_settype(pthread_mutexattr_t *, int); int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *); int pthread_mutex_lock(pthread_mutex_t *); int pthread_mutex_unlock(pthread_mutex_t *); int pthread_mutex_trylock(pthread_mutex_t *); int pthread_mutex_destroy(pthread_mutex_t *); int pthread_mutex_consistent(pthread_mutex_t *); -int pthread_mutexattr_init(pthread_mutexattr_t *); -int pthread_mutexattr_destroy(pthread_mutexattr_t *); -int pthread_mutexattr_gettype(const pthread_mutexattr_t *, int *); -int pthread_mutexattr_settype(pthread_mutexattr_t *, int); +int pthread_condattr_init(pthread_condattr_t *); +int pthread_condattr_destroy(pthread_condattr_t *); +int pthread_condattr_setpshared(pthread_condattr_t *, int); +int pthread_condattr_getpshared(const pthread_condattr_t *, int *); int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *); int pthread_cond_destroy(pthread_cond_t *); int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *); int pthread_cond_broadcast(pthread_cond_t *); -int pthread_cancel(pthread_t); int pthread_cond_signal(pthread_cond_t *); +int pthread_rwlockattr_init(pthread_rwlockattr_t *); +int pthread_rwlockattr_destroy(pthread_rwlockattr_t *); +int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *, int); +int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *, int *); int pthread_rwlock_init(pthread_rwlock_t *, const pthread_rwlockattr_t *); int pthread_rwlock_destroy(pthread_rwlock_t *); int pthread_rwlock_rdlock(pthread_rwlock_t *); @@ -105,20 +141,70 @@ int pthread_key_create(pthread_key_t *, pthread_key_dtor); int pthread_key_delete(pthread_key_t); int pthread_setspecific(pthread_key_t, void *); void *pthread_getspecific(pthread_key_t); +int pthread_barrierattr_init(pthread_barrierattr_t *); +int pthread_barrierattr_destroy(pthread_barrierattr_t *); +int pthread_barrierattr_getpshared(const pthread_barrierattr_t *, int *); +int pthread_barrierattr_setpshared(pthread_barrierattr_t *, int); +int pthread_barrier_wait(pthread_barrier_t *); +int pthread_barrier_destroy(pthread_barrier_t *); +int pthread_barrier_init(pthread_barrier_t *, const pthread_barrierattr_t *, + unsigned); -#define pthread_mutexattr_init(pAttr) ((pAttr)->attr = PTHREAD_MUTEX_DEFAULT, 0) -#define pthread_mutexattr_destroy(pAttr) ((pAttr)->attr = 0) -#define pthread_mutexattr_gettype(pAttr, pType) (*(pType) = (pAttr)->attr, 0) -#define pthread_mutexattr_settype(pAttr, type) ((pAttr)->attr = type, 0) +#define pthread_spin_init(pSpin, multiprocess) (*(pSpin) = 0) +#define pthread_spin_destroy(pSpin) (*(pSpin) = 0) + +#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 +extern const errno_t EBUSY; +#define pthread_spin_unlock(pSpin) \ + (__atomic_store_n(pSpin, 0, __ATOMIC_RELAXED), 0) +#define pthread_spin_trylock(pSpin) \ + (__atomic_test_and_set(pSpin, __ATOMIC_SEQ_CST) ? EBUSY : 0) +#ifdef TINY +#define pthread_spin_lock(pSpin) __pthread_spin_lock_tiny(pSpin) +#else +#define pthread_spin_lock(pSpin) __pthread_spin_lock_cooperative(pSpin) +#endif +#define __pthread_spin_lock_tiny(pSpin) \ + ({ \ + while (__atomic_test_and_set(pSpin, __ATOMIC_SEQ_CST)) { \ + __builtin_ia32_pause(); \ + } \ + 0; \ + }) +#define __pthread_spin_lock_cooperative(pSpin) \ + ({ \ + char __x; \ + volatile int __i; \ + unsigned __tries = 0; \ + pthread_spinlock_t *__lock = pSpin; \ + for (;;) { \ + __atomic_load(__lock, &__x, __ATOMIC_RELAXED); \ + if (!__x && !__atomic_test_and_set(__lock, __ATOMIC_SEQ_CST)) { \ + break; \ + } else if (__tries < 7) { \ + for (__i = 0; __i != 1 << __tries; __i++) { \ + } \ + __tries++; \ + } else { \ + pthread_yield(); \ + } \ + } \ + 0; \ + }) +#endif /* GCC 4.7+ */ + +#define pthread_mutexattr_init(pAttr) (*(pAttr) = PTHREAD_MUTEX_DEFAULT, 0) +#define pthread_mutexattr_destroy(pAttr) (*(pAttr) = 0) +#define pthread_mutexattr_gettype(pAttr, pType) (*(pType) = *(pAttr), 0) #ifdef __GNUC__ -#define pthread_mutex_init(mutex, pAttr) \ - ({ \ - pthread_mutexattr_t *_pAttr = (pAttr); \ - *(mutex) = (pthread_mutex_t){ \ - (_pAttr) ? (_pAttr)->attr : PTHREAD_MUTEX_DEFAULT, \ - }; \ - 0; \ +#define pthread_mutex_init(mutex, pAttr) \ + ({ \ + pthread_mutexattr_t *_pAttr = (pAttr); \ + *(mutex) = (pthread_mutex_t){ \ + _pAttr ? *_pAttr : PTHREAD_MUTEX_DEFAULT, \ + }; \ + 0; \ }) #endif @@ -138,6 +224,42 @@ void *pthread_getspecific(pthread_key_t); : pthread_mutex_unlock(mutex)) #endif +#define pthread_condattr_init(pAttr) (*(pAttr) = PTHREAD_PROCESS_DEFAULT, 0) +#define pthread_condattr_destroy(pAttr) (*(pAttr) = 0) +#define pthread_condattr_getpshared(pAttr, pPshared) (*(pPshared) = *(pAttr), 0) + +#ifdef __GNUC__ +#define pthread_cond_init(cond, pAttr) \ + ({ \ + pthread_condattr_t *_pAttr = (pAttr); \ + *(cond) = (pthread_cond_t){ \ + _pAttr ? *_pAttr : PTHREAD_PROCESS_DEFAULT, \ + }; \ + 0; \ + }) +#endif + +#define pthread_barrierattr_init(pAttr) (*(pAttr) = PTHREAD_PROCESS_DEFAULT, 0) +#define pthread_barrierattr_destroy(pAttr) (*(pAttr) = 0) +#define pthread_barrierattr_getpshared(pAttr, pPshared) \ + (*(pPshared) = *(pAttr), 0) + +#define pthread_rwlockattr_init(pAttr) (*(pAttr) = PTHREAD_PROCESS_DEFAULT, 0) +#define pthread_rwlockattr_destroy(pAttr) (*(pAttr) = 0) +#define pthread_rwlockattr_getpshared(pAttr, pPshared) \ + (*(pPshared) = *(pAttr), 0) + +#ifdef __GNUC__ +#define pthread_rwlock_init(rwlock, pAttr) \ + ({ \ + pthread_rwlockattr_t *_pAttr = (pAttr); \ + *(rwlock) = (pthread_rwlock_t){ \ + _pAttr ? *_pAttr : PTHREAD_PROCESS_DEFAULT, \ + }; \ + 0; \ + }) +#endif + int _pthread_mutex_wake(pthread_mutex_t *) hidden; COSMOPOLITAN_C_END_ diff --git a/libc/intrin/pthread2.h b/libc/intrin/pthread2.h new file mode 100644 index 000000000..31319ce06 --- /dev/null +++ b/libc/intrin/pthread2.h @@ -0,0 +1,16 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN_PTHREAD2_H_ +#define COSMOPOLITAN_LIBC_INTRIN_PTHREAD2_H_ +#include "libc/calls/struct/sched_param.h" +#include "libc/calls/struct/timespec.h" +#include "libc/intrin/pthread.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +int pthread_attr_getschedparam(const pthread_attr_t *, struct sched_param *); +int pthread_attr_setschedparam(pthread_attr_t *, const struct sched_param *); +int pthread_cond_timedwait(pthread_cond_t *, pthread_mutex_t *, + const struct timespec *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_INTRIN_PTHREAD2_H_ */ diff --git a/libc/intrin/pthread_attr_destroy.c b/libc/intrin/pthread_attr_destroy.c new file mode 100644 index 000000000..59c1e7e71 --- /dev/null +++ b/libc/intrin/pthread_attr_destroy.c @@ -0,0 +1,28 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" +#include "libc/str/str.h" + +/** + * Destroys pthread attributes. + */ +int pthread_attr_destroy(pthread_attr_t *attr) { + bzero(attr, sizeof(*attr)); + return 0; +} diff --git a/libc/intrin/pthread_attr_getdetachstate.c b/libc/intrin/pthread_attr_getdetachstate.c new file mode 100644 index 000000000..b229aa8aa --- /dev/null +++ b/libc/intrin/pthread_attr_getdetachstate.c @@ -0,0 +1,24 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +int pthread_attr_getdetachstate(const pthread_attr_t *a, int *x) { + *x = a->detachstate; + return 0; +} diff --git a/libc/intrin/pthread_attr_getguardsize.c b/libc/intrin/pthread_attr_getguardsize.c new file mode 100644 index 000000000..94a5d25ea --- /dev/null +++ b/libc/intrin/pthread_attr_getguardsize.c @@ -0,0 +1,24 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +int pthread_attr_getguardsize(const pthread_attr_t *a, size_t *x) { + *x = a->guardsize; + return 0; +} diff --git a/libc/intrin/pthread_attr_getscope.c b/libc/intrin/pthread_attr_getscope.c new file mode 100644 index 000000000..fa24f5432 --- /dev/null +++ b/libc/intrin/pthread_attr_getscope.c @@ -0,0 +1,24 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +int pthread_attr_getscope(const pthread_attr_t *a, int *x) { + *x = a->scope; + return 0; +} diff --git a/libc/intrin/pthread_attr_getstacksize.c b/libc/intrin/pthread_attr_getstacksize.c new file mode 100644 index 000000000..a20785dc0 --- /dev/null +++ b/libc/intrin/pthread_attr_getstacksize.c @@ -0,0 +1,24 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +int pthread_attr_getstacksize(const pthread_attr_t *a, size_t *x) { + *x = a->stacksize; + return 0; +} diff --git a/libc/intrin/pthread_attr_init.c b/libc/intrin/pthread_attr_init.c new file mode 100644 index 000000000..fc674e5ad --- /dev/null +++ b/libc/intrin/pthread_attr_init.c @@ -0,0 +1,28 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" +#include "libc/str/str.h" + +/** + * Initializes pthread attributes. + */ +int pthread_attr_init(pthread_attr_t *attr) { + bzero(attr, sizeof(*attr)); + return 0; +} diff --git a/libc/intrin/pthread_attr_setdetachstate.c b/libc/intrin/pthread_attr_setdetachstate.c new file mode 100644 index 000000000..84d0d2345 --- /dev/null +++ b/libc/intrin/pthread_attr_setdetachstate.c @@ -0,0 +1,24 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +int pthread_attr_setdetachstate(pthread_attr_t *a, int x) { + a->detachstate = x; + return 0; +} diff --git a/libc/intrin/pthread_attr_setguardsize.c b/libc/intrin/pthread_attr_setguardsize.c new file mode 100644 index 000000000..f1e19a1b5 --- /dev/null +++ b/libc/intrin/pthread_attr_setguardsize.c @@ -0,0 +1,24 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +int pthread_attr_setguardsize(pthread_attr_t *a, size_t x) { + a->guardsize = x; + return 0; +} diff --git a/libc/intrin/pthread_attr_setscope.c b/libc/intrin/pthread_attr_setscope.c new file mode 100644 index 000000000..365a40120 --- /dev/null +++ b/libc/intrin/pthread_attr_setscope.c @@ -0,0 +1,24 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +int pthread_attr_setscope(pthread_attr_t *a, int x) { + a->scope = x; + return 0; +} diff --git a/libc/intrin/pthread_attr_setstacksize.c b/libc/intrin/pthread_attr_setstacksize.c new file mode 100644 index 000000000..5ab1e5a6d --- /dev/null +++ b/libc/intrin/pthread_attr_setstacksize.c @@ -0,0 +1,24 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +int pthread_attr_setstacksize(pthread_attr_t *a, size_t x) { + a->stacksize = x; + return 0; +} diff --git a/libc/intrin/pthread_barrier_destroy.c b/libc/intrin/pthread_barrier_destroy.c new file mode 100644 index 000000000..502a6e64b --- /dev/null +++ b/libc/intrin/pthread_barrier_destroy.c @@ -0,0 +1,36 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/assert.h" +#include "libc/errno.h" +#include "libc/intrin/pthread.h" + +/** + * Destroys barrier. + * + * @return 0 on success, or error on failure + * @raise EINVAL if threads are still inside the barrier + */ +int pthread_barrier_destroy(pthread_barrier_t *barrier) { + if (barrier->waits || barrier->popped) { + assert(!"deadlock"); + return EINVAL; + } + *barrier = (pthread_barrier_t)PTHREAD_BARRIER_INITIALIZER; + return 0; +} diff --git a/libc/intrin/pthread_barrier_init.c b/libc/intrin/pthread_barrier_init.c new file mode 100644 index 000000000..f6ea185b8 --- /dev/null +++ b/libc/intrin/pthread_barrier_init.c @@ -0,0 +1,45 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/assert.h" +#include "libc/errno.h" +#include "libc/intrin/pthread.h" +#include "libc/limits.h" + +/** + * Initializes barrier. + * + * @param attr may be null + * @param count is how many threads need to call pthread_barrier_wait() + * before the barrier is released, which must be greater than zero + * @return 0 on success, or error number on failure + * @raise EINVAL if `count` isn't greater than zero or overflows + */ +int pthread_barrier_init(pthread_barrier_t *barrier, + const pthread_barrierattr_t *attr, unsigned count) { + if (count && count < INT_MAX / 2) { + *barrier = (pthread_barrier_t){ + attr ? *attr : PTHREAD_PROCESS_DEFAULT, + count, + }; + return 0; + } else { + assert(!"bad count"); + return EINVAL; + } +} diff --git a/libc/intrin/pthread_barrier_wait.c b/libc/intrin/pthread_barrier_wait.c new file mode 100644 index 000000000..f5aea8782 --- /dev/null +++ b/libc/intrin/pthread_barrier_wait.c @@ -0,0 +1,74 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/calls/calls.h" +#include "libc/dce.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/intrin.h" +#include "libc/intrin/kprintf.h" +#include "libc/intrin/pthread.h" +#include "libc/limits.h" + +/** + * Waits for all threads to arrive at barrier. + * + * When the barrier is broken, the state becomes reset to what it was + * when pthread_barrier_init() was called, so that the barrior may be + * used again in the same way. The last thread to arrive shall be the + * last to leave and it returns a magic value. + * + * @return 0 on success, `PTHREAD_BARRIER_SERIAL_THREAD` to one lucky + * thread which was the last arrival, or an errno on error + */ +int pthread_barrier_wait(pthread_barrier_t *barrier) { + if (atomic_fetch_add(&barrier->waits, 1) + 1 == barrier->count) { + if (atomic_fetch_add(&barrier->waits, 1) + 1 < barrier->count * 2) { + atomic_store(&barrier->popped, 1); + do { + if (IsLinux() || IsOpenbsd()) { + if (barrier->attr == PTHREAD_PROCESS_SHARED) { + _futex_wake_public(&barrier->popped, INT_MAX); + } else { + _futex_wake_private(&barrier->popped, INT_MAX); + } + } else { + pthread_yield(); + } + } while (atomic_load_explicit(&barrier->waits, memory_order_relaxed) < + barrier->count * 2); + atomic_store_explicit(&barrier->popped, 0, memory_order_relaxed); + } + atomic_store_explicit(&barrier->waits, 0, memory_order_relaxed); + return PTHREAD_BARRIER_SERIAL_THREAD; + } + do { + if (IsLinux() || IsOpenbsd()) { + if (barrier->attr == PTHREAD_PROCESS_SHARED) { + _futex_wait_public(&barrier->popped, 0, 0); + } else { + _futex_wait_private(&barrier->popped, 0, 0); + } + } else { + pthread_yield(); + } + } while (atomic_load_explicit(&barrier->waits, memory_order_relaxed) < + barrier->count); + atomic_fetch_add(&barrier->waits, 1); + return 0; +} diff --git a/libc/intrin/pthread_barrierattr_destroy.c b/libc/intrin/pthread_barrierattr_destroy.c new file mode 100644 index 000000000..f0cc9598a --- /dev/null +++ b/libc/intrin/pthread_barrierattr_destroy.c @@ -0,0 +1,28 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Destroys barrier attributes. + * + * @return 0 on success, or error on failure + */ +int(pthread_barrierattr_destroy)(pthread_barrierattr_t *attr) { + return pthread_barrierattr_destroy(attr); +} diff --git a/libc/intrin/pthread_barrierattr_getpshared.c b/libc/intrin/pthread_barrierattr_getpshared.c new file mode 100644 index 000000000..5a432d434 --- /dev/null +++ b/libc/intrin/pthread_barrierattr_getpshared.c @@ -0,0 +1,32 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Gets barrier process sharing. + * + * @param pshared is set to one of the following + * - `PTHREAD_PROCESS_SHARED` + * - `PTHREAD_PROCESS_PRIVATE` + * @return 0 on success, or error on failure + */ +int(pthread_barrierattr_getpshared)(const pthread_barrierattr_t *attr, + int *pshared) { + return pthread_barrierattr_getpshared(attr, pshared); +} diff --git a/libc/intrin/pthread_barrierattr_init.c b/libc/intrin/pthread_barrierattr_init.c new file mode 100644 index 000000000..20083ec15 --- /dev/null +++ b/libc/intrin/pthread_barrierattr_init.c @@ -0,0 +1,28 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Initializes barrier attributes. + * + * @return 0 on success, or error on failure + */ +int(pthread_barrierattr_init)(pthread_barrierattr_t *attr) { + return pthread_barrierattr_init(attr); +} diff --git a/libc/intrin/pthread_barrierattr_setpshared.c b/libc/intrin/pthread_barrierattr_setpshared.c new file mode 100644 index 000000000..d10ee6aac --- /dev/null +++ b/libc/intrin/pthread_barrierattr_setpshared.c @@ -0,0 +1,40 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/errno.h" +#include "libc/intrin/pthread.h" + +/** + * Sets barrier process sharing. + * + * @param pshared can be one of + * - `PTHREAD_PROCESS_SHARED` + * - `PTHREAD_PROCESS_PRIVATE` + * @return 0 on success, or error on failure + * @raises EINVAL if `pshared` is invalid + */ +int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared) { + switch (pshared) { + case PTHREAD_PROCESS_SHARED: + case PTHREAD_PROCESS_PRIVATE: + *attr = pshared; + return 0; + default: + return EINVAL; + } +} diff --git a/libc/intrin/pthread_cond_broadcast.c b/libc/intrin/pthread_cond_broadcast.c new file mode 100644 index 000000000..cafd530c1 --- /dev/null +++ b/libc/intrin/pthread_cond_broadcast.c @@ -0,0 +1,100 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/dce.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" +#include "libc/limits.h" + +static dontinline int pthread_cond_signal_impl(pthread_cond_t *cond, int n) { + if (atomic_load_explicit(&cond->waits, memory_order_relaxed)) { + atomic_fetch_add(&cond->seq, 1); + if (IsLinux() || IsOpenbsd()) { + if (cond->attr == PTHREAD_PROCESS_SHARED) { + _futex_wake_public(&cond->seq, n); + } else { + _futex_wake_private(&cond->seq, n); + } + } + } + return 0; +} + +/** + * Wakes at least one thread waiting on condition, e.g. + * + * pthread_cond_t cond = PTHREAD_COND_INITIALIZER; + * pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + * + * // thread pool waiters + * pthread_mutex_lock(&lock); + * pthread_cond_wait(&cond, &lock); + * pthread_mutex_unlock(&lock); + * + * // waker upper + * pthread_mutex_lock(&lock); + * pthread_cond_signal(&cond); + * pthread_mutex_unlock(&lock); + * + * This function has no effect if there aren't any threads currently + * waiting on the condition. + * + * @return 0 on success, or errno on error + * @see pthread_cond_broadcast + * @see pthread_cond_wait + */ +int pthread_cond_signal(pthread_cond_t *cond) { + return pthread_cond_signal_impl(cond, 1); +} + +/** + * Wakes all threads waiting on condition, e.g. + * + * pthread_mutex_t lock; + * pthread_mutexattr_t mattr; + * pthread_mutexattr_init(&mattr); + * pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ERRORCHECK); + * pthread_mutex_init(&lock, &mattr); + * + * pthread_cond_t cond; + * pthread_condattr_t cattr; + * pthread_condattr_init(&cattr); + * pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED); + * pthread_cond_init(&cond, &cattr); + * + * // waiting threads + * CHECK_EQ(0, pthread_mutex_lock(&lock)); + * CHECK_EQ(0, pthread_cond_wait(&cond, &lock)); + * pthread_mutex_unlock(&lock); + * + * // notifying thread + * CHECK_EQ(0, pthread_mutex_lock(&lock)); + * pthread_cond_broadcast(&cond); + * pthread_mutex_unlock(&lock); + * + * This function has no effect if there aren't any threads currently + * waiting on the condition. + * + * @return 0 on success, or errno on error + * @see pthread_cond_signal + * @see pthread_cond_wait + */ +int pthread_cond_broadcast(pthread_cond_t *cond) { + return pthread_cond_signal_impl(cond, INT_MAX); +} diff --git a/libc/intrin/pthread_cond_destroy.c b/libc/intrin/pthread_cond_destroy.c new file mode 100644 index 000000000..c06653784 --- /dev/null +++ b/libc/intrin/pthread_cond_destroy.c @@ -0,0 +1,36 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/assert.h" +#include "libc/errno.h" +#include "libc/intrin/pthread.h" + +/** + * Destroys condition. + * + * @return 0 on success, or error number on failure + * @raise EINVAL if threads are still waiting on condition + */ +int pthread_cond_destroy(pthread_cond_t *cond) { + if (cond->waits) { + assert(!"deadlock"); + return EINVAL; + } + *cond = (pthread_cond_t)PTHREAD_COND_INITIALIZER; + return 0; +} diff --git a/libc/intrin/pthread_cond_init.c b/libc/intrin/pthread_cond_init.c new file mode 100644 index 000000000..6a57842b3 --- /dev/null +++ b/libc/intrin/pthread_cond_init.c @@ -0,0 +1,29 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Initializes condition. + * + * @param attr may be null + * @return 0 on success, or error number on failure + */ +int(pthread_cond_init)(pthread_cond_t *cond, const pthread_condattr_t *attr) { + return pthread_cond_init(cond, attr); +} diff --git a/libc/intrin/pthread_condattr_destroy.c b/libc/intrin/pthread_condattr_destroy.c new file mode 100644 index 000000000..509fd64ef --- /dev/null +++ b/libc/intrin/pthread_condattr_destroy.c @@ -0,0 +1,28 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Destroys condition attributes. + * + * @return 0 on success, or error on failure + */ +int(pthread_condattr_destroy)(pthread_condattr_t *attr) { + return pthread_condattr_destroy(attr); +} diff --git a/libc/intrin/pthread_condattr_getpshared.c b/libc/intrin/pthread_condattr_getpshared.c new file mode 100644 index 000000000..a1ef93147 --- /dev/null +++ b/libc/intrin/pthread_condattr_getpshared.c @@ -0,0 +1,31 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Gets condition process sharing. + * + * @param pshared is set to one of the following + * - `PTHREAD_PROCESS_SHARED` + * - `PTHREAD_PROCESS_PRIVATE` + * @return 0 on success, or error on failure + */ +int(pthread_condattr_getpshared)(const pthread_condattr_t *attr, int *pshared) { + return pthread_condattr_getpshared(attr, pshared); +} diff --git a/libc/intrin/pthread_condattr_init.c b/libc/intrin/pthread_condattr_init.c new file mode 100644 index 000000000..d0a5f90e0 --- /dev/null +++ b/libc/intrin/pthread_condattr_init.c @@ -0,0 +1,28 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Initializes condition attributes. + * + * @return 0 on success, or error on failure + */ +int(pthread_condattr_init)(pthread_condattr_t *attr) { + return pthread_condattr_init(attr); +} diff --git a/libc/intrin/pthread_condattr_setpshared.c b/libc/intrin/pthread_condattr_setpshared.c new file mode 100644 index 000000000..9c8929a27 --- /dev/null +++ b/libc/intrin/pthread_condattr_setpshared.c @@ -0,0 +1,40 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/errno.h" +#include "libc/intrin/pthread.h" + +/** + * Sets condition process sharing. + * + * @param pshared can be one of + * - `PTHREAD_PROCESS_SHARED` + * - `PTHREAD_PROCESS_PRIVATE` + * @return 0 on success, or error on failure + * @raises EINVAL if `pshared` is invalid + */ +int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) { + switch (pshared) { + case PTHREAD_PROCESS_SHARED: + case PTHREAD_PROCESS_PRIVATE: + *attr = pshared; + return 0; + default: + return EINVAL; + } +} diff --git a/libc/intrin/pthread_mutex_destroy.c b/libc/intrin/pthread_mutex_destroy.c index 18c8636dc..2bbc2c169 100644 --- a/libc/intrin/pthread_mutex_destroy.c +++ b/libc/intrin/pthread_mutex_destroy.c @@ -19,20 +19,18 @@ #include "libc/assert.h" #include "libc/errno.h" #include "libc/intrin/pthread.h" -#include "libc/str/str.h" /** * Destroys mutex. + * * @return 0 on success, or error number on failure + * @raise EINVAL if mutex is locked in our implementation */ int pthread_mutex_destroy(pthread_mutex_t *mutex) { - int rc; - if (!mutex->lock && !mutex->waits) { - rc = 0; - } else { - assert(!"dead lock"); - rc = EDEADLK; + if (mutex->lock || mutex->waits) { + assert(!"deadlock"); + return EINVAL; } - bzero(mutex, sizeof(*mutex)); + *mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER; return 0; } diff --git a/libc/intrin/pthread_mutex_init.c b/libc/intrin/pthread_mutex_init.c index b72e6be5c..24f6dc4eb 100644 --- a/libc/intrin/pthread_mutex_init.c +++ b/libc/intrin/pthread_mutex_init.c @@ -21,12 +21,11 @@ /** * Initializes mutex. - * @param attr may be NULL + * + * @param attr may be null * @return 0 on success, or error number on failure */ int(pthread_mutex_init)(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) { - bzero(mutex, sizeof(*mutex)); - mutex->attr = attr ? attr->attr : PTHREAD_MUTEX_DEFAULT; - return 0; + return pthread_mutex_init(mutex, attr); } diff --git a/libc/intrin/pthread_mutex_lock.c b/libc/intrin/pthread_mutex_lock.c index 686e4aaf1..b45d3881a 100644 --- a/libc/intrin/pthread_mutex_lock.c +++ b/libc/intrin/pthread_mutex_lock.c @@ -17,11 +17,11 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/intrin/asmflag.h" -#include "libc/intrin/atomic.h" #include "libc/calls/calls.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/asmflag.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/futex.internal.h" #include "libc/intrin/pthread.h" #include "libc/intrin/spinlock.h" @@ -39,10 +39,10 @@ static int pthread_mutex_lock_spin(pthread_mutex_t *mutex, int expect, tries++; } else if (IsLinux() || IsOpenbsd()) { atomic_fetch_add(&mutex->waits, 1); - _futex_wait(&mutex->lock, expect, &(struct timespec){1}); + _futex_wait_private(&mutex->lock, expect, &(struct timespec){1}); atomic_fetch_sub(&mutex->waits, 1); } else { - sched_yield(); + pthread_yield(); } return tries; } @@ -50,12 +50,48 @@ static int pthread_mutex_lock_spin(pthread_mutex_t *mutex, int expect, /** * Locks mutex. * - * _spinlock() l: 181,570c 58,646ns + * spin l: 181,570c 58,646ns * mutex normal l: 297,965c 96,241ns * mutex recursive l: 1,112,166c 359,223ns * mutex errorcheck l: 1,449,723c 468,252ns * + * Here's an example of using a normal mutex: + * + * pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + * pthread_mutex_lock(&lock); + * // do work... + * pthread_mutex_unlock(&lock); + * pthread_mutex_destroy(&lock); + * + * Cosmopolitan permits succinct notation for normal mutexes: + * + * pthread_mutex_t lock = {0}; + * pthread_mutex_lock(&lock); + * // do work... + * pthread_mutex_unlock(&lock); + * + * Here's an example of the proper way to do recursive mutexes: + * + * pthread_mutex_t lock; + * pthread_mutexattr_t attr; + * pthread_mutexattr_init(&attr); + * pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + * pthread_mutex_init(&lock, &attr); + * pthread_mutexattr_destroy(&attr); + * pthread_mutex_lock(&lock); + * // do work... + * pthread_mutex_unlock(&lock); + * pthread_mutex_destroy(&lock); + * + * Alternatively, Cosmopolitan lets you do the folllowing instead: + * + * pthread_mutex_t lock = {PTHREAD_MUTEX_RECURSIVE}; + * pthread_mutex_lock(&lock); + * // do work... + * pthread_mutex_unlock(&lock); + * * @return 0 on success, or error number on failure + * @see pthread_spin_lock */ int(pthread_mutex_lock)(pthread_mutex_t *mutex) { int me, owner, tries; @@ -81,7 +117,7 @@ int(pthread_mutex_lock)(pthread_mutex_t *mutex) { if (mutex->attr != PTHREAD_MUTEX_ERRORCHECK) { break; } else { - assert(!"dead lock"); + assert(!"deadlock"); return EDEADLK; } } @@ -90,7 +126,7 @@ int(pthread_mutex_lock)(pthread_mutex_t *mutex) { ++mutex->reent; return 0; default: - assert(!"inva lock"); + assert(!"badlock"); return EINVAL; } } diff --git a/libc/intrin/pthread_mutex_unlock.c b/libc/intrin/pthread_mutex_unlock.c index 2f03a4464..dbe8811b5 100644 --- a/libc/intrin/pthread_mutex_unlock.c +++ b/libc/intrin/pthread_mutex_unlock.c @@ -18,13 +18,13 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/calls.h" -#include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/atomic.h" #include "libc/intrin/pthread.h" /** * Releases mutex. + * * @return 0 on success or error number on failure * @raises EPERM if in error check mode and not owned by caller */ @@ -35,7 +35,7 @@ int(pthread_mutex_unlock)(pthread_mutex_t *mutex) { me = gettid(); owner = atomic_load_explicit(&mutex->lock, memory_order_relaxed); if (owner != me) { - assert(!"perm lock"); + assert(!"permlock"); return EPERM; } // fallthrough @@ -49,7 +49,7 @@ int(pthread_mutex_unlock)(pthread_mutex_t *mutex) { } return 0; default: - assert(!"inva lock"); + assert(!"badlock"); return EINVAL; } } diff --git a/libc/intrin/pthread_mutex_wake.c b/libc/intrin/pthread_mutex_wake.c index 4a7a42f6a..5060e035d 100644 --- a/libc/intrin/pthread_mutex_wake.c +++ b/libc/intrin/pthread_mutex_wake.c @@ -16,15 +16,15 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/atomic.h" #include "libc/dce.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/futex.internal.h" #include "libc/intrin/pthread.h" int _pthread_mutex_wake(pthread_mutex_t *mutex) { if ((IsLinux() || IsOpenbsd()) && atomic_load_explicit(&mutex->waits, memory_order_relaxed)) { - return _futex_wake(&mutex->lock, 1); + return _futex_wake_private(&mutex->lock, 1); } else { return 0; } diff --git a/libc/intrin/pthread_mutexattr_destroy.c b/libc/intrin/pthread_mutexattr_destroy.c index de018f49f..754065950 100644 --- a/libc/intrin/pthread_mutexattr_destroy.c +++ b/libc/intrin/pthread_mutexattr_destroy.c @@ -23,6 +23,5 @@ * @return 0 on success, or error number on failure */ int(pthread_mutexattr_destroy)(pthread_mutexattr_t *attr) { - attr->attr = 0; - return 0; + return pthread_mutexattr_destroy(attr); } diff --git a/libc/intrin/pthread_mutexattr_gettype.c b/libc/intrin/pthread_mutexattr_gettype.c index cb64d118d..6fbae531c 100644 --- a/libc/intrin/pthread_mutexattr_gettype.c +++ b/libc/intrin/pthread_mutexattr_gettype.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/errno.h" #include "libc/intrin/pthread.h" /** @@ -30,6 +29,5 @@ * @return 0 on success, or error on failure */ int(pthread_mutexattr_gettype)(const pthread_mutexattr_t *attr, int *type) { - *type = attr->attr; - return 0; + return pthread_mutexattr_gettype(attr, type); } diff --git a/libc/intrin/pthread_mutexattr_init.c b/libc/intrin/pthread_mutexattr_init.c index a1c0caa95..10856580d 100644 --- a/libc/intrin/pthread_mutexattr_init.c +++ b/libc/intrin/pthread_mutexattr_init.c @@ -23,6 +23,5 @@ * @return 0 on success, or error number on failure */ int(pthread_mutexattr_init)(pthread_mutexattr_t *attr) { - attr->attr = PTHREAD_MUTEX_DEFAULT; - return 0; + return pthread_mutexattr_init(attr); } diff --git a/libc/intrin/pthread_mutexattr_settype.c b/libc/intrin/pthread_mutexattr_settype.c index 79a92ebf2..9f875881c 100644 --- a/libc/intrin/pthread_mutexattr_settype.c +++ b/libc/intrin/pthread_mutexattr_settype.c @@ -30,12 +30,12 @@ * @return 0 on success, or error on failure * @raises EINVAL if `type` is invalid */ -int(pthread_mutexattr_settype)(pthread_mutexattr_t *attr, int type) { +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) { switch (type) { case PTHREAD_MUTEX_NORMAL: case PTHREAD_MUTEX_RECURSIVE: case PTHREAD_MUTEX_ERRORCHECK: - attr->attr = type; + *attr = type; return 0; default: return EINVAL; diff --git a/libc/intrin/pthread_once.c b/libc/intrin/pthread_once.c index 1b96a5b81..d7881043d 100644 --- a/libc/intrin/pthread_once.c +++ b/libc/intrin/pthread_once.c @@ -16,33 +16,55 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/atomic.h" +#include "libc/assert.h" #include "libc/calls/calls.h" +#include "libc/errno.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/pthread.h" +#define INIT 0 +#define CALLING 1 +#define FINISHED 2 + +/** + * Ensures initialization function is called exactly once, e.g. + * + * static void *g_factory; + * + * static void InitFactory(void) { + * g_factory = expensive(); + * } + * + * void *GetFactory(void) { + * static pthread_once_t once = PTHREAD_ONCE_INIT; + * pthread_once(&once, InitFactory); + * return g_factory; + * } + * + * @return 0 on success, or errno on error + */ int pthread_once(pthread_once_t *once, void init(void)) { - int x; - unsigned tries; - switch ((x = atomic_load(once))) { - case 0: - if (atomic_compare_exchange_strong(once, &x, 1)) { + char old; + switch ((old = atomic_load_explicit(once, memory_order_relaxed))) { + case INIT: + if (atomic_compare_exchange_strong_explicit(once, &old, CALLING, + memory_order_acquire, + memory_order_relaxed)) { init(); - atomic_store(once, 2); + atomic_store(once, FINISHED); break; } // fallthrough - case 1: - tries = 0; + case CALLING: do { - if (++tries & 7) { - __builtin_ia32_pause(); - } else { - sched_yield(); - } - } while (atomic_load(once) == 1); + pthread_yield(); + } while (atomic_load_explicit(once, memory_order_relaxed) == CALLING); + break; + case FINISHED: break; default: - break; + assert(!"bad once"); + return EINVAL; } return 0; } diff --git a/libc/intrin/pthread_rwlock_destroy.c b/libc/intrin/pthread_rwlock_destroy.c new file mode 100644 index 000000000..9135b46d0 --- /dev/null +++ b/libc/intrin/pthread_rwlock_destroy.c @@ -0,0 +1,36 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/assert.h" +#include "libc/errno.h" +#include "libc/intrin/pthread.h" + +/** + * Destroys read-write lock. + * + * @return 0 on success, or error number on failure + * @raise EINVAL if any threads still hold the lock + */ +int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) { + if (rwlock->lock) { + assert(!"deadlock"); + return EINVAL; + } + *rwlock = (pthread_rwlock_t)PTHREAD_RWLOCK_INITIALIZER; + return 0; +} diff --git a/libc/intrin/pthread_rwlock_init.c b/libc/intrin/pthread_rwlock_init.c new file mode 100644 index 000000000..71a0198b3 --- /dev/null +++ b/libc/intrin/pthread_rwlock_init.c @@ -0,0 +1,30 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Initializes read-write lock. + * + * @param attr may be null + * @return 0 on success, or error number on failure + */ +int(pthread_rwlock_init)(pthread_rwlock_t *rwlock, + const pthread_rwlockattr_t *attr) { + return pthread_rwlock_init(rwlock, attr); +} diff --git a/libc/intrin/pthread_rwlock_rdlock.c b/libc/intrin/pthread_rwlock_rdlock.c new file mode 100644 index 000000000..0ecbf3be5 --- /dev/null +++ b/libc/intrin/pthread_rwlock_rdlock.c @@ -0,0 +1,61 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/dce.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" + +static int pthread_rwlock_rdlock_spin(pthread_rwlock_t *rwlock, int expect, + int tries) { + if (tries < 7) { + volatile int i; + for (i = 0; i != 1 << tries; i++) { + } + tries++; + } else if (IsLinux() || IsOpenbsd()) { + atomic_fetch_add(&rwlock->waits, 1); + _futex_wait_private(&rwlock->lock, expect, &(struct timespec){1}); + atomic_fetch_sub(&rwlock->waits, 1); + } else { + pthread_yield(); + } + return tries; +} + +/** + * Acquires read lock on read-write lock. + * + * @return 0 on success, or errno on error + */ +int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) { + int old, tries; + for (tries = 0;;) { + old = atomic_load_explicit(&rwlock->lock, memory_order_relaxed); + if (old >= 0) { + do { + if (atomic_compare_exchange_weak_explicit(&rwlock->lock, &old, old + 1, + memory_order_acquire, + memory_order_relaxed)) { + return 0; + } + } while (old >= 0); + } + tries = pthread_rwlock_rdlock_spin(rwlock, old, tries); + } +} diff --git a/libc/intrin/pthread_rwlock_unlock.c b/libc/intrin/pthread_rwlock_unlock.c new file mode 100644 index 000000000..72dbef1e3 --- /dev/null +++ b/libc/intrin/pthread_rwlock_unlock.c @@ -0,0 +1,60 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/assert.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/intrin.h" +#include "libc/intrin/pthread.h" + +/** + * Unlocks read-write lock. + * + * @return 0 on success, or errno on error + * @raise EINVAL if lock is in a bad state + */ +int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) { + int old, waits; + for (;;) { + old = atomic_load_explicit(&rwlock->lock, memory_order_relaxed); + if (!old || old < -1) { + assert(!"badlock"); + return EINVAL; + } else if (old == -1 || old == 1) { + waits = atomic_load_explicit(&rwlock->waits, memory_order_relaxed); + if (atomic_compare_exchange_weak_explicit(&rwlock->lock, &old, 0, + memory_order_acquire, + memory_order_relaxed)) { + if (waits && (IsLinux() || IsOpenbsd())) { + if (rwlock->attr == PTHREAD_PROCESS_SHARED) { + _futex_wake_public(&rwlock->lock, 1); + } else { + _futex_wake_private(&rwlock->lock, 1); + } + } + return 0; + } + } else if (atomic_compare_exchange_weak_explicit( + &rwlock->lock, &old, old - 1, memory_order_acquire, + memory_order_relaxed)) { + return 0; + } + } +} diff --git a/libc/intrin/pthread_rwlock_wrlock.c b/libc/intrin/pthread_rwlock_wrlock.c new file mode 100644 index 000000000..1e2e9b332 --- /dev/null +++ b/libc/intrin/pthread_rwlock_wrlock.c @@ -0,0 +1,60 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/dce.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" + +static int pthread_rwlock_wrlock_spin(pthread_rwlock_t *rwlock, int expect, + int tries) { + if (tries < 7) { + volatile int i; + for (i = 0; i != 1 << tries; i++) { + } + tries++; + } else if (IsLinux() || IsOpenbsd()) { + atomic_fetch_add(&rwlock->waits, 1); + _futex_wait_private(&rwlock->lock, expect, &(struct timespec){1}); + atomic_fetch_sub(&rwlock->waits, 1); + } else { + pthread_yield(); + } + return tries; +} + +/** + * Acquires write lock on read-write lock. + * + * @return 0 on success, or errno on error + */ +int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) { + int old, tries; + for (tries = 0;;) { + if (!(old = atomic_load_explicit(&rwlock->lock, memory_order_relaxed))) { + do { + if (atomic_compare_exchange_weak_explicit(&rwlock->lock, &old, -1, + memory_order_acquire, + memory_order_relaxed)) { + return 0; + } + } while (!old); + } + tries = pthread_rwlock_wrlock_spin(rwlock, old, tries); + } +} diff --git a/libc/intrin/pthread_rwlockattr_destroy.c b/libc/intrin/pthread_rwlockattr_destroy.c new file mode 100644 index 000000000..4cd78e5f4 --- /dev/null +++ b/libc/intrin/pthread_rwlockattr_destroy.c @@ -0,0 +1,28 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Destroys read-write lock attributes. + * + * @return 0 on success, or error on failure + */ +int(pthread_rwlockattr_destroy)(pthread_rwlockattr_t *attr) { + return pthread_rwlockattr_destroy(attr); +} diff --git a/libc/intrin/pthread_rwlockattr_getpshared.c b/libc/intrin/pthread_rwlockattr_getpshared.c new file mode 100644 index 000000000..6f4d91e51 --- /dev/null +++ b/libc/intrin/pthread_rwlockattr_getpshared.c @@ -0,0 +1,32 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Gets read-write lock process sharing. + * + * @param pshared is set to one of the following + * - `PTHREAD_PROCESS_SHARED` + * - `PTHREAD_PROCESS_PRIVATE` + * @return 0 on success, or error on failure + */ +int(pthread_rwlockattr_getpshared)(const pthread_rwlockattr_t *attr, + int *pshared) { + return pthread_rwlockattr_getpshared(attr, pshared); +} diff --git a/libc/intrin/pthread_rwlockattr_init.c b/libc/intrin/pthread_rwlockattr_init.c new file mode 100644 index 000000000..adca969f1 --- /dev/null +++ b/libc/intrin/pthread_rwlockattr_init.c @@ -0,0 +1,28 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Initializes read-write lock attributes. + * + * @return 0 on success, or error on failure + */ +int(pthread_rwlockattr_init)(pthread_rwlockattr_t *attr) { + return pthread_rwlockattr_init(attr); +} diff --git a/libc/intrin/pthread_rwlockattr_setpshared.c b/libc/intrin/pthread_rwlockattr_setpshared.c new file mode 100644 index 000000000..2139bfd72 --- /dev/null +++ b/libc/intrin/pthread_rwlockattr_setpshared.c @@ -0,0 +1,40 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/errno.h" +#include "libc/intrin/pthread.h" + +/** + * Sets read-write lock process sharing. + * + * @param pshared can be one of + * - `PTHREAD_PROCESS_SHARED` + * - `PTHREAD_PROCESS_PRIVATE` + * @return 0 on success, or error on failure + * @raises EINVAL if `pshared` is invalid + */ +int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) { + switch (pshared) { + case PTHREAD_PROCESS_SHARED: + case PTHREAD_PROCESS_PRIVATE: + *attr = pshared; + return 0; + default: + return EINVAL; + } +} diff --git a/libc/intrin/pthread_spin_destroy.c b/libc/intrin/pthread_spin_destroy.c new file mode 100644 index 000000000..309071433 --- /dev/null +++ b/libc/intrin/pthread_spin_destroy.c @@ -0,0 +1,28 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Destroys spin lock. + * + * @return 0 on success, or errno on error + */ +int(pthread_spin_destroy)(pthread_spinlock_t *spin) { + return pthread_spin_destroy(spin); +} diff --git a/libc/intrin/pthread_spin_init.c b/libc/intrin/pthread_spin_init.c new file mode 100644 index 000000000..753d4e956 --- /dev/null +++ b/libc/intrin/pthread_spin_init.c @@ -0,0 +1,32 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Initializes spin lock. + * + * @param pshared is ignored, since this implementation always permits + * multiple processes to operate on the same spin locks + * @return 0 on success, or errno on error + * @see pthread_spin_destroy + * @see pthread_spin_lock + */ +int(pthread_spin_init)(pthread_spinlock_t *spin, int pshared) { + return pthread_spin_init(spin, pshared); +} diff --git a/libc/intrin/pthread_spin_lock.c b/libc/intrin/pthread_spin_lock.c new file mode 100644 index 000000000..009aefe52 --- /dev/null +++ b/libc/intrin/pthread_spin_lock.c @@ -0,0 +1,60 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Acquires spin lock. + * + * spin l: 181,570c 58,646ns + * mutex normal l: 297,965c 96,241ns + * mutex recursive l: 1,112,166c 359,223ns + * mutex errorcheck l: 1,449,723c 468,252ns + * + * If the lock is already held, this function will wait for it to become + * available. No genuine error conditions are currently defined. This is + * similar to pthread_mutex_lock() except spin locks are much simpler so + * this API is able to offer a performance advantage in situations where + * scalable contention handling isn't necessary. Spinlocks are also very + * small especially in MODE=tiny where a lock needs 16 bytes of code and + * unlocking needs just 5 bytes. The lock object also only takes 1 byte. + * + * The posixly correct way to use this API is as follows: + * + * pthread_spinlock_t lock; + * pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE); + * pthread_spin_lock(&lock); + * // do work... + * pthread_spin_unlock(&lock); + * pthread_spin_destroy(&lock); + * + * Cosmopolitan permits succinct notation for spin locks: + * + * pthread_spinlock_t lock = 0; + * pthread_spin_lock(&lock); + * // do work... + * pthread_spin_unlock(&lock); + * + * @return 0 on success, or errno on error + * @see pthread_spin_trylock + * @see pthread_spin_unlock + * @see pthread_spin_init + */ +int(pthread_spin_lock)(pthread_spinlock_t *spin) { + return pthread_spin_lock(spin); +} diff --git a/libc/intrin/pthread_spin_trylock.c b/libc/intrin/pthread_spin_trylock.c new file mode 100644 index 000000000..51b693522 --- /dev/null +++ b/libc/intrin/pthread_spin_trylock.c @@ -0,0 +1,33 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Acquires spin lock if available. + * + * Unlike pthread_spin_lock() this function won't block, and instead + * returns an error immediately if the spinlock couldn't be acquired + * furthermore this function doesn't define any error conditions now + * + * @return 0 on success, or errno on error + * @raise EBUSY if lock is already held + */ +int(pthread_spin_trylock)(pthread_spinlock_t *spin) { + return pthread_spin_trylock(spin); +} diff --git a/libc/intrin/pthread_spin_unlock.c b/libc/intrin/pthread_spin_unlock.c new file mode 100644 index 000000000..ba3318c6f --- /dev/null +++ b/libc/intrin/pthread_spin_unlock.c @@ -0,0 +1,29 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" + +/** + * Releases spin lock. + * + * @return 0 on success, or errno on error + * @see pthread_spin_lock + */ +int(pthread_spin_unlock)(pthread_spinlock_t *spin) { + return pthread_spin_unlock(spin); +} diff --git a/test/libc/intrin/pthread_yield.c b/libc/intrin/pthread_yield.c similarity index 95% rename from test/libc/intrin/pthread_yield.c rename to libc/intrin/pthread_yield.c index 6f094cf21..322e0799c 100644 --- a/test/libc/intrin/pthread_yield.c +++ b/libc/intrin/pthread_yield.c @@ -17,17 +17,16 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/errno.h" +#include "libc/calls/strace.internal.h" #include "libc/intrin/pthread.h" /** * Yields current thread's remaining timeslice to operating system. + * * @return 0 on success, or error number on failure */ int pthread_yield(void) { - if (sched_yield() != -1) { - return 0; - } else { - return errno; - } + sched_yield(); + STRACE("pthread_yield()"); + return 0; } diff --git a/libc/intrin/sched_yield.S b/libc/intrin/sched_yield.S index 6af736ef6..06bab590c 100644 --- a/libc/intrin/sched_yield.S +++ b/libc/intrin/sched_yield.S @@ -22,7 +22,8 @@ .privileged // Asks kernel to let other threads be scheduled. -// @return 0 on success, or non-zero on failure +// +// @return 0 on success, or -1 w/ errno // @norestart sched_yield: push %rbp diff --git a/libc/intrin/spinlock.h b/libc/intrin/spinlock.h index 58d666b76..e2f0cd67a 100644 --- a/libc/intrin/spinlock.h +++ b/libc/intrin/spinlock.h @@ -3,6 +3,8 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +/* TODO(jart): DELETE */ + #ifdef TINY #define _spinlock(lock) _spinlock_tiny(lock) #else diff --git a/libc/intrin/wait0.c b/libc/intrin/wait0.c index 0b145ced4..df7c84b29 100644 --- a/libc/intrin/wait0.c +++ b/libc/intrin/wait0.c @@ -16,11 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/atomic.h" #include "libc/calls/calls.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" #include "libc/intrin/wait0.internal.h" #include "libc/linux/futex.h" @@ -34,16 +35,17 @@ void _wait0(const int *ctid) { int x; for (;;) { - if (!(x = atomic_load_explicit(ctid, memory_order_acquire))) { + if (!(x = atomic_load_explicit(ctid, memory_order_relaxed))) { break; } else if (IsLinux() || IsOpenbsd()) { - _futex_wait(ctid, x, &(struct timespec){2}); + _futex_wait_public(ctid, x, &(struct timespec){2}); } else { - sched_yield(); + pthread_yield(); } } if (IsOpenbsd()) { - // TODO(jart): whyyyy do we need it - sched_yield(); + // TODO(jart): Why do we need it? It's not even perfect. + // What's up with all these OpenBSD flakes?? + pthread_yield(); } } diff --git a/libc/isystem/pthread.h b/libc/isystem/pthread.h index 15164ec4d..9e5e99b40 100644 --- a/libc/isystem/pthread.h +++ b/libc/isystem/pthread.h @@ -1,4 +1,5 @@ #ifndef LIBC_ISYSTEM_PTHREAD_H_ #define LIBC_ISYSTEM_PTHREAD_H_ #include "libc/intrin/pthread.h" +#include "libc/intrin/pthread2.h" #endif /* LIBC_ISYSTEM_PTHREAD_H_ */ diff --git a/libc/thread/posixthread.internal.h b/libc/thread/posixthread.internal.h new file mode 100644 index 000000000..e5030a99f --- /dev/null +++ b/libc/thread/posixthread.internal.h @@ -0,0 +1,31 @@ +#ifndef COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_ +#include "libc/runtime/runtime.h" +#include "libc/thread/spawn.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +enum PosixThreadStatus { + kPosixThreadStarted, + kPosixThreadDetached, + kPosixThreadTerminated, + kPosixThreadZombie, +}; + +struct PosixThread { + struct spawn spawn; + void *(*start_routine)(void *); + void *arg; + void *rc; + int tid; + _Atomic(enum PosixThreadStatus) status; + jmp_buf exiter; +}; + +void pthread_zombies_add(struct PosixThread *); +void pthread_zombies_decimate(void); +void pthread_zombies_harvest(void); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_ */ diff --git a/libc/thread/pthread_cond_timedwait.c b/libc/thread/pthread_cond_timedwait.c new file mode 100644 index 000000000..cb2d0263c --- /dev/null +++ b/libc/thread/pthread_cond_timedwait.c @@ -0,0 +1,98 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/assert.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/timespec.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" +#include "libc/intrin/pthread2.h" + +/** + * Waits for condition with optional time limit, e.g. + * + * struct timespec ts; // one second timeout + * ts = _timespec_add(_timespec_mono(), _timespec_frommillis(1000)); + * if (pthread_cond_timedwait(cond, mutex, &ts) == ETIMEDOUT) { + * // handle timeout... + * } + * + * @param mutex needs to be held by thread when calling this function + * @param abstime may be null to wait indefinitely and should contain + * some arbitrary interval added to a `CLOCK_MONOTONIC` timestamp + * @return 0 on success, or errno on error + * @raise ETIMEDOUT if `abstime` was specified and the current time + * exceeded its value + * @raise EPERM if `mutex` is `PTHREAD_MUTEX_ERRORCHECK` and the lock + * isn't owned by the current thread + * @raise EINVAL if `0 ≤ abstime->tv_nsec < 1000000000` wasn't the case + * @see pthread_cond_broadcast + * @see pthread_cond_signal + */ +int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime) { + int rc, err, seq; + struct timespec now, rel, *tsp; + + if (abstime && !(0 <= abstime->tv_nsec && abstime->tv_nsec < 1000000000)) { + assert(!"bad abstime"); + return EINVAL; + } + + if ((err = pthread_mutex_unlock(mutex))) { + return err; + } + + atomic_fetch_add(&cond->waits, 1); + + rc = 0; + seq = atomic_load_explicit(&cond->seq, memory_order_relaxed); + do { + if (!abstime) { + tsp = 0; + } else { + now = _timespec_mono(); + if (_timespec_gte(now, *abstime)) { + rc = ETIMEDOUT; + break; + } + rel = _timespec_sub(*abstime, now); + tsp = &rel; + } + if (IsLinux() || IsOpenbsd()) { + if (cond->attr == PTHREAD_PROCESS_SHARED) { + _futex_wait_public(&cond->seq, seq, tsp); + } else { + _futex_wait_private(&cond->seq, seq, tsp); + } + } else { + sched_yield(); + } + } while (seq == atomic_load_explicit(&cond->seq, memory_order_relaxed)); + + atomic_fetch_sub(&cond->waits, 1); + + if ((err = pthread_mutex_lock(mutex))) { + return err; + } + + return rc; +} diff --git a/libc/thread/pthread_cond_wait.c b/libc/thread/pthread_cond_wait.c new file mode 100644 index 000000000..1f48fba32 --- /dev/null +++ b/libc/thread/pthread_cond_wait.c @@ -0,0 +1,47 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread2.h" + +/** + * Waits for condition, e.g. + * + * pthread_cond_t cond = PTHREAD_COND_INITIALIZER; + * pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + * + * // waiting threads + * pthread_mutex_lock(&lock); + * pthread_cond_wait(&cond, &lock); + * pthread_mutex_unlock(&lock); + * + * // notifying thread + * pthread_mutex_lock(&lock); + * pthread_cond_broadcast(&cond); + * pthread_mutex_unlock(&lock); + * + * @param mutex needs to be held by thread when calling this function + * @return 0 on success, or errno on error + * @raise EPERM if `mutex` is `PTHREAD_MUTEX_ERRORCHECK` and the lock + * isn't owned by the current thread + * @see pthread_cond_timedwait + * @see pthread_cond_broadcast + * @see pthread_cond_signal + */ +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) { + return pthread_cond_timedwait(cond, mutex, 0); +} diff --git a/libc/thread/pthread_create.c b/libc/thread/pthread_create.c new file mode 100644 index 000000000..7659327f2 --- /dev/null +++ b/libc/thread/pthread_create.c @@ -0,0 +1,73 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/errno.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/pthread.h" +#include "libc/mem/mem.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/runtime/runtime.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" + +static int PosixThread(void *arg, int tid) { + struct PosixThread *pt = arg; + enum PosixThreadStatus status; + pt->tid = tid; + if (!setjmp(pt->exiter)) { + ((cthread_t)__get_tls())->pthread = pt; + pt->rc = pt->start_routine(pt->arg); + } + if (atomic_load_explicit(&pt->status, memory_order_relaxed) == + kPosixThreadDetached) { + atomic_store_explicit(&pt->status, kPosixThreadZombie, + memory_order_relaxed); + } else { + atomic_store_explicit(&pt->status, kPosixThreadTerminated, + memory_order_relaxed); + } + return 0; +} + +/** + * Creates thread. + * + * @return 0 on success, or errno on error + */ +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg) { + int e, rc; + struct PosixThread *pt; + e = errno; + pthread_zombies_decimate(); + if ((pt = calloc(1, sizeof(struct PosixThread)))) { + pt->start_routine = start_routine; + pt->arg = arg; + if (!_spawn(PosixThread, pt, &pt->spawn)) { + *thread = pt; + rc = 0; + } else { + free(pt); + rc = errno; + } + } else { + rc = errno; + } + errno = e; + return rc; +} diff --git a/libc/thread/pthread_detach.c b/libc/thread/pthread_detach.c new file mode 100644 index 000000000..28655b9f8 --- /dev/null +++ b/libc/thread/pthread_detach.c @@ -0,0 +1,51 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/atomic.h" +#include "libc/intrin/pthread.h" +#include "libc/macros.internal.h" +#include "libc/mem/mem.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/spawn.h" + +/** + * Asks POSIX thread to free itself automatically on termination. + * + * @return 0 on success, or errno with error + */ +int pthread_detach(pthread_t thread) { + enum PosixThreadStatus status; + struct PosixThread *pt = thread; + for (;;) { + status = atomic_load_explicit(&pt->status, memory_order_relaxed); + if (status == kPosixThreadDetached || status == kPosixThreadZombie) { + break; + } else if (status == kPosixThreadTerminated) { + _join(&pt->spawn); + free(pt); + break; + } else if (status == kPosixThreadStarted && + atomic_compare_exchange_weak_explicit( + &pt->status, &status, kPosixThreadDetached, + memory_order_acquire, memory_order_relaxed)) { + pthread_zombies_add(pt); + break; + } + } + return 0; +} diff --git a/libc/thread/pthread_equal.c b/libc/thread/pthread_equal.c new file mode 100644 index 000000000..0f2fd79cb --- /dev/null +++ b/libc/thread/pthread_equal.c @@ -0,0 +1,31 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" +#include "libc/thread/posixthread.internal.h" + +/** + * Compares thread ids; + * + * @return nonzero if equal, otherwise zero + */ +int pthread_equal(pthread_t t1, pthread_t t2) { + struct PosixThread *a = t1; + struct PosixThread *b = t2; + return a->tid == b->tid; +} diff --git a/libc/thread/pthread_exit.c b/libc/thread/pthread_exit.c new file mode 100644 index 000000000..e1d2f4186 --- /dev/null +++ b/libc/thread/pthread_exit.c @@ -0,0 +1,33 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/spawn.h" +#include "libc/thread/thread.h" + +/** + * Terminates current POSIX thread. + */ +void pthread_exit(void *rc) { + struct PosixThread *pt; + pt = ((cthread_t)__get_tls())->pthread; + pt->rc = rc; + longjmp(pt->exiter, 1); +} diff --git a/libc/thread/pthread_getthreadid_np.c b/libc/thread/pthread_getthreadid_np.c new file mode 100644 index 000000000..e3ceefcdc --- /dev/null +++ b/libc/thread/pthread_getthreadid_np.c @@ -0,0 +1,27 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/calls/calls.h" +#include "libc/intrin/pthread.h" + +/** + * Returns thread id of current POSIX thread. + */ +pthread_id_np_t pthread_getthreadid_np(void) { + return gettid(); +} diff --git a/libc/thread/pthread_getunique_np.c b/libc/thread/pthread_getunique_np.c new file mode 100644 index 000000000..f1b477881 --- /dev/null +++ b/libc/thread/pthread_getunique_np.c @@ -0,0 +1,28 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/thread/posixthread.internal.h" +#include "libc/thread/thread.h" + +/** + * Returns thread id of POSIX thread. + */ +int64_t pthread_getunique_np(pthread_t thread) { + struct PosixThread *pt = thread; + return pt->tid; +} diff --git a/libc/thread/pthread_join.c b/libc/thread/pthread_join.c new file mode 100644 index 000000000..949d6b8f0 --- /dev/null +++ b/libc/thread/pthread_join.c @@ -0,0 +1,45 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/assert.h" +#include "libc/errno.h" +#include "libc/intrin/pthread.h" +#include "libc/macros.internal.h" +#include "libc/mem/mem.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/spawn.h" + +/** + * Waits for thread to terminate. + * + * @return 0 on success, or errno with error + * @raise EDEADLK if thread is detached + */ +int pthread_join(pthread_t thread, void **value_ptr) { + struct PosixThread *pt = thread; + if (pt->status == kPosixThreadDetached) { + assert(!"badjoin"); + return EDEADLK; + } + _join(&pt->spawn); + if (value_ptr) { + *value_ptr = pt->rc; + } + free(pt); + return 0; +} diff --git a/libc/thread/pthread_self.c b/libc/thread/pthread_self.c new file mode 100644 index 000000000..0212ecb83 --- /dev/null +++ b/libc/thread/pthread_self.c @@ -0,0 +1,28 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/thread/thread.h" + +/** + * Returns current POSIX thread. + */ +pthread_t pthread_self(void) { + return ((cthread_t)__get_tls())->pthread; +} diff --git a/libc/thread/thread.h b/libc/thread/thread.h index 900041004..2dce41568 100644 --- a/libc/thread/thread.h +++ b/libc/thread/thread.h @@ -30,7 +30,7 @@ struct cthread_descriptor_t { struct FtraceTls ftrace; /* 0x08 */ void *garbages; /* 0x10 */ locale_t locale; /* 0x20 */ - int64_t __pad2; /* 0x28 */ + pthread_t pthread; /* 0x28 */ struct cthread_descriptor_t *self2; /* 0x30 */ int32_t tid; /* 0x38 */ int32_t err; /* 0x3c */ @@ -64,8 +64,6 @@ int cthread_sem_wait(cthread_sem_t *, int, const struct timespec *); int cthread_sem_signal(cthread_sem_t *); int cthread_memory_wait32(int *, int, const struct timespec *); int cthread_memory_wake32(int *, int); -void cthread_zombies_add(cthread_t); -void cthread_zombies_reap(void); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/thread/wait.c b/libc/thread/wait.c index 09ed69a00..33b9c1b09 100644 --- a/libc/thread/wait.c +++ b/libc/thread/wait.c @@ -16,26 +16,26 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/struct/timespec.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/futex.internal.h" #include "libc/thread/thread.h" int cthread_memory_wait32(int* addr, int val, const struct timespec* timeout) { size_t size; - if (IsLinux() /* || IsOpenbsd() */) { - return _futex_wait(addr, val, timeout); + if (IsLinux() || IsOpenbsd()) { + return _futex_wait_public(addr, val, timeout); } else { return sched_yield(); } } int cthread_memory_wake32(int* addr, int n) { - if (IsLinux() /* || IsOpenbsd() */) { - return _futex_wake(addr, n); + if (IsLinux() || IsOpenbsd()) { + return _futex_wake_public(addr, n); } else { return 0; } diff --git a/libc/thread/zombie.c b/libc/thread/zombie.c new file mode 100644 index 000000000..0203e547f --- /dev/null +++ b/libc/thread/zombie.c @@ -0,0 +1,75 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/bits/atomic.h" +#include "libc/intrin/atomic.h" +#include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/spawn.h" + +/** + * @fileoverview Memory collector for detached threads. + */ + +static struct Zombie { + struct Zombie *next; + struct PosixThread *pt; +} * pthread_zombies; + +void pthread_zombies_add(struct PosixThread *pt) { + struct Zombie *z; + if ((z = malloc(sizeof(struct Zombie)))) { + z->pt = pt; + z->next = atomic_load(&pthread_zombies); + for (;;) { + if (atomic_compare_exchange_weak(&pthread_zombies, &z->next, z)) { + break; + } + } + } +} + +void pthread_zombies_destroy(struct Zombie *z) { + _join(&z->pt->spawn); + free(z->pt); + free(z); +} + +void pthread_zombies_decimate(void) { + struct Zombie *z; + while ((z = atomic_load(&pthread_zombies)) && + atomic_load(&z->pt->status) == kPosixThreadZombie) { + if (atomic_compare_exchange_strong(&pthread_zombies, &z, z->next)) { + pthread_zombies_destroy(z); + } + } +} + +void pthread_zombies_harvest(void) { + struct Zombie *z; + while ((z = atomic_load(&pthread_zombies))) { + if (atomic_compare_exchange_weak(&pthread_zombies, &z, z->next)) { + pthread_zombies_destroy(z); + } + } +} + +__attribute__((__constructor__)) static void init(void) { + atexit(pthread_zombies_harvest); +} diff --git a/test/libc/intrin/pthread_barrier_wait_test.c b/test/libc/intrin/pthread_barrier_wait_test.c new file mode 100644 index 000000000..baa0d4acb --- /dev/null +++ b/test/libc/intrin/pthread_barrier_wait_test.c @@ -0,0 +1,82 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/assert.h" +#include "libc/errno.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/pthread.h" +#include "libc/mem/mem.h" +#include "libc/runtime/gc.internal.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/spawn.h" + +int i, n; +_Atomic(int) p, w; +pthread_barrier_t barrier; + +int Worker(void *arg, int tid) { + int rc; + rc = pthread_barrier_wait(&barrier); + atomic_fetch_add(&w, 1); + ASSERT_GE(rc, 0); + if (rc == PTHREAD_BARRIER_SERIAL_THREAD) { + atomic_fetch_add(&p, 1); + ASSERT_EQ(0, barrier.popped); + ASSERT_EQ(0, barrier.waits); + ASSERT_EQ(n, barrier.count); + } + return 0; +} + +TEST(pthread_barrier_init, test0_isInvalid) { + __assert_disable = true; + ASSERT_EQ(EINVAL, pthread_barrier_init(&barrier, 0, 0)); + __assert_disable = false; +} + +TEST(pthread_barrier_wait, test1) { + struct spawn t; + p = 0; + w = 0; + n = 1; + ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, n)); + ASSERT_SYS(0, 0, _spawn(Worker, 0, &t)); + EXPECT_SYS(0, 0, _join(&t)); + ASSERT_EQ(1, p); + ASSERT_EQ(n, w); + ASSERT_EQ(0, pthread_barrier_destroy(&barrier)); +} + +TEST(pthread_barrier_wait, test32) { + struct spawn *t; + p = 0; + w = 0; + n = 32; + t = gc(malloc(sizeof(struct spawn) * n)); + ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, n)); + for (i = 0; i < n; ++i) { + ASSERT_EQ(0, w); + ASSERT_SYS(0, 0, _spawn(Worker, 0, t + i)); + } + for (i = 0; i < n; ++i) { + EXPECT_SYS(0, 0, _join(t + i)); + } + ASSERT_EQ(1, p); + ASSERT_EQ(n, w); + ASSERT_EQ(0, pthread_barrier_destroy(&barrier)); +} diff --git a/test/libc/intrin/pthread_cond_broadcast_test.c b/test/libc/intrin/pthread_cond_broadcast_test.c new file mode 100644 index 000000000..fd38a3dcc --- /dev/null +++ b/test/libc/intrin/pthread_cond_broadcast_test.c @@ -0,0 +1,120 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/calls/struct/timespec.h" +#include "libc/errno.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/kprintf.h" +#include "libc/intrin/pthread.h" +#include "libc/intrin/pthread2.h" +#include "libc/mem/mem.h" +#include "libc/runtime/gc.internal.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/spawn.h" + +_Atomic(int) bReady; +pthread_cond_t bCond; +pthread_mutex_t bMutex; + +int BroadcastWorker(void *arg, int tid) { + ASSERT_EQ(0, pthread_mutex_lock(&bMutex)); + atomic_fetch_add(&bReady, 1); + ASSERT_EQ(0, pthread_cond_wait(&bCond, &bMutex)); + ASSERT_EQ(0, pthread_mutex_unlock(&bMutex)); + return 0; +} + +TEST(pthread_cond_broadcast, test) { + pthread_condattr_t cAttr; + pthread_mutexattr_t mAttr; + ASSERT_EQ(0, pthread_mutexattr_init(&mAttr)); + ASSERT_EQ(0, pthread_mutexattr_settype(&mAttr, PTHREAD_MUTEX_ERRORCHECK)); + ASSERT_EQ(0, pthread_mutex_init(&bMutex, &mAttr)); + ASSERT_EQ(0, pthread_condattr_init(&cAttr)); + ASSERT_EQ(0, pthread_condattr_setpshared(&cAttr, PTHREAD_PROCESS_PRIVATE)); + ASSERT_EQ(0, pthread_cond_init(&bCond, &cAttr)); + int i, n = 16; + struct spawn *t = gc(malloc(sizeof(struct spawn) * n)); + for (i = 0; i < n; ++i) { + ASSERT_SYS(0, 0, _spawn(BroadcastWorker, 0, t + i)); + } + for (;;) { + if (atomic_load_explicit(&bReady, memory_order_relaxed) == n) { + break; + } else { + pthread_yield(); + } + } + ASSERT_EQ(0, pthread_mutex_lock(&bMutex)); + ASSERT_EQ(0, pthread_cond_broadcast(&bCond)); + ASSERT_EQ(0, pthread_mutex_unlock(&bMutex)); + for (i = 0; i < n; ++i) { + EXPECT_SYS(0, 0, _join(t + i)); + } + ASSERT_EQ(0, pthread_mutex_destroy(&bMutex)); + ASSERT_EQ(0, pthread_cond_destroy(&bCond)); +} + +TEST(pthread_cond_timedwait, testTimeoutInPast_timesOutImmediately) { + struct timespec t = {100000}; + pthread_cond_t c = PTHREAD_COND_INITIALIZER; + pthread_mutex_t m = {PTHREAD_MUTEX_ERRORCHECK}; + ASSERT_EQ(0, pthread_mutex_lock(&m)); + ASSERT_EQ(ETIMEDOUT, pthread_cond_timedwait(&c, &m, &t)); + ASSERT_EQ(0, pthread_mutex_unlock(&m)); +} + +_Atomic(int) tReady; +pthread_cond_t tCond; +pthread_mutex_t tMutex; + +int TimedWorker(void *arg, int tid) { + struct timespec ts; + ASSERT_EQ(0, pthread_mutex_lock(&tMutex)); + atomic_fetch_add(&tReady, 1); + ts = _timespec_add(_timespec_mono(), _timespec_frommillis(30000)); + ASSERT_EQ(0, pthread_cond_timedwait(&tCond, &tMutex, &ts)); + ASSERT_EQ(0, pthread_mutex_unlock(&tMutex)); + return 0; +} + +TEST(pthread_cond_timedwait, testThirtySeconds_doesntTimeOut) { + pthread_condattr_t cAttr; + pthread_mutexattr_t mAttr; + ASSERT_EQ(0, pthread_mutexattr_init(&mAttr)); + ASSERT_EQ(0, pthread_mutexattr_settype(&mAttr, PTHREAD_MUTEX_ERRORCHECK)); + ASSERT_EQ(0, pthread_mutex_init(&tMutex, &mAttr)); + ASSERT_EQ(0, pthread_condattr_init(&cAttr)); + ASSERT_EQ(0, pthread_condattr_setpshared(&cAttr, PTHREAD_PROCESS_PRIVATE)); + ASSERT_EQ(0, pthread_cond_init(&tCond, &cAttr)); + struct spawn t; + ASSERT_SYS(0, 0, _spawn(TimedWorker, 0, &t)); + for (;;) { + if (atomic_load_explicit(&tReady, memory_order_relaxed)) { + break; + } else { + pthread_yield(); + } + } + ASSERT_EQ(0, pthread_mutex_lock(&tMutex)); + ASSERT_EQ(0, pthread_cond_signal(&tCond)); + ASSERT_EQ(0, pthread_mutex_unlock(&tMutex)); + EXPECT_SYS(0, 0, _join(&t)); + ASSERT_EQ(0, pthread_mutex_destroy(&tMutex)); + ASSERT_EQ(0, pthread_cond_destroy(&tCond)); +} diff --git a/test/libc/intrin/pthread_mutex_lock_test.c b/test/libc/intrin/pthread_mutex_lock_test.c index f31437b5a..cc0736d21 100644 --- a/test/libc/intrin/pthread_mutex_lock_test.c +++ b/test/libc/intrin/pthread_mutex_lock_test.c @@ -49,8 +49,8 @@ int count; _Atomic(int) started; _Atomic(int) finished; -_Alignas(64) char slock; pthread_mutex_t mylock; +pthread_spinlock_t slock; struct spawn th[THREADS]; void SetUpOnce(void) { @@ -188,19 +188,27 @@ int SpinlockWorker(void *p, int tid) { int i; ++started; for (i = 0; i < ITERATIONS; ++i) { - _spinlock(&slock); + pthread_spin_lock(&slock); ++count; - _spunlock(&slock); + pthread_spin_unlock(&slock); } ++finished; + STRACE("SpinlockWorker Finished %d", tid); return 0; } -TEST(_spinlock, contention) { +TEST(pthread_spin_lock, test) { int i; count = 0; started = 0; finished = 0; + EXPECT_EQ(0, pthread_spin_init(&slock, 0)); + EXPECT_EQ(0, pthread_spin_trylock(&slock)); + EXPECT_EQ(EBUSY, pthread_spin_trylock(&slock)); + EXPECT_EQ(0, pthread_spin_unlock(&slock)); + EXPECT_EQ(0, pthread_spin_lock(&slock)); + EXPECT_EQ(EBUSY, pthread_spin_trylock(&slock)); + EXPECT_EQ(0, pthread_spin_unlock(&slock)); for (i = 0; i < THREADS; ++i) { ASSERT_NE(-1, _spawn(SpinlockWorker, (void *)(intptr_t)i, th + i)); } @@ -210,12 +218,13 @@ TEST(_spinlock, contention) { EXPECT_EQ(THREADS, started); EXPECT_EQ(THREADS, finished); EXPECT_EQ(THREADS * ITERATIONS, count); + EXPECT_EQ(0, pthread_spin_destroy(&slock)); } BENCH(pthread_mutex_lock, bench) { char schar = 0; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; - EZBENCH2("_spinlock", donothing, _spinlock_contention()); + EZBENCH2("spin", donothing, pthread_spin_lock_test()); EZBENCH2("normal", donothing, pthread_mutex_lock_contention()); EZBENCH2("recursive", donothing, pthread_mutex_lock_rcontention()); EZBENCH2("errorcheck", donothing, pthread_mutex_lock_econtention()); diff --git a/test/libc/intrin/pthread_once_test.c b/test/libc/intrin/pthread_once_test.c new file mode 100644 index 000000000..9032724a8 --- /dev/null +++ b/test/libc/intrin/pthread_once_test.c @@ -0,0 +1,54 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/atomic.h" +#include "libc/intrin/pthread.h" +#include "libc/mem/mem.h" +#include "libc/runtime/gc.internal.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/spawn.h" + +int i, n; +struct spawn *t; +_Atomic(int) x, y; +pthread_barrier_t b; + +void InitFactory(void) { + ASSERT_EQ(0, atomic_load(&x)); + atomic_fetch_add(&y, 1); +} + +int Worker(void *arg, int tid) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + pthread_barrier_wait(&b); + ASSERT_EQ(0, pthread_once(&once, InitFactory)); + ASSERT_EQ(1, atomic_load(&y)); + atomic_fetch_add(&x, 1); + return 0; +} + +TEST(pthread_once, test) { + n = 32; + x = y = 0; + pthread_barrier_init(&b, 0, n); + t = gc(malloc(sizeof(struct spawn) * n)); + for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, _spawn(Worker, 0, t + i)); + for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, _join(t + i)); + ASSERT_EQ(n, atomic_load(&x)); + ASSERT_EQ(1, atomic_load(&y)); +} diff --git a/test/libc/intrin/pthread_rwlock_rdlock_test.c b/test/libc/intrin/pthread_rwlock_rdlock_test.c new file mode 100644 index 000000000..6eaf59a9a --- /dev/null +++ b/test/libc/intrin/pthread_rwlock_rdlock_test.c @@ -0,0 +1,66 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" +#include "libc/mem/mem.h" +#include "libc/runtime/gc.internal.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/spawn.h" + +#define ITERATIONS 50000 +#define READERS 8 +#define WRITERS 2 + +_Atomic(int) reads; +_Atomic(int) writes; +pthread_rwlock_t lock; +pthread_barrier_t barrier; + +int Reader(void *arg, int tid) { + pthread_barrier_wait(&barrier); + for (int i = 0; i < ITERATIONS; ++i) { + pthread_rwlock_rdlock(&lock); + ++reads; + pthread_rwlock_unlock(&lock); + } + return 0; +} + +int Writer(void *arg, int tid) { + pthread_barrier_wait(&barrier); + for (int i = 0; i < ITERATIONS; ++i) { + pthread_rwlock_wrlock(&lock); + ++writes; + pthread_rwlock_unlock(&lock); + } + return 0; +} + +TEST(pthread_rwlock_rdlock, test) { + int i; + struct spawn *t = gc(malloc(sizeof(struct spawn) * (READERS + WRITERS))); + pthread_barrier_init(&barrier, 0, READERS + WRITERS); + for (i = 0; i < READERS + WRITERS; ++i) { + ASSERT_SYS(0, 0, _spawn(i < READERS ? Reader : Writer, 0, t + i)); + } + for (i = 0; i < READERS + WRITERS; ++i) { + EXPECT_SYS(0, 0, _join(t + i)); + } + EXPECT_EQ(READERS * ITERATIONS, reads); + EXPECT_EQ(WRITERS * ITERATIONS, writes); +} diff --git a/test/libc/intrin/rand64_test.c b/test/libc/intrin/rand64_test.c index 2fda6c521..0022a3e0f 100644 --- a/test/libc/intrin/rand64_test.c +++ b/test/libc/intrin/rand64_test.c @@ -44,8 +44,8 @@ #define THREADS 8 #define ENTRIES 1024 -int ready; volatile uint64_t A[THREADS * ENTRIES]; +pthread_barrier_t barrier = PTHREAD_BARRIER_INITIALIZER; void SetUpOnce(void) { __enable_threads(); @@ -62,9 +62,7 @@ dontinline void Generate(int i) { int Thrasher(void *arg, int tid) { int i, id = (intptr_t)arg; - while (!atomic_load(&ready)) { - cthread_memory_wait32(&ready, 0, 0); - } + pthread_barrier_wait(&barrier); for (i = 0; i < ENTRIES; ++i) { Generate(id * ENTRIES + i); } @@ -97,12 +95,10 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) { sigemptyset(&ss); sigaddset(&ss, SIGCHLD); EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &ss, &oldss)); - ready = false; + pthread_barrier_init(&barrier, 0, THREADS); for (i = 0; i < THREADS; ++i) { ASSERT_SYS(0, 0, _spawn(Thrasher, (void *)(intptr_t)i, th + i)); } - atomic_store(&ready, 1); - cthread_memory_wake32(&ready, INT_MAX); for (i = 0; i < THREADS; ++i) { ASSERT_SYS(0, 0, _join(th + i)); } diff --git a/test/libc/thread/pthread_create_test.c b/test/libc/thread/pthread_create_test.c new file mode 100644 index 000000000..8d7eadd22 --- /dev/null +++ b/test/libc/thread/pthread_create_test.c @@ -0,0 +1,39 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 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/intrin/pthread.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" + +pthread_t thread; + +static void *ReturnArg(void *arg) { + return arg; +} + +TEST(pthread_create, testCreateReturnJoin) { + void *exitcode; + ASSERT_EQ(0, pthread_create(&thread, 0, ReturnArg, ReturnArg)); + ASSERT_EQ(0, pthread_join(thread, &exitcode)); + ASSERT_EQ(ReturnArg, exitcode); +} + +TEST(pthread_detach, testCreateReturn) { + ASSERT_EQ(0, pthread_create(&thread, 0, ReturnArg, ReturnArg)); + ASSERT_EQ(0, pthread_detach(thread)); +} diff --git a/third_party/libcxx/__config b/third_party/libcxx/__config index 69e181b90..a2b4ebdd9 100644 --- a/third_party/libcxx/__config +++ b/third_party/libcxx/__config @@ -11,11 +11,11 @@ #define _LIBCPP_CONFIG #include "libc/isystem/features.h" -#define _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -#define _LIBCPP_HAS_NO_THREADS #define _LIBCPP_ABI_UNSTABLE +#define _LIBCPP_NO_EXCEPTIONS +#define _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER #define _LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -// #define _LIBCPP_NO_EXCEPTIONS +#define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION #if defined(_MSC_VER) && !defined(__clang__) # if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) diff --git a/third_party/libcxx/__threading_support b/third_party/libcxx/__threading_support index a5de1f7bc..d7a0b2114 100644 --- a/third_party/libcxx/__threading_support +++ b/third_party/libcxx/__threading_support @@ -13,6 +13,7 @@ #include "third_party/libcxx/__config" #include "third_party/libcxx/chrono" #include "third_party/libcxx/iosfwd" +#include "libc/intrin/pthread2.h" #include "third_party/libcxx/errno.h" #ifndef _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER diff --git a/third_party/libcxx/atomic b/third_party/libcxx/atomic index 5c07e0d2b..7800d41f5 100644 --- a/third_party/libcxx/atomic +++ b/third_party/libcxx/atomic @@ -11,34 +11,6 @@ #ifndef _LIBCPP_ATOMIC #define _LIBCPP_ATOMIC -#include "third_party/libcxx/__config" -#include "third_party/libcxx/cstddef" -#include "third_party/libcxx/cstdint" -#include "third_party/libcxx/type_traits" -#include "third_party/libcxx/version" - -#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) -#pragma GCC system_header -#endif - -#define _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) \ - _LIBCPP_DIAGNOSE_WARNING(__m == memory_order_consume || \ - __m == memory_order_acquire || \ - __m == memory_order_acq_rel, \ - "memory order argument to atomic operation is invalid") - -#define _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) \ - _LIBCPP_DIAGNOSE_WARNING(__m == memory_order_release || \ - __m == memory_order_acq_rel, \ - "memory order argument to atomic operation is invalid") - -#define _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__m, __f) \ - _LIBCPP_DIAGNOSE_WARNING(__f == memory_order_release || \ - __f == memory_order_acq_rel, \ - "memory order argument to atomic operation is invalid") - -_LIBCPP_BEGIN_NAMESPACE_STD - /* atomic synopsis @@ -575,6 +547,44 @@ void atomic_signal_fence(memory_order m) noexcept; */ +#include "third_party/libcxx/__config" +#include "third_party/libcxx/cstddef" +#include "third_party/libcxx/cstdint" +#include "third_party/libcxx/type_traits" +#include "third_party/libcxx/version" + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +#ifdef _LIBCPP_HAS_NO_THREADS +# error is not supported on this single threaded system +#endif +#ifdef _LIBCPP_HAS_NO_ATOMIC_HEADER +# error is not implemented +#endif +#ifdef kill_dependency +# error C++ standard library is incompatible with +#endif + +#define _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) \ + _LIBCPP_DIAGNOSE_WARNING(__m == memory_order_consume || \ + __m == memory_order_acquire || \ + __m == memory_order_acq_rel, \ + "memory order argument to atomic operation is invalid") + +#define _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) \ + _LIBCPP_DIAGNOSE_WARNING(__m == memory_order_release || \ + __m == memory_order_acq_rel, \ + "memory order argument to atomic operation is invalid") + +#define _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__m, __f) \ + _LIBCPP_DIAGNOSE_WARNING(__f == memory_order_release || \ + __f == memory_order_acq_rel, \ + "memory order argument to atomic operation is invalid") + +_LIBCPP_BEGIN_NAMESPACE_STD + // Figure out what the underlying type for `memory_order` would be if it were // declared as an unscoped enum (accounting for -fshort-enums). Use this result // to pin the underlying type in C++20. @@ -683,13 +693,13 @@ _LIBCPP_INLINE_VISIBILITY inline _LIBCPP_CONSTEXPR int __to_gcc_failure_order(me } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) { __cxx_atomic_assign_volatile(__a->__a_value, __val); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) { __a->__a_value = __val; } @@ -705,7 +715,7 @@ void __cxx_atomic_signal_fence(memory_order __order) { } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY inline void __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) { __atomic_store(&__a->__a_value, &__val, @@ -713,7 +723,7 @@ void __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY inline void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) { __atomic_store(&__a->__a_value, &__val, @@ -721,7 +731,7 @@ void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_load(const volatile __cxx_atomic_base_impl<_Tp>* __a, memory_order __order) { _Tp __ret; @@ -731,7 +741,7 @@ _Tp __cxx_atomic_load(const volatile __cxx_atomic_base_impl<_Tp>* __a, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_load(const __cxx_atomic_base_impl<_Tp>* __a, memory_order __order) { _Tp __ret; __atomic_load(&__a->__a_value, &__ret, @@ -740,7 +750,7 @@ _Tp __cxx_atomic_load(const __cxx_atomic_base_impl<_Tp>* __a, memory_order __ord } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY inline _Tp __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) { _Tp __ret; @@ -750,7 +760,7 @@ _Tp __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY inline _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) { _Tp __ret; @@ -760,7 +770,7 @@ _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_compare_exchange_strong( volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) { @@ -771,7 +781,7 @@ bool __cxx_atomic_compare_exchange_strong( } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_compare_exchange_strong( __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) { @@ -782,7 +792,7 @@ bool __cxx_atomic_compare_exchange_strong( } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_compare_exchange_weak( volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) { @@ -793,7 +803,7 @@ bool __cxx_atomic_compare_exchange_weak( } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_compare_exchange_weak( __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) { @@ -817,7 +827,7 @@ template struct __skip_amt<_Tp[n]> { }; template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_add(volatile __cxx_atomic_base_impl<_Tp>* __a, _Td __delta, memory_order __order) { return __atomic_fetch_add(&__a->__a_value, __delta * __skip_amt<_Tp>::value, @@ -825,7 +835,7 @@ _Tp __cxx_atomic_fetch_add(volatile __cxx_atomic_base_impl<_Tp>* __a, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_add(__cxx_atomic_base_impl<_Tp>* __a, _Td __delta, memory_order __order) { return __atomic_fetch_add(&__a->__a_value, __delta * __skip_amt<_Tp>::value, @@ -833,7 +843,7 @@ _Tp __cxx_atomic_fetch_add(__cxx_atomic_base_impl<_Tp>* __a, _Td __delta, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_sub(volatile __cxx_atomic_base_impl<_Tp>* __a, _Td __delta, memory_order __order) { return __atomic_fetch_sub(&__a->__a_value, __delta * __skip_amt<_Tp>::value, @@ -841,7 +851,7 @@ _Tp __cxx_atomic_fetch_sub(volatile __cxx_atomic_base_impl<_Tp>* __a, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_sub(__cxx_atomic_base_impl<_Tp>* __a, _Td __delta, memory_order __order) { return __atomic_fetch_sub(&__a->__a_value, __delta * __skip_amt<_Tp>::value, @@ -849,7 +859,7 @@ _Tp __cxx_atomic_fetch_sub(__cxx_atomic_base_impl<_Tp>* __a, _Td __delta, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_and(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) { return __atomic_fetch_and(&__a->__a_value, __pattern, @@ -857,7 +867,7 @@ _Tp __cxx_atomic_fetch_and(volatile __cxx_atomic_base_impl<_Tp>* __a, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_and(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) { return __atomic_fetch_and(&__a->__a_value, __pattern, @@ -865,7 +875,7 @@ _Tp __cxx_atomic_fetch_and(__cxx_atomic_base_impl<_Tp>* __a, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_or(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) { return __atomic_fetch_or(&__a->__a_value, __pattern, @@ -873,7 +883,7 @@ _Tp __cxx_atomic_fetch_or(volatile __cxx_atomic_base_impl<_Tp>* __a, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_or(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) { return __atomic_fetch_or(&__a->__a_value, __pattern, @@ -881,7 +891,7 @@ _Tp __cxx_atomic_fetch_or(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_xor(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) { return __atomic_fetch_xor(&__a->__a_value, __pattern, @@ -889,7 +899,7 @@ _Tp __cxx_atomic_fetch_xor(volatile __cxx_atomic_base_impl<_Tp>* __a, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) { return __atomic_fetch_xor(&__a->__a_value, __pattern, @@ -927,145 +937,145 @@ void __cxx_atomic_signal_fence(memory_order __order) _NOEXCEPT { } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT { __c11_atomic_init(&__a->__a_value, __val); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> * __a, _Tp __val) _NOEXCEPT { __c11_atomic_init(&__a->__a_value, __val); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT { __c11_atomic_store(&__a->__a_value, __val, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> * __a, _Tp __val, memory_order __order) _NOEXCEPT { __c11_atomic_store(&__a->__a_value, __val, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_load(__cxx_atomic_base_impl<_Tp> const volatile* __a, memory_order __order) _NOEXCEPT { using __ptr_type = typename remove_const__a_value)>::type*; return __c11_atomic_load(const_cast<__ptr_type>(&__a->__a_value), static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_load(__cxx_atomic_base_impl<_Tp> const* __a, memory_order __order) _NOEXCEPT { using __ptr_type = typename remove_const__a_value)>::type*; return __c11_atomic_load(const_cast<__ptr_type>(&__a->__a_value), static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT { return __c11_atomic_exchange(&__a->__a_value, __value, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> * __a, _Tp __value, memory_order __order) _NOEXCEPT { return __c11_atomic_exchange(&__a->__a_value, __value, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_compare_exchange_strong(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) _NOEXCEPT { return __c11_atomic_compare_exchange_strong(&__a->__a_value, __expected, __value, static_cast<__memory_order_underlying_t>(__success), static_cast<__memory_order_underlying_t>(__failure)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_compare_exchange_strong(__cxx_atomic_base_impl<_Tp> * __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) _NOEXCEPT { return __c11_atomic_compare_exchange_strong(&__a->__a_value, __expected, __value, static_cast<__memory_order_underlying_t>(__success), static_cast<__memory_order_underlying_t>(__failure)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_compare_exchange_weak(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) _NOEXCEPT { return __c11_atomic_compare_exchange_weak(&__a->__a_value, __expected, __value, static_cast<__memory_order_underlying_t>(__success), static_cast<__memory_order_underlying_t>(__failure)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_compare_exchange_weak(__cxx_atomic_base_impl<_Tp> * __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) _NOEXCEPT { return __c11_atomic_compare_exchange_weak(&__a->__a_value, __expected, __value, static_cast<__memory_order_underlying_t>(__success), static_cast<__memory_order_underlying_t>(__failure)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_add(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __delta, memory_order __order) _NOEXCEPT { return __c11_atomic_fetch_add(&__a->__a_value, __delta, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_add(__cxx_atomic_base_impl<_Tp> * __a, _Tp __delta, memory_order __order) _NOEXCEPT { return __c11_atomic_fetch_add(&__a->__a_value, __delta, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp* __cxx_atomic_fetch_add(__cxx_atomic_base_impl<_Tp*> volatile* __a, ptrdiff_t __delta, memory_order __order) _NOEXCEPT { return __c11_atomic_fetch_add(&__a->__a_value, __delta, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp* __cxx_atomic_fetch_add(__cxx_atomic_base_impl<_Tp*> * __a, ptrdiff_t __delta, memory_order __order) _NOEXCEPT { return __c11_atomic_fetch_add(&__a->__a_value, __delta, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_sub(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __delta, memory_order __order) _NOEXCEPT { return __c11_atomic_fetch_sub(&__a->__a_value, __delta, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_sub(__cxx_atomic_base_impl<_Tp> * __a, _Tp __delta, memory_order __order) _NOEXCEPT { return __c11_atomic_fetch_sub(&__a->__a_value, __delta, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp* __cxx_atomic_fetch_sub(__cxx_atomic_base_impl<_Tp*> volatile* __a, ptrdiff_t __delta, memory_order __order) _NOEXCEPT { return __c11_atomic_fetch_sub(&__a->__a_value, __delta, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp* __cxx_atomic_fetch_sub(__cxx_atomic_base_impl<_Tp*> * __a, ptrdiff_t __delta, memory_order __order) _NOEXCEPT { return __c11_atomic_fetch_sub(&__a->__a_value, __delta, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_and(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT { return __c11_atomic_fetch_and(&__a->__a_value, __pattern, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_and(__cxx_atomic_base_impl<_Tp> * __a, _Tp __pattern, memory_order __order) _NOEXCEPT { return __c11_atomic_fetch_and(&__a->__a_value, __pattern, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_or(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT { return __c11_atomic_fetch_or(&__a->__a_value, __pattern, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_or(__cxx_atomic_base_impl<_Tp> * __a, _Tp __pattern, memory_order __order) _NOEXCEPT { return __c11_atomic_fetch_or(&__a->__a_value, __pattern, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT { return __c11_atomic_fetch_xor(&__a->__a_value, __pattern, static_cast<__memory_order_underlying_t>(__order)); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp> * __a, _Tp __pattern, memory_order __order) _NOEXCEPT { return __c11_atomic_fetch_xor(&__a->__a_value, __pattern, static_cast<__memory_order_underlying_t>(__order)); } @@ -1073,7 +1083,7 @@ _Tp __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp> * __a, _Tp __pattern, mem #endif // _LIBCPP_HAS_GCC_ATOMIC_IMP, _LIBCPP_HAS_C_ATOMIC_IMP template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp kill_dependency(_Tp __y) _NOEXCEPT { return __y; @@ -1148,25 +1158,25 @@ struct __cxx_atomic_lock_impl { }; template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_init(volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp __val) { __cxx_atomic_assign_volatile(__a->__a_value, __val); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_init(__cxx_atomic_lock_impl<_Tp>* __a, _Tp __val) { __a->__a_value = __val; } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_store(volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp __val, memory_order) { __a->__lock(); __cxx_atomic_assign_volatile(__a->__a_value, __val); __a->__unlock(); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_store(__cxx_atomic_lock_impl<_Tp>* __a, _Tp __val, memory_order) { __a->__lock(); __a->__a_value = __val; @@ -1174,18 +1184,18 @@ void __cxx_atomic_store(__cxx_atomic_lock_impl<_Tp>* __a, _Tp __val, memory_ord } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_load(const volatile __cxx_atomic_lock_impl<_Tp>* __a, memory_order) { return __a->__read(); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_load(const __cxx_atomic_lock_impl<_Tp>* __a, memory_order) { return __a->__read(); } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_exchange(volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp __value, memory_order) { __a->__lock(); _Tp __old; @@ -1195,7 +1205,7 @@ _Tp __cxx_atomic_exchange(volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp __value return __old; } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_exchange(__cxx_atomic_lock_impl<_Tp>* __a, _Tp __value, memory_order) { __a->__lock(); _Tp __old = __a->__a_value; @@ -1205,7 +1215,7 @@ _Tp __cxx_atomic_exchange(__cxx_atomic_lock_impl<_Tp>* __a, _Tp __value, memory_ } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_compare_exchange_strong(volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order, memory_order) { __a->__lock(); @@ -1220,7 +1230,7 @@ bool __cxx_atomic_compare_exchange_strong(volatile __cxx_atomic_lock_impl<_Tp>* return __ret; } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_compare_exchange_strong(__cxx_atomic_lock_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order, memory_order) { __a->__lock(); @@ -1234,7 +1244,7 @@ bool __cxx_atomic_compare_exchange_strong(__cxx_atomic_lock_impl<_Tp>* __a, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_compare_exchange_weak(volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order, memory_order) { __a->__lock(); @@ -1249,7 +1259,7 @@ bool __cxx_atomic_compare_exchange_weak(volatile __cxx_atomic_lock_impl<_Tp>* __ return __ret; } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_compare_exchange_weak(__cxx_atomic_lock_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order, memory_order) { __a->__lock(); @@ -1263,7 +1273,7 @@ bool __cxx_atomic_compare_exchange_weak(__cxx_atomic_lock_impl<_Tp>* __a, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_add(volatile __cxx_atomic_lock_impl<_Tp>* __a, _Td __delta, memory_order) { __a->__lock(); @@ -1274,7 +1284,7 @@ _Tp __cxx_atomic_fetch_add(volatile __cxx_atomic_lock_impl<_Tp>* __a, return __old; } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_add(__cxx_atomic_lock_impl<_Tp>* __a, _Td __delta, memory_order) { __a->__lock(); @@ -1285,7 +1295,7 @@ _Tp __cxx_atomic_fetch_add(__cxx_atomic_lock_impl<_Tp>* __a, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp* __cxx_atomic_fetch_add(volatile __cxx_atomic_lock_impl<_Tp*>* __a, ptrdiff_t __delta, memory_order) { __a->__lock(); @@ -1296,7 +1306,7 @@ _Tp* __cxx_atomic_fetch_add(volatile __cxx_atomic_lock_impl<_Tp*>* __a, return __old; } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp* __cxx_atomic_fetch_add(__cxx_atomic_lock_impl<_Tp*>* __a, ptrdiff_t __delta, memory_order) { __a->__lock(); @@ -1307,7 +1317,7 @@ _Tp* __cxx_atomic_fetch_add(__cxx_atomic_lock_impl<_Tp*>* __a, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_sub(volatile __cxx_atomic_lock_impl<_Tp>* __a, _Td __delta, memory_order) { __a->__lock(); @@ -1318,7 +1328,7 @@ _Tp __cxx_atomic_fetch_sub(volatile __cxx_atomic_lock_impl<_Tp>* __a, return __old; } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_sub(__cxx_atomic_lock_impl<_Tp>* __a, _Td __delta, memory_order) { __a->__lock(); @@ -1329,7 +1339,7 @@ _Tp __cxx_atomic_fetch_sub(__cxx_atomic_lock_impl<_Tp>* __a, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_and(volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp __pattern, memory_order) { __a->__lock(); @@ -1340,7 +1350,7 @@ _Tp __cxx_atomic_fetch_and(volatile __cxx_atomic_lock_impl<_Tp>* __a, return __old; } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_and(__cxx_atomic_lock_impl<_Tp>* __a, _Tp __pattern, memory_order) { __a->__lock(); @@ -1351,7 +1361,7 @@ _Tp __cxx_atomic_fetch_and(__cxx_atomic_lock_impl<_Tp>* __a, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_or(volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp __pattern, memory_order) { __a->__lock(); @@ -1362,7 +1372,7 @@ _Tp __cxx_atomic_fetch_or(volatile __cxx_atomic_lock_impl<_Tp>* __a, return __old; } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_or(__cxx_atomic_lock_impl<_Tp>* __a, _Tp __pattern, memory_order) { __a->__lock(); @@ -1373,7 +1383,7 @@ _Tp __cxx_atomic_fetch_or(__cxx_atomic_lock_impl<_Tp>* __a, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_xor(volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp __pattern, memory_order) { __a->__lock(); @@ -1384,7 +1394,7 @@ _Tp __cxx_atomic_fetch_xor(volatile __cxx_atomic_lock_impl<_Tp>* __a, return __old; } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp __cxx_atomic_fetch_xor(__cxx_atomic_lock_impl<_Tp>* __a, _Tp __pattern, memory_order) { __a->__lock(); @@ -1710,7 +1720,7 @@ struct atomic<_Tp*> // atomic_is_lock_free template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool atomic_is_lock_free(const volatile atomic<_Tp>* __o) _NOEXCEPT { @@ -1718,7 +1728,7 @@ atomic_is_lock_free(const volatile atomic<_Tp>* __o) _NOEXCEPT } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool atomic_is_lock_free(const atomic<_Tp>* __o) _NOEXCEPT { @@ -1728,7 +1738,7 @@ atomic_is_lock_free(const atomic<_Tp>* __o) _NOEXCEPT // atomic_init template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void atomic_init(volatile atomic<_Tp>* __o, _Tp __d) _NOEXCEPT { @@ -1736,7 +1746,7 @@ atomic_init(volatile atomic<_Tp>* __o, _Tp __d) _NOEXCEPT } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void atomic_init(atomic<_Tp>* __o, _Tp __d) _NOEXCEPT { @@ -1746,7 +1756,7 @@ atomic_init(atomic<_Tp>* __o, _Tp __d) _NOEXCEPT // atomic_store template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void atomic_store(volatile atomic<_Tp>* __o, _Tp __d) _NOEXCEPT { @@ -1754,7 +1764,7 @@ atomic_store(volatile atomic<_Tp>* __o, _Tp __d) _NOEXCEPT } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void atomic_store(atomic<_Tp>* __o, _Tp __d) _NOEXCEPT { @@ -1764,7 +1774,7 @@ atomic_store(atomic<_Tp>* __o, _Tp __d) _NOEXCEPT // atomic_store_explicit template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void atomic_store_explicit(volatile atomic<_Tp>* __o, _Tp __d, memory_order __m) _NOEXCEPT _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) @@ -1773,7 +1783,7 @@ atomic_store_explicit(volatile atomic<_Tp>* __o, _Tp __d, memory_order __m) _NOE } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY void atomic_store_explicit(atomic<_Tp>* __o, _Tp __d, memory_order __m) _NOEXCEPT _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) @@ -1784,7 +1794,7 @@ atomic_store_explicit(atomic<_Tp>* __o, _Tp __d, memory_order __m) _NOEXCEPT // atomic_load template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp atomic_load(const volatile atomic<_Tp>* __o) _NOEXCEPT { @@ -1792,7 +1802,7 @@ atomic_load(const volatile atomic<_Tp>* __o) _NOEXCEPT } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp atomic_load(const atomic<_Tp>* __o) _NOEXCEPT { @@ -1802,7 +1812,7 @@ atomic_load(const atomic<_Tp>* __o) _NOEXCEPT // atomic_load_explicit template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp atomic_load_explicit(const volatile atomic<_Tp>* __o, memory_order __m) _NOEXCEPT _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) @@ -1811,7 +1821,7 @@ atomic_load_explicit(const volatile atomic<_Tp>* __o, memory_order __m) _NOEXCEP } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp atomic_load_explicit(const atomic<_Tp>* __o, memory_order __m) _NOEXCEPT _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) @@ -1822,7 +1832,7 @@ atomic_load_explicit(const atomic<_Tp>* __o, memory_order __m) _NOEXCEPT // atomic_exchange template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp atomic_exchange(volatile atomic<_Tp>* __o, _Tp __d) _NOEXCEPT { @@ -1830,7 +1840,7 @@ atomic_exchange(volatile atomic<_Tp>* __o, _Tp __d) _NOEXCEPT } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp atomic_exchange(atomic<_Tp>* __o, _Tp __d) _NOEXCEPT { @@ -1840,7 +1850,7 @@ atomic_exchange(atomic<_Tp>* __o, _Tp __d) _NOEXCEPT // atomic_exchange_explicit template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp atomic_exchange_explicit(volatile atomic<_Tp>* __o, _Tp __d, memory_order __m) _NOEXCEPT { @@ -1848,7 +1858,7 @@ atomic_exchange_explicit(volatile atomic<_Tp>* __o, _Tp __d, memory_order __m) _ } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp atomic_exchange_explicit(atomic<_Tp>* __o, _Tp __d, memory_order __m) _NOEXCEPT { @@ -1858,7 +1868,7 @@ atomic_exchange_explicit(atomic<_Tp>* __o, _Tp __d, memory_order __m) _NOEXCEPT // atomic_compare_exchange_weak template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool atomic_compare_exchange_weak(volatile atomic<_Tp>* __o, _Tp* __e, _Tp __d) _NOEXCEPT { @@ -1866,7 +1876,7 @@ atomic_compare_exchange_weak(volatile atomic<_Tp>* __o, _Tp* __e, _Tp __d) _NOEX } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool atomic_compare_exchange_weak(atomic<_Tp>* __o, _Tp* __e, _Tp __d) _NOEXCEPT { @@ -1876,7 +1886,7 @@ atomic_compare_exchange_weak(atomic<_Tp>* __o, _Tp* __e, _Tp __d) _NOEXCEPT // atomic_compare_exchange_strong template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool atomic_compare_exchange_strong(volatile atomic<_Tp>* __o, _Tp* __e, _Tp __d) _NOEXCEPT { @@ -1884,7 +1894,7 @@ atomic_compare_exchange_strong(volatile atomic<_Tp>* __o, _Tp* __e, _Tp __d) _NO } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool atomic_compare_exchange_strong(atomic<_Tp>* __o, _Tp* __e, _Tp __d) _NOEXCEPT { @@ -1894,7 +1904,7 @@ atomic_compare_exchange_strong(atomic<_Tp>* __o, _Tp* __e, _Tp __d) _NOEXCEPT // atomic_compare_exchange_weak_explicit template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool atomic_compare_exchange_weak_explicit(volatile atomic<_Tp>* __o, _Tp* __e, _Tp __d, @@ -1905,7 +1915,7 @@ atomic_compare_exchange_weak_explicit(volatile atomic<_Tp>* __o, _Tp* __e, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool atomic_compare_exchange_weak_explicit(atomic<_Tp>* __o, _Tp* __e, _Tp __d, memory_order __s, memory_order __f) _NOEXCEPT @@ -1917,7 +1927,7 @@ atomic_compare_exchange_weak_explicit(atomic<_Tp>* __o, _Tp* __e, _Tp __d, // atomic_compare_exchange_strong_explicit template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool atomic_compare_exchange_strong_explicit(volatile atomic<_Tp>* __o, _Tp* __e, _Tp __d, @@ -1928,7 +1938,7 @@ atomic_compare_exchange_strong_explicit(volatile atomic<_Tp>* __o, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY bool atomic_compare_exchange_strong_explicit(atomic<_Tp>* __o, _Tp* __e, _Tp __d, @@ -1941,7 +1951,7 @@ atomic_compare_exchange_strong_explicit(atomic<_Tp>* __o, _Tp* __e, // atomic_fetch_add template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -1953,7 +1963,7 @@ atomic_fetch_add(volatile atomic<_Tp>* __o, _Tp __op) _NOEXCEPT } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -1965,7 +1975,7 @@ atomic_fetch_add(atomic<_Tp>* __o, _Tp __op) _NOEXCEPT } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp* atomic_fetch_add(volatile atomic<_Tp*>* __o, ptrdiff_t __op) _NOEXCEPT { @@ -1973,7 +1983,7 @@ atomic_fetch_add(volatile atomic<_Tp*>* __o, ptrdiff_t __op) _NOEXCEPT } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp* atomic_fetch_add(atomic<_Tp*>* __o, ptrdiff_t __op) _NOEXCEPT { @@ -1983,7 +1993,7 @@ atomic_fetch_add(atomic<_Tp*>* __o, ptrdiff_t __op) _NOEXCEPT // atomic_fetch_add_explicit template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -1995,7 +2005,7 @@ atomic_fetch_add_explicit(volatile atomic<_Tp>* __o, _Tp __op, memory_order __m) } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2007,7 +2017,7 @@ atomic_fetch_add_explicit(atomic<_Tp>* __o, _Tp __op, memory_order __m) _NOEXCEP } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp* atomic_fetch_add_explicit(volatile atomic<_Tp*>* __o, ptrdiff_t __op, memory_order __m) _NOEXCEPT @@ -2016,7 +2026,7 @@ atomic_fetch_add_explicit(volatile atomic<_Tp*>* __o, ptrdiff_t __op, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp* atomic_fetch_add_explicit(atomic<_Tp*>* __o, ptrdiff_t __op, memory_order __m) _NOEXCEPT { @@ -2026,7 +2036,7 @@ atomic_fetch_add_explicit(atomic<_Tp*>* __o, ptrdiff_t __op, memory_order __m) _ // atomic_fetch_sub template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2038,7 +2048,7 @@ atomic_fetch_sub(volatile atomic<_Tp>* __o, _Tp __op) _NOEXCEPT } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2050,7 +2060,7 @@ atomic_fetch_sub(atomic<_Tp>* __o, _Tp __op) _NOEXCEPT } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp* atomic_fetch_sub(volatile atomic<_Tp*>* __o, ptrdiff_t __op) _NOEXCEPT { @@ -2058,7 +2068,7 @@ atomic_fetch_sub(volatile atomic<_Tp*>* __o, ptrdiff_t __op) _NOEXCEPT } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp* atomic_fetch_sub(atomic<_Tp*>* __o, ptrdiff_t __op) _NOEXCEPT { @@ -2068,7 +2078,7 @@ atomic_fetch_sub(atomic<_Tp*>* __o, ptrdiff_t __op) _NOEXCEPT // atomic_fetch_sub_explicit template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2080,7 +2090,7 @@ atomic_fetch_sub_explicit(volatile atomic<_Tp>* __o, _Tp __op, memory_order __m) } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2092,7 +2102,7 @@ atomic_fetch_sub_explicit(atomic<_Tp>* __o, _Tp __op, memory_order __m) _NOEXCEP } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp* atomic_fetch_sub_explicit(volatile atomic<_Tp*>* __o, ptrdiff_t __op, memory_order __m) _NOEXCEPT @@ -2101,7 +2111,7 @@ atomic_fetch_sub_explicit(volatile atomic<_Tp*>* __o, ptrdiff_t __op, } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY _Tp* atomic_fetch_sub_explicit(atomic<_Tp*>* __o, ptrdiff_t __op, memory_order __m) _NOEXCEPT { @@ -2111,7 +2121,7 @@ atomic_fetch_sub_explicit(atomic<_Tp*>* __o, ptrdiff_t __op, memory_order __m) _ // atomic_fetch_and template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2123,7 +2133,7 @@ atomic_fetch_and(volatile atomic<_Tp>* __o, _Tp __op) _NOEXCEPT } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2137,7 +2147,7 @@ atomic_fetch_and(atomic<_Tp>* __o, _Tp __op) _NOEXCEPT // atomic_fetch_and_explicit template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2149,7 +2159,7 @@ atomic_fetch_and_explicit(volatile atomic<_Tp>* __o, _Tp __op, memory_order __m) } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2163,7 +2173,7 @@ atomic_fetch_and_explicit(atomic<_Tp>* __o, _Tp __op, memory_order __m) _NOEXCEP // atomic_fetch_or template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2175,7 +2185,7 @@ atomic_fetch_or(volatile atomic<_Tp>* __o, _Tp __op) _NOEXCEPT } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2189,7 +2199,7 @@ atomic_fetch_or(atomic<_Tp>* __o, _Tp __op) _NOEXCEPT // atomic_fetch_or_explicit template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2201,7 +2211,7 @@ atomic_fetch_or_explicit(volatile atomic<_Tp>* __o, _Tp __op, memory_order __m) } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2215,7 +2225,7 @@ atomic_fetch_or_explicit(atomic<_Tp>* __o, _Tp __op, memory_order __m) _NOEXCEPT // atomic_fetch_xor template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2227,7 +2237,7 @@ atomic_fetch_xor(volatile atomic<_Tp>* __o, _Tp __op) _NOEXCEPT } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2241,7 +2251,7 @@ atomic_fetch_xor(atomic<_Tp>* __o, _Tp __op) _NOEXCEPT // atomic_fetch_xor_explicit template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, @@ -2253,7 +2263,7 @@ atomic_fetch_xor_explicit(volatile atomic<_Tp>* __o, _Tp __op, memory_order __m) } template -inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_INLINE_VISIBILITY typename enable_if < is_integral<_Tp>::value && !is_same<_Tp, bool>::value, diff --git a/third_party/libcxx/future b/third_party/libcxx/future new file mode 100644 index 000000000..a89cabb6d --- /dev/null +++ b/third_party/libcxx/future @@ -0,0 +1,2609 @@ +// -*- C++ -*- +// clang-format off +//===--------------------------- future -----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_FUTURE +#define _LIBCPP_FUTURE + +/* + future synopsis + +namespace std +{ + +enum class future_errc +{ + future_already_retrieved = 1, + promise_already_satisfied, + no_state, + broken_promise +}; + +enum class launch +{ + async = 1, + deferred = 2, + any = async | deferred +}; + +enum class future_status +{ + ready, + timeout, + deferred +}; + +template <> struct is_error_code_enum : public true_type { }; +error_code make_error_code(future_errc e) noexcept; +error_condition make_error_condition(future_errc e) noexcept; + +const error_category& future_category() noexcept; + +class future_error + : public logic_error +{ +public: + future_error(error_code ec); // exposition only + explicit future_error(future_errc); // C++17 + const error_code& code() const noexcept; + const char* what() const noexcept; +}; + +template +class promise +{ +public: + promise(); + template + promise(allocator_arg_t, const Allocator& a); + promise(promise&& rhs) noexcept; + promise(const promise& rhs) = delete; + ~promise(); + + // assignment + promise& operator=(promise&& rhs) noexcept; + promise& operator=(const promise& rhs) = delete; + void swap(promise& other) noexcept; + + // retrieving the result + future get_future(); + + // setting the result + void set_value(const R& r); + void set_value(R&& r); + void set_exception(exception_ptr p); + + // setting the result with deferred notification + void set_value_at_thread_exit(const R& r); + void set_value_at_thread_exit(R&& r); + void set_exception_at_thread_exit(exception_ptr p); +}; + +template +class promise +{ +public: + promise(); + template + promise(allocator_arg_t, const Allocator& a); + promise(promise&& rhs) noexcept; + promise(const promise& rhs) = delete; + ~promise(); + + // assignment + promise& operator=(promise&& rhs) noexcept; + promise& operator=(const promise& rhs) = delete; + void swap(promise& other) noexcept; + + // retrieving the result + future get_future(); + + // setting the result + void set_value(R& r); + void set_exception(exception_ptr p); + + // setting the result with deferred notification + void set_value_at_thread_exit(R&); + void set_exception_at_thread_exit(exception_ptr p); +}; + +template <> +class promise +{ +public: + promise(); + template + promise(allocator_arg_t, const Allocator& a); + promise(promise&& rhs) noexcept; + promise(const promise& rhs) = delete; + ~promise(); + + // assignment + promise& operator=(promise&& rhs) noexcept; + promise& operator=(const promise& rhs) = delete; + void swap(promise& other) noexcept; + + // retrieving the result + future get_future(); + + // setting the result + void set_value(); + void set_exception(exception_ptr p); + + // setting the result with deferred notification + void set_value_at_thread_exit(); + void set_exception_at_thread_exit(exception_ptr p); +}; + +template void swap(promise& x, promise& y) noexcept; + +template + struct uses_allocator, Alloc> : public true_type {}; + +template +class future +{ +public: + future() noexcept; + future(future&&) noexcept; + future(const future& rhs) = delete; + ~future(); + future& operator=(const future& rhs) = delete; + future& operator=(future&&) noexcept; + shared_future share() noexcept; + + // retrieving the value + R get(); + + // functions to check state + bool valid() const noexcept; + + void wait() const; + template + future_status + wait_for(const chrono::duration& rel_time) const; + template + future_status + wait_until(const chrono::time_point& abs_time) const; +}; + +template +class future +{ +public: + future() noexcept; + future(future&&) noexcept; + future(const future& rhs) = delete; + ~future(); + future& operator=(const future& rhs) = delete; + future& operator=(future&&) noexcept; + shared_future share() noexcept; + + // retrieving the value + R& get(); + + // functions to check state + bool valid() const noexcept; + + void wait() const; + template + future_status + wait_for(const chrono::duration& rel_time) const; + template + future_status + wait_until(const chrono::time_point& abs_time) const; +}; + +template <> +class future +{ +public: + future() noexcept; + future(future&&) noexcept; + future(const future& rhs) = delete; + ~future(); + future& operator=(const future& rhs) = delete; + future& operator=(future&&) noexcept; + shared_future share() noexcept; + + // retrieving the value + void get(); + + // functions to check state + bool valid() const noexcept; + + void wait() const; + template + future_status + wait_for(const chrono::duration& rel_time) const; + template + future_status + wait_until(const chrono::time_point& abs_time) const; +}; + +template +class shared_future +{ +public: + shared_future() noexcept; + shared_future(const shared_future& rhs); + shared_future(future&&) noexcept; + shared_future(shared_future&& rhs) noexcept; + ~shared_future(); + shared_future& operator=(const shared_future& rhs); + shared_future& operator=(shared_future&& rhs) noexcept; + + // retrieving the value + const R& get() const; + + // functions to check state + bool valid() const noexcept; + + void wait() const; + template + future_status + wait_for(const chrono::duration& rel_time) const; + template + future_status + wait_until(const chrono::time_point& abs_time) const; +}; + +template +class shared_future +{ +public: + shared_future() noexcept; + shared_future(const shared_future& rhs); + shared_future(future&&) noexcept; + shared_future(shared_future&& rhs) noexcept; + ~shared_future(); + shared_future& operator=(const shared_future& rhs); + shared_future& operator=(shared_future&& rhs) noexcept; + + // retrieving the value + R& get() const; + + // functions to check state + bool valid() const noexcept; + + void wait() const; + template + future_status + wait_for(const chrono::duration& rel_time) const; + template + future_status + wait_until(const chrono::time_point& abs_time) const; +}; + +template <> +class shared_future +{ +public: + shared_future() noexcept; + shared_future(const shared_future& rhs); + shared_future(future&&) noexcept; + shared_future(shared_future&& rhs) noexcept; + ~shared_future(); + shared_future& operator=(const shared_future& rhs); + shared_future& operator=(shared_future&& rhs) noexcept; + + // retrieving the value + void get() const; + + // functions to check state + bool valid() const noexcept; + + void wait() const; + template + future_status + wait_for(const chrono::duration& rel_time) const; + template + future_status + wait_until(const chrono::time_point& abs_time) const; +}; + +template + future::type(typename decay::type...)>::type> + async(F&& f, Args&&... args); + +template + future::type(typename decay::type...)>::type> + async(launch policy, F&& f, Args&&... args); + +template class packaged_task; // undefined + +template +class packaged_task +{ +public: + typedef R result_type; // extension + + // construction and destruction + packaged_task() noexcept; + template + explicit packaged_task(F&& f); + template + packaged_task(allocator_arg_t, const Allocator& a, F&& f); + ~packaged_task(); + + // no copy + packaged_task(const packaged_task&) = delete; + packaged_task& operator=(const packaged_task&) = delete; + + // move support + packaged_task(packaged_task&& other) noexcept; + packaged_task& operator=(packaged_task&& other) noexcept; + void swap(packaged_task& other) noexcept; + + bool valid() const noexcept; + + // result retrieval + future get_future(); + + // execution + void operator()(ArgTypes... ); + void make_ready_at_thread_exit(ArgTypes...); + + void reset(); +}; + +template + void swap(packaged_task&) noexcept; + +template struct uses_allocator, Alloc>; + +} // std + +*/ + +#include "third_party/libcxx/__config" +#include "third_party/libcxx/system_error" +#include "third_party/libcxx/memory" +#include "third_party/libcxx/chrono" +#include "third_party/libcxx/exception" +#include "third_party/libcxx/mutex" +#include "third_party/libcxx/thread" + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +#ifdef _LIBCPP_HAS_NO_THREADS +#error is not supported on this single threaded system +#else // !_LIBCPP_HAS_NO_THREADS + +_LIBCPP_BEGIN_NAMESPACE_STD + +//enum class future_errc +_LIBCPP_DECLARE_STRONG_ENUM(future_errc) +{ + future_already_retrieved = 1, + promise_already_satisfied, + no_state, + broken_promise +}; +_LIBCPP_DECLARE_STRONG_ENUM_EPILOG(future_errc) + +template <> +struct _LIBCPP_TEMPLATE_VIS is_error_code_enum : public true_type {}; + +#ifdef _LIBCPP_HAS_NO_STRONG_ENUMS +template <> +struct _LIBCPP_TEMPLATE_VIS is_error_code_enum : public true_type { }; +#endif + +//enum class launch +_LIBCPP_DECLARE_STRONG_ENUM(launch) +{ + async = 1, + deferred = 2, + any = async | deferred +}; +_LIBCPP_DECLARE_STRONG_ENUM_EPILOG(launch) + +#ifndef _LIBCPP_HAS_NO_STRONG_ENUMS + +typedef underlying_type::type __launch_underlying_type; + +inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_CONSTEXPR +launch +operator&(launch __x, launch __y) +{ + return static_cast(static_cast<__launch_underlying_type>(__x) & + static_cast<__launch_underlying_type>(__y)); +} + +inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_CONSTEXPR +launch +operator|(launch __x, launch __y) +{ + return static_cast(static_cast<__launch_underlying_type>(__x) | + static_cast<__launch_underlying_type>(__y)); +} + +inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_CONSTEXPR +launch +operator^(launch __x, launch __y) +{ + return static_cast(static_cast<__launch_underlying_type>(__x) ^ + static_cast<__launch_underlying_type>(__y)); +} + +inline _LIBCPP_INLINE_VISIBILITY +_LIBCPP_CONSTEXPR +launch +operator~(launch __x) +{ + return static_cast(~static_cast<__launch_underlying_type>(__x) & 3); +} + +inline _LIBCPP_INLINE_VISIBILITY +launch& +operator&=(launch& __x, launch __y) +{ + __x = __x & __y; return __x; +} + +inline _LIBCPP_INLINE_VISIBILITY +launch& +operator|=(launch& __x, launch __y) +{ + __x = __x | __y; return __x; +} + +inline _LIBCPP_INLINE_VISIBILITY +launch& +operator^=(launch& __x, launch __y) +{ + __x = __x ^ __y; return __x; +} + +#endif // !_LIBCPP_HAS_NO_STRONG_ENUMS + +//enum class future_status +_LIBCPP_DECLARE_STRONG_ENUM(future_status) +{ + ready, + timeout, + deferred +}; +_LIBCPP_DECLARE_STRONG_ENUM_EPILOG(future_status) + +_LIBCPP_FUNC_VIS +const error_category& future_category() _NOEXCEPT; + +inline _LIBCPP_INLINE_VISIBILITY +error_code +make_error_code(future_errc __e) _NOEXCEPT +{ + return error_code(static_cast(__e), future_category()); +} + +inline _LIBCPP_INLINE_VISIBILITY +error_condition +make_error_condition(future_errc __e) _NOEXCEPT +{ + return error_condition(static_cast(__e), future_category()); +} + +class _LIBCPP_EXCEPTION_ABI _LIBCPP_AVAILABILITY_FUTURE_ERROR future_error + : public logic_error +{ + error_code __ec_; +public: + future_error(error_code __ec); +#if _LIBCPP_STD_VERS > 14 + explicit future_error(future_errc _Ev) : logic_error(), __ec_(make_error_code(_Ev)) {} +#endif + _LIBCPP_INLINE_VISIBILITY + const error_code& code() const _NOEXCEPT {return __ec_;} + + virtual ~future_error() _NOEXCEPT; +}; + +_LIBCPP_NORETURN inline _LIBCPP_INLINE_VISIBILITY +#ifndef _LIBCPP_NO_EXCEPTIONS +_LIBCPP_AVAILABILITY_FUTURE_ERROR +#endif +void __throw_future_error(future_errc _Ev) +{ +#ifndef _LIBCPP_NO_EXCEPTIONS + throw future_error(make_error_code(_Ev)); +#else + ((void)_Ev); + _VSTD::abort(); +#endif +} + +class _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_FUTURE __assoc_sub_state + : public __shared_count +{ +protected: + exception_ptr __exception_; + mutable mutex __mut_; + mutable condition_variable __cv_; + unsigned __state_; + + virtual void __on_zero_shared() _NOEXCEPT; + void __sub_wait(unique_lock& __lk); +public: + enum + { + __constructed = 1, + __future_attached = 2, + ready = 4, + deferred = 8 + }; + + _LIBCPP_INLINE_VISIBILITY + __assoc_sub_state() : __state_(0) {} + + _LIBCPP_INLINE_VISIBILITY + bool __has_value() const + {return (__state_ & __constructed) || (__exception_ != nullptr);} + + _LIBCPP_INLINE_VISIBILITY + void __attach_future() { + lock_guard __lk(__mut_); + bool __has_future_attached = (__state_ & __future_attached) != 0; + if (__has_future_attached) + __throw_future_error(future_errc::future_already_retrieved); + this->__add_shared(); + __state_ |= __future_attached; + } + + _LIBCPP_INLINE_VISIBILITY + void __set_deferred() {__state_ |= deferred;} + + void __make_ready(); + _LIBCPP_INLINE_VISIBILITY + bool __is_ready() const {return (__state_ & ready) != 0;} + + void set_value(); + void set_value_at_thread_exit(); + + void set_exception(exception_ptr __p); + void set_exception_at_thread_exit(exception_ptr __p); + + void copy(); + + void wait(); + template + future_status + _LIBCPP_INLINE_VISIBILITY + wait_for(const chrono::duration<_Rep, _Period>& __rel_time) const; + template + _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS + future_status + wait_until(const chrono::time_point<_Clock, _Duration>& __abs_time) const; + + virtual void __execute(); +}; + +template +future_status +__assoc_sub_state::wait_until(const chrono::time_point<_Clock, _Duration>& __abs_time) const +{ + unique_lock __lk(__mut_); + if (__state_ & deferred) + return future_status::deferred; + while (!(__state_ & ready) && _Clock::now() < __abs_time) + __cv_.wait_until(__lk, __abs_time); + if (__state_ & ready) + return future_status::ready; + return future_status::timeout; +} + +template +inline +future_status +__assoc_sub_state::wait_for(const chrono::duration<_Rep, _Period>& __rel_time) const +{ + return wait_until(chrono::steady_clock::now() + __rel_time); +} + +template +class _LIBCPP_AVAILABILITY_FUTURE __assoc_state + : public __assoc_sub_state +{ + typedef __assoc_sub_state base; + typedef typename aligned_storage::value>::type _Up; +protected: + _Up __value_; + + virtual void __on_zero_shared() _NOEXCEPT; +public: + + template +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + void set_value(_Arg&& __arg); +#else + void set_value(_Arg& __arg); +#endif + + template +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + void set_value_at_thread_exit(_Arg&& __arg); +#else + void set_value_at_thread_exit(_Arg& __arg); +#endif + + _Rp move(); + typename add_lvalue_reference<_Rp>::type copy(); +}; + +template +void +__assoc_state<_Rp>::__on_zero_shared() _NOEXCEPT +{ + if (this->__state_ & base::__constructed) + reinterpret_cast<_Rp*>(&__value_)->~_Rp(); + delete this; +} + +template +template +_LIBCPP_AVAILABILITY_FUTURE +void +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES +__assoc_state<_Rp>::set_value(_Arg&& __arg) +#else +__assoc_state<_Rp>::set_value(_Arg& __arg) +#endif +{ + unique_lock __lk(this->__mut_); + if (this->__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); + ::new(&__value_) _Rp(_VSTD::forward<_Arg>(__arg)); + this->__state_ |= base::__constructed | base::ready; + __cv_.notify_all(); +} + +template +template +void +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES +__assoc_state<_Rp>::set_value_at_thread_exit(_Arg&& __arg) +#else +__assoc_state<_Rp>::set_value_at_thread_exit(_Arg& __arg) +#endif +{ + unique_lock __lk(this->__mut_); + if (this->__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); + ::new(&__value_) _Rp(_VSTD::forward<_Arg>(__arg)); + this->__state_ |= base::__constructed; + __thread_local_data()->__make_ready_at_thread_exit(this); +} + +template +_Rp +__assoc_state<_Rp>::move() +{ + unique_lock __lk(this->__mut_); + this->__sub_wait(__lk); + if (this->__exception_ != nullptr) + rethrow_exception(this->__exception_); + return _VSTD::move(*reinterpret_cast<_Rp*>(&__value_)); +} + +template +typename add_lvalue_reference<_Rp>::type +__assoc_state<_Rp>::copy() +{ + unique_lock __lk(this->__mut_); + this->__sub_wait(__lk); + if (this->__exception_ != nullptr) + rethrow_exception(this->__exception_); + return *reinterpret_cast<_Rp*>(&__value_); +} + +template +class _LIBCPP_AVAILABILITY_FUTURE __assoc_state<_Rp&> + : public __assoc_sub_state +{ + typedef __assoc_sub_state base; + typedef _Rp* _Up; +protected: + _Up __value_; + + virtual void __on_zero_shared() _NOEXCEPT; +public: + + void set_value(_Rp& __arg); + void set_value_at_thread_exit(_Rp& __arg); + + _Rp& copy(); +}; + +template +void +__assoc_state<_Rp&>::__on_zero_shared() _NOEXCEPT +{ + delete this; +} + +template +void +__assoc_state<_Rp&>::set_value(_Rp& __arg) +{ + unique_lock __lk(this->__mut_); + if (this->__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); + __value_ = _VSTD::addressof(__arg); + this->__state_ |= base::__constructed | base::ready; + __cv_.notify_all(); +} + +template +void +__assoc_state<_Rp&>::set_value_at_thread_exit(_Rp& __arg) +{ + unique_lock __lk(this->__mut_); + if (this->__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); + __value_ = _VSTD::addressof(__arg); + this->__state_ |= base::__constructed; + __thread_local_data()->__make_ready_at_thread_exit(this); +} + +template +_Rp& +__assoc_state<_Rp&>::copy() +{ + unique_lock __lk(this->__mut_); + this->__sub_wait(__lk); + if (this->__exception_ != nullptr) + rethrow_exception(this->__exception_); + return *__value_; +} + +template +class _LIBCPP_AVAILABILITY_FUTURE __assoc_state_alloc + : public __assoc_state<_Rp> +{ + typedef __assoc_state<_Rp> base; + _Alloc __alloc_; + + virtual void __on_zero_shared() _NOEXCEPT; +public: + _LIBCPP_INLINE_VISIBILITY + explicit __assoc_state_alloc(const _Alloc& __a) + : __alloc_(__a) {} +}; + +template +void +__assoc_state_alloc<_Rp, _Alloc>::__on_zero_shared() _NOEXCEPT +{ + if (this->__state_ & base::__constructed) + reinterpret_cast<_Rp*>(_VSTD::addressof(this->__value_))->~_Rp(); + typedef typename __allocator_traits_rebind<_Alloc, __assoc_state_alloc>::type _Al; + typedef allocator_traits<_Al> _ATraits; + typedef pointer_traits _PTraits; + _Al __a(__alloc_); + this->~__assoc_state_alloc(); + __a.deallocate(_PTraits::pointer_to(*this), 1); +} + +template +class _LIBCPP_AVAILABILITY_FUTURE __assoc_state_alloc<_Rp&, _Alloc> + : public __assoc_state<_Rp&> +{ + typedef __assoc_state<_Rp&> base; + _Alloc __alloc_; + + virtual void __on_zero_shared() _NOEXCEPT; +public: + _LIBCPP_INLINE_VISIBILITY + explicit __assoc_state_alloc(const _Alloc& __a) + : __alloc_(__a) {} +}; + +template +void +__assoc_state_alloc<_Rp&, _Alloc>::__on_zero_shared() _NOEXCEPT +{ + typedef typename __allocator_traits_rebind<_Alloc, __assoc_state_alloc>::type _Al; + typedef allocator_traits<_Al> _ATraits; + typedef pointer_traits _PTraits; + _Al __a(__alloc_); + this->~__assoc_state_alloc(); + __a.deallocate(_PTraits::pointer_to(*this), 1); +} + +template +class _LIBCPP_AVAILABILITY_FUTURE __assoc_sub_state_alloc + : public __assoc_sub_state +{ + typedef __assoc_sub_state base; + _Alloc __alloc_; + + virtual void __on_zero_shared() _NOEXCEPT; +public: + _LIBCPP_INLINE_VISIBILITY + explicit __assoc_sub_state_alloc(const _Alloc& __a) + : __alloc_(__a) {} +}; + +template +void +__assoc_sub_state_alloc<_Alloc>::__on_zero_shared() _NOEXCEPT +{ + typedef typename __allocator_traits_rebind<_Alloc, __assoc_sub_state_alloc>::type _Al; + typedef allocator_traits<_Al> _ATraits; + typedef pointer_traits _PTraits; + _Al __a(__alloc_); + this->~__assoc_sub_state_alloc(); + __a.deallocate(_PTraits::pointer_to(*this), 1); +} + +template +class _LIBCPP_AVAILABILITY_FUTURE __deferred_assoc_state + : public __assoc_state<_Rp> +{ + typedef __assoc_state<_Rp> base; + + _Fp __func_; + +public: +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + explicit __deferred_assoc_state(_Fp&& __f); +#endif + + virtual void __execute(); +}; + +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + +template +inline +__deferred_assoc_state<_Rp, _Fp>::__deferred_assoc_state(_Fp&& __f) + : __func_(_VSTD::forward<_Fp>(__f)) +{ + this->__set_deferred(); +} + +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + +template +void +__deferred_assoc_state<_Rp, _Fp>::__execute() +{ +#ifndef _LIBCPP_NO_EXCEPTIONS + try + { +#endif // _LIBCPP_NO_EXCEPTIONS + this->set_value(__func_()); +#ifndef _LIBCPP_NO_EXCEPTIONS + } + catch (...) + { + this->set_exception(current_exception()); + } +#endif // _LIBCPP_NO_EXCEPTIONS +} + +template +class _LIBCPP_AVAILABILITY_FUTURE __deferred_assoc_state + : public __assoc_sub_state +{ + typedef __assoc_sub_state base; + + _Fp __func_; + +public: +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + explicit __deferred_assoc_state(_Fp&& __f); +#endif + + virtual void __execute(); +}; + +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + +template +inline +__deferred_assoc_state::__deferred_assoc_state(_Fp&& __f) + : __func_(_VSTD::forward<_Fp>(__f)) +{ + this->__set_deferred(); +} + +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + +template +void +__deferred_assoc_state::__execute() +{ +#ifndef _LIBCPP_NO_EXCEPTIONS + try + { +#endif // _LIBCPP_NO_EXCEPTIONS + __func_(); + this->set_value(); +#ifndef _LIBCPP_NO_EXCEPTIONS + } + catch (...) + { + this->set_exception(current_exception()); + } +#endif // _LIBCPP_NO_EXCEPTIONS +} + +template +class _LIBCPP_AVAILABILITY_FUTURE __async_assoc_state + : public __assoc_state<_Rp> +{ + typedef __assoc_state<_Rp> base; + + _Fp __func_; + + virtual void __on_zero_shared() _NOEXCEPT; +public: +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + explicit __async_assoc_state(_Fp&& __f); +#endif + + virtual void __execute(); +}; + +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + +template +inline +__async_assoc_state<_Rp, _Fp>::__async_assoc_state(_Fp&& __f) + : __func_(_VSTD::forward<_Fp>(__f)) +{ +} + +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + +template +void +__async_assoc_state<_Rp, _Fp>::__execute() +{ +#ifndef _LIBCPP_NO_EXCEPTIONS + try + { +#endif // _LIBCPP_NO_EXCEPTIONS + this->set_value(__func_()); +#ifndef _LIBCPP_NO_EXCEPTIONS + } + catch (...) + { + this->set_exception(current_exception()); + } +#endif // _LIBCPP_NO_EXCEPTIONS +} + +template +void +__async_assoc_state<_Rp, _Fp>::__on_zero_shared() _NOEXCEPT +{ + this->wait(); + base::__on_zero_shared(); +} + +template +class _LIBCPP_AVAILABILITY_FUTURE __async_assoc_state + : public __assoc_sub_state +{ + typedef __assoc_sub_state base; + + _Fp __func_; + + virtual void __on_zero_shared() _NOEXCEPT; +public: +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + explicit __async_assoc_state(_Fp&& __f); +#endif + + virtual void __execute(); +}; + +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + +template +inline +__async_assoc_state::__async_assoc_state(_Fp&& __f) + : __func_(_VSTD::forward<_Fp>(__f)) +{ +} + +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + +template +void +__async_assoc_state::__execute() +{ +#ifndef _LIBCPP_NO_EXCEPTIONS + try + { +#endif // _LIBCPP_NO_EXCEPTIONS + __func_(); + this->set_value(); +#ifndef _LIBCPP_NO_EXCEPTIONS + } + catch (...) + { + this->set_exception(current_exception()); + } +#endif // _LIBCPP_NO_EXCEPTIONS +} + +template +void +__async_assoc_state::__on_zero_shared() _NOEXCEPT +{ + this->wait(); + base::__on_zero_shared(); +} + +template class _LIBCPP_TEMPLATE_VIS promise; +template class _LIBCPP_TEMPLATE_VIS shared_future; + +// future + +template class _LIBCPP_TEMPLATE_VIS future; + +template +future<_Rp> +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES +__make_deferred_assoc_state(_Fp&& __f); +#else +__make_deferred_assoc_state(_Fp __f); +#endif + +template +future<_Rp> +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES +__make_async_assoc_state(_Fp&& __f); +#else +__make_async_assoc_state(_Fp __f); +#endif + +template +class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FUTURE future +{ + __assoc_state<_Rp>* __state_; + + explicit future(__assoc_state<_Rp>* __state); + + template friend class promise; + template friend class shared_future; + +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + template + friend future<_R1> __make_deferred_assoc_state(_Fp&& __f); + template + friend future<_R1> __make_async_assoc_state(_Fp&& __f); +#else + template + friend future<_R1> __make_deferred_assoc_state(_Fp __f); + template + friend future<_R1> __make_async_assoc_state(_Fp __f); +#endif + +public: + _LIBCPP_INLINE_VISIBILITY + future() _NOEXCEPT : __state_(nullptr) {} +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + future(future&& __rhs) _NOEXCEPT + : __state_(__rhs.__state_) {__rhs.__state_ = nullptr;} + future(const future&) = delete; + future& operator=(const future&) = delete; + _LIBCPP_INLINE_VISIBILITY + future& operator=(future&& __rhs) _NOEXCEPT + { + future(std::move(__rhs)).swap(*this); + return *this; + } +#else // _LIBCPP_HAS_NO_RVALUE_REFERENCES +private: + future(const future&); + future& operator=(const future&); +public: +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + ~future(); + _LIBCPP_INLINE_VISIBILITY + shared_future<_Rp> share() _NOEXCEPT; + + // retrieving the value + _Rp get(); + + _LIBCPP_INLINE_VISIBILITY + void swap(future& __rhs) _NOEXCEPT {_VSTD::swap(__state_, __rhs.__state_);} + + // functions to check state + _LIBCPP_INLINE_VISIBILITY + bool valid() const _NOEXCEPT {return __state_ != nullptr;} + + _LIBCPP_INLINE_VISIBILITY + void wait() const {__state_->wait();} + template + _LIBCPP_INLINE_VISIBILITY + future_status + wait_for(const chrono::duration<_Rep, _Period>& __rel_time) const + {return __state_->wait_for(__rel_time);} + template + _LIBCPP_INLINE_VISIBILITY + future_status + wait_until(const chrono::time_point<_Clock, _Duration>& __abs_time) const + {return __state_->wait_until(__abs_time);} +}; + +template +future<_Rp>::future(__assoc_state<_Rp>* __state) + : __state_(__state) +{ + __state_->__attach_future(); +} + +struct __release_shared_count +{ + void operator()(__shared_count* p) {p->__release_shared();} +}; + +template +future<_Rp>::~future() +{ + if (__state_) + __state_->__release_shared(); +} + +template +_Rp +future<_Rp>::get() +{ + unique_ptr<__shared_count, __release_shared_count> __(__state_); + __assoc_state<_Rp>* __s = __state_; + __state_ = nullptr; + return __s->move(); +} + +template +class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FUTURE future<_Rp&> +{ + __assoc_state<_Rp&>* __state_; + + explicit future(__assoc_state<_Rp&>* __state); + + template friend class promise; + template friend class shared_future; + +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + template + friend future<_R1> __make_deferred_assoc_state(_Fp&& __f); + template + friend future<_R1> __make_async_assoc_state(_Fp&& __f); +#else + template + friend future<_R1> __make_deferred_assoc_state(_Fp __f); + template + friend future<_R1> __make_async_assoc_state(_Fp __f); +#endif + +public: + _LIBCPP_INLINE_VISIBILITY + future() _NOEXCEPT : __state_(nullptr) {} +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + future(future&& __rhs) _NOEXCEPT + : __state_(__rhs.__state_) {__rhs.__state_ = nullptr;} + future(const future&) = delete; + future& operator=(const future&) = delete; + _LIBCPP_INLINE_VISIBILITY + future& operator=(future&& __rhs) _NOEXCEPT + { + future(std::move(__rhs)).swap(*this); + return *this; + } +#else // _LIBCPP_HAS_NO_RVALUE_REFERENCES +private: + future(const future&); + future& operator=(const future&); +public: +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + ~future(); + _LIBCPP_INLINE_VISIBILITY + shared_future<_Rp&> share() _NOEXCEPT; + + // retrieving the value + _Rp& get(); + + _LIBCPP_INLINE_VISIBILITY + void swap(future& __rhs) _NOEXCEPT {_VSTD::swap(__state_, __rhs.__state_);} + + // functions to check state + _LIBCPP_INLINE_VISIBILITY + bool valid() const _NOEXCEPT {return __state_ != nullptr;} + + _LIBCPP_INLINE_VISIBILITY + void wait() const {__state_->wait();} + template + _LIBCPP_INLINE_VISIBILITY + future_status + wait_for(const chrono::duration<_Rep, _Period>& __rel_time) const + {return __state_->wait_for(__rel_time);} + template + _LIBCPP_INLINE_VISIBILITY + future_status + wait_until(const chrono::time_point<_Clock, _Duration>& __abs_time) const + {return __state_->wait_until(__abs_time);} +}; + +template +future<_Rp&>::future(__assoc_state<_Rp&>* __state) + : __state_(__state) +{ + __state_->__attach_future(); +} + +template +future<_Rp&>::~future() +{ + if (__state_) + __state_->__release_shared(); +} + +template +_Rp& +future<_Rp&>::get() +{ + unique_ptr<__shared_count, __release_shared_count> __(__state_); + __assoc_state<_Rp&>* __s = __state_; + __state_ = nullptr; + return __s->copy(); +} + +template <> +class _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_FUTURE future +{ + __assoc_sub_state* __state_; + + explicit future(__assoc_sub_state* __state); + + template friend class promise; + template friend class shared_future; + +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + template + friend future<_R1> __make_deferred_assoc_state(_Fp&& __f); + template + friend future<_R1> __make_async_assoc_state(_Fp&& __f); +#else + template + friend future<_R1> __make_deferred_assoc_state(_Fp __f); + template + friend future<_R1> __make_async_assoc_state(_Fp __f); +#endif + +public: + _LIBCPP_INLINE_VISIBILITY + future() _NOEXCEPT : __state_(nullptr) {} +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + future(future&& __rhs) _NOEXCEPT + : __state_(__rhs.__state_) {__rhs.__state_ = nullptr;} + future(const future&) = delete; + future& operator=(const future&) = delete; + _LIBCPP_INLINE_VISIBILITY + future& operator=(future&& __rhs) _NOEXCEPT + { + future(std::move(__rhs)).swap(*this); + return *this; + } +#else // _LIBCPP_HAS_NO_RVALUE_REFERENCES +private: + future(const future&); + future& operator=(const future&); +public: +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + ~future(); + _LIBCPP_INLINE_VISIBILITY + shared_future share() _NOEXCEPT; + + // retrieving the value + void get(); + + _LIBCPP_INLINE_VISIBILITY + void swap(future& __rhs) _NOEXCEPT {_VSTD::swap(__state_, __rhs.__state_);} + + // functions to check state + _LIBCPP_INLINE_VISIBILITY + bool valid() const _NOEXCEPT {return __state_ != nullptr;} + + _LIBCPP_INLINE_VISIBILITY + void wait() const {__state_->wait();} + template + _LIBCPP_INLINE_VISIBILITY + future_status + wait_for(const chrono::duration<_Rep, _Period>& __rel_time) const + {return __state_->wait_for(__rel_time);} + template + _LIBCPP_INLINE_VISIBILITY + future_status + wait_until(const chrono::time_point<_Clock, _Duration>& __abs_time) const + {return __state_->wait_until(__abs_time);} +}; + +template +inline _LIBCPP_INLINE_VISIBILITY +void +swap(future<_Rp>& __x, future<_Rp>& __y) _NOEXCEPT +{ + __x.swap(__y); +} + +// promise + +template class packaged_task; + +template +class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FUTURE promise +{ + __assoc_state<_Rp>* __state_; + + _LIBCPP_INLINE_VISIBILITY + explicit promise(nullptr_t) _NOEXCEPT : __state_(nullptr) {} + + template friend class packaged_task; +public: + promise(); + template + promise(allocator_arg_t, const _Alloc& __a); +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + promise(promise&& __rhs) _NOEXCEPT + : __state_(__rhs.__state_) {__rhs.__state_ = nullptr;} + promise(const promise& __rhs) = delete; +#else // _LIBCPP_HAS_NO_RVALUE_REFERENCES +private: + promise(const promise& __rhs); +public: +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + ~promise(); + + // assignment +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + promise& operator=(promise&& __rhs) _NOEXCEPT + { + promise(std::move(__rhs)).swap(*this); + return *this; + } + promise& operator=(const promise& __rhs) = delete; +#else // _LIBCPP_HAS_NO_RVALUE_REFERENCES +private: + promise& operator=(const promise& __rhs); +public: +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + void swap(promise& __rhs) _NOEXCEPT {_VSTD::swap(__state_, __rhs.__state_);} + + // retrieving the result + future<_Rp> get_future(); + + // setting the result + void set_value(const _Rp& __r); +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + void set_value(_Rp&& __r); +#endif + void set_exception(exception_ptr __p); + + // setting the result with deferred notification + void set_value_at_thread_exit(const _Rp& __r); +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + void set_value_at_thread_exit(_Rp&& __r); +#endif + void set_exception_at_thread_exit(exception_ptr __p); +}; + +template +promise<_Rp>::promise() + : __state_(new __assoc_state<_Rp>) +{ +} + +template +template +promise<_Rp>::promise(allocator_arg_t, const _Alloc& __a0) +{ + typedef __assoc_state_alloc<_Rp, _Alloc> _State; + typedef typename __allocator_traits_rebind<_Alloc, _State>::type _A2; + typedef __allocator_destructor<_A2> _D2; + _A2 __a(__a0); + unique_ptr<_State, _D2> __hold(__a.allocate(1), _D2(__a, 1)); + ::new(static_cast(_VSTD::addressof(*__hold.get()))) _State(__a0); + __state_ = _VSTD::addressof(*__hold.release()); +} + +template +promise<_Rp>::~promise() +{ + if (__state_) + { + if (!__state_->__has_value() && __state_->use_count() > 1) + __state_->set_exception(make_exception_ptr( + future_error(make_error_code(future_errc::broken_promise)) + )); + __state_->__release_shared(); + } +} + +template +future<_Rp> +promise<_Rp>::get_future() +{ + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + return future<_Rp>(__state_); +} + +template +void +promise<_Rp>::set_value(const _Rp& __r) +{ + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_value(__r); +} + +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + +template +void +promise<_Rp>::set_value(_Rp&& __r) +{ + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_value(_VSTD::move(__r)); +} + +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + +template +void +promise<_Rp>::set_exception(exception_ptr __p) +{ + _LIBCPP_ASSERT( __p != nullptr, "promise::set_exception: received nullptr" ); + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_exception(__p); +} + +template +void +promise<_Rp>::set_value_at_thread_exit(const _Rp& __r) +{ + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_value_at_thread_exit(__r); +} + +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + +template +void +promise<_Rp>::set_value_at_thread_exit(_Rp&& __r) +{ + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_value_at_thread_exit(_VSTD::move(__r)); +} + +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + +template +void +promise<_Rp>::set_exception_at_thread_exit(exception_ptr __p) +{ + _LIBCPP_ASSERT( __p != nullptr, "promise::set_exception_at_thread_exit: received nullptr" ); + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_exception_at_thread_exit(__p); +} + +// promise + +template +class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FUTURE promise<_Rp&> +{ + __assoc_state<_Rp&>* __state_; + + _LIBCPP_INLINE_VISIBILITY + explicit promise(nullptr_t) _NOEXCEPT : __state_(nullptr) {} + + template friend class packaged_task; + +public: + promise(); + template + promise(allocator_arg_t, const _Allocator& __a); +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + promise(promise&& __rhs) _NOEXCEPT + : __state_(__rhs.__state_) {__rhs.__state_ = nullptr;} + promise(const promise& __rhs) = delete; +#else // _LIBCPP_HAS_NO_RVALUE_REFERENCES +private: + promise(const promise& __rhs); +public: +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + ~promise(); + + // assignment +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + promise& operator=(promise&& __rhs) _NOEXCEPT + { + promise(std::move(__rhs)).swap(*this); + return *this; + } + promise& operator=(const promise& __rhs) = delete; +#else // _LIBCPP_HAS_NO_RVALUE_REFERENCES +private: + promise& operator=(const promise& __rhs); +public: +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + void swap(promise& __rhs) _NOEXCEPT {_VSTD::swap(__state_, __rhs.__state_);} + + // retrieving the result + future<_Rp&> get_future(); + + // setting the result + void set_value(_Rp& __r); + void set_exception(exception_ptr __p); + + // setting the result with deferred notification + void set_value_at_thread_exit(_Rp&); + void set_exception_at_thread_exit(exception_ptr __p); +}; + +template +promise<_Rp&>::promise() + : __state_(new __assoc_state<_Rp&>) +{ +} + +template +template +promise<_Rp&>::promise(allocator_arg_t, const _Alloc& __a0) +{ + typedef __assoc_state_alloc<_Rp&, _Alloc> _State; + typedef typename __allocator_traits_rebind<_Alloc, _State>::type _A2; + typedef __allocator_destructor<_A2> _D2; + _A2 __a(__a0); + unique_ptr<_State, _D2> __hold(__a.allocate(1), _D2(__a, 1)); + ::new(static_cast(_VSTD::addressof(*__hold.get()))) _State(__a0); + __state_ = _VSTD::addressof(*__hold.release()); +} + +template +promise<_Rp&>::~promise() +{ + if (__state_) + { + if (!__state_->__has_value() && __state_->use_count() > 1) + __state_->set_exception(make_exception_ptr( + future_error(make_error_code(future_errc::broken_promise)) + )); + __state_->__release_shared(); + } +} + +template +future<_Rp&> +promise<_Rp&>::get_future() +{ + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + return future<_Rp&>(__state_); +} + +template +void +promise<_Rp&>::set_value(_Rp& __r) +{ + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_value(__r); +} + +template +void +promise<_Rp&>::set_exception(exception_ptr __p) +{ + _LIBCPP_ASSERT( __p != nullptr, "promise::set_exception: received nullptr" ); + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_exception(__p); +} + +template +void +promise<_Rp&>::set_value_at_thread_exit(_Rp& __r) +{ + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_value_at_thread_exit(__r); +} + +template +void +promise<_Rp&>::set_exception_at_thread_exit(exception_ptr __p) +{ + _LIBCPP_ASSERT( __p != nullptr, "promise::set_exception_at_thread_exit: received nullptr" ); + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_exception_at_thread_exit(__p); +} + +// promise + +template <> +class _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_FUTURE promise +{ + __assoc_sub_state* __state_; + + _LIBCPP_INLINE_VISIBILITY + explicit promise(nullptr_t) _NOEXCEPT : __state_(nullptr) {} + + template friend class packaged_task; + +public: + promise(); + template + _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS + promise(allocator_arg_t, const _Allocator& __a); +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + promise(promise&& __rhs) _NOEXCEPT + : __state_(__rhs.__state_) {__rhs.__state_ = nullptr;} + promise(const promise& __rhs) = delete; +#else // _LIBCPP_HAS_NO_RVALUE_REFERENCES +private: + promise(const promise& __rhs); +public: +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + ~promise(); + + // assignment +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + promise& operator=(promise&& __rhs) _NOEXCEPT + { + promise(std::move(__rhs)).swap(*this); + return *this; + } + promise& operator=(const promise& __rhs) = delete; +#else // _LIBCPP_HAS_NO_RVALUE_REFERENCES +private: + promise& operator=(const promise& __rhs); +public: +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + void swap(promise& __rhs) _NOEXCEPT {_VSTD::swap(__state_, __rhs.__state_);} + + // retrieving the result + future get_future(); + + // setting the result + void set_value(); + void set_exception(exception_ptr __p); + + // setting the result with deferred notification + void set_value_at_thread_exit(); + void set_exception_at_thread_exit(exception_ptr __p); +}; + +template +promise::promise(allocator_arg_t, const _Alloc& __a0) +{ + typedef __assoc_sub_state_alloc<_Alloc> _State; + typedef typename __allocator_traits_rebind<_Alloc, _State>::type _A2; + typedef __allocator_destructor<_A2> _D2; + _A2 __a(__a0); + unique_ptr<_State, _D2> __hold(__a.allocate(1), _D2(__a, 1)); + ::new(static_cast(_VSTD::addressof(*__hold.get()))) _State(__a0); + __state_ = _VSTD::addressof(*__hold.release()); +} + +template +inline _LIBCPP_INLINE_VISIBILITY +void +swap(promise<_Rp>& __x, promise<_Rp>& __y) _NOEXCEPT +{ + __x.swap(__y); +} + +template + struct _LIBCPP_TEMPLATE_VIS uses_allocator, _Alloc> + : public true_type {}; + +#ifndef _LIBCPP_HAS_NO_VARIADICS + +// packaged_task + +template class __packaged_task_base; + +template +class _LIBCPP_AVAILABILITY_FUTURE __packaged_task_base<_Rp(_ArgTypes...)> +{ + __packaged_task_base(const __packaged_task_base&); + __packaged_task_base& operator=(const __packaged_task_base&); +public: + _LIBCPP_INLINE_VISIBILITY + __packaged_task_base() {} + _LIBCPP_INLINE_VISIBILITY + virtual ~__packaged_task_base() {} + virtual void __move_to(__packaged_task_base*) _NOEXCEPT = 0; + virtual void destroy() = 0; + virtual void destroy_deallocate() = 0; + virtual _Rp operator()(_ArgTypes&& ...) = 0; +}; + +template class __packaged_task_func; + +template +class _LIBCPP_AVAILABILITY_FUTURE __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)> + : public __packaged_task_base<_Rp(_ArgTypes...)> +{ + __compressed_pair<_Fp, _Alloc> __f_; +public: + _LIBCPP_INLINE_VISIBILITY + explicit __packaged_task_func(const _Fp& __f) : __f_(__f) {} + _LIBCPP_INLINE_VISIBILITY + explicit __packaged_task_func(_Fp&& __f) : __f_(_VSTD::move(__f)) {} + _LIBCPP_INLINE_VISIBILITY + __packaged_task_func(const _Fp& __f, const _Alloc& __a) + : __f_(__f, __a) {} + _LIBCPP_INLINE_VISIBILITY + __packaged_task_func(_Fp&& __f, const _Alloc& __a) + : __f_(_VSTD::move(__f), __a) {} + virtual void __move_to(__packaged_task_base<_Rp(_ArgTypes...)>*) _NOEXCEPT; + virtual void destroy(); + virtual void destroy_deallocate(); + virtual _Rp operator()(_ArgTypes&& ... __args); +}; + +template +void +__packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)>::__move_to( + __packaged_task_base<_Rp(_ArgTypes...)>* __p) _NOEXCEPT +{ + ::new (__p) __packaged_task_func(_VSTD::move(__f_.first()), _VSTD::move(__f_.second())); +} + +template +void +__packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)>::destroy() +{ + __f_.~__compressed_pair<_Fp, _Alloc>(); +} + +template +void +__packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)>::destroy_deallocate() +{ + typedef typename __allocator_traits_rebind<_Alloc, __packaged_task_func>::type _Ap; + typedef allocator_traits<_Ap> _ATraits; + typedef pointer_traits _PTraits; + _Ap __a(__f_.second()); + __f_.~__compressed_pair<_Fp, _Alloc>(); + __a.deallocate(_PTraits::pointer_to(*this), 1); +} + +template +_Rp +__packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)>::operator()(_ArgTypes&& ... __arg) +{ + return __invoke(__f_.first(), _VSTD::forward<_ArgTypes>(__arg)...); +} + +template class __packaged_task_function; + +template +class _LIBCPP_AVAILABILITY_FUTURE __packaged_task_function<_Rp(_ArgTypes...)> +{ + typedef __packaged_task_base<_Rp(_ArgTypes...)> __base; + typename aligned_storage<3*sizeof(void*)>::type __buf_; + __base* __f_; + +public: + typedef _Rp result_type; + + // construct/copy/destroy: + _LIBCPP_INLINE_VISIBILITY + __packaged_task_function() _NOEXCEPT : __f_(nullptr) {} + template + __packaged_task_function(_Fp&& __f); + template + __packaged_task_function(allocator_arg_t, const _Alloc& __a, _Fp&& __f); + + __packaged_task_function(__packaged_task_function&&) _NOEXCEPT; + __packaged_task_function& operator=(__packaged_task_function&&) _NOEXCEPT; + + __packaged_task_function(const __packaged_task_function&) = delete; + __packaged_task_function& operator=(const __packaged_task_function&) = delete; + + ~__packaged_task_function(); + + void swap(__packaged_task_function&) _NOEXCEPT; + + _LIBCPP_INLINE_VISIBILITY + _Rp operator()(_ArgTypes...) const; +}; + +template +__packaged_task_function<_Rp(_ArgTypes...)>::__packaged_task_function(__packaged_task_function&& __f) _NOEXCEPT +{ + if (__f.__f_ == nullptr) + __f_ = nullptr; + else if (__f.__f_ == (__base*)&__f.__buf_) + { + __f_ = (__base*)&__buf_; + __f.__f_->__move_to(__f_); + } + else + { + __f_ = __f.__f_; + __f.__f_ = nullptr; + } +} + +template +template +__packaged_task_function<_Rp(_ArgTypes...)>::__packaged_task_function(_Fp&& __f) + : __f_(nullptr) +{ + typedef typename remove_reference::type>::type _FR; + typedef __packaged_task_func<_FR, allocator<_FR>, _Rp(_ArgTypes...)> _FF; + if (sizeof(_FF) <= sizeof(__buf_)) + { + __f_ = (__base*)&__buf_; + ::new (__f_) _FF(_VSTD::forward<_Fp>(__f)); + } + else + { + typedef allocator<_FF> _Ap; + _Ap __a; + typedef __allocator_destructor<_Ap> _Dp; + unique_ptr<__base, _Dp> __hold(__a.allocate(1), _Dp(__a, 1)); + ::new (__hold.get()) _FF(_VSTD::forward<_Fp>(__f), allocator<_FR>(__a)); + __f_ = __hold.release(); + } +} + +template +template +__packaged_task_function<_Rp(_ArgTypes...)>::__packaged_task_function( + allocator_arg_t, const _Alloc& __a0, _Fp&& __f) + : __f_(nullptr) +{ + typedef typename remove_reference::type>::type _FR; + typedef __packaged_task_func<_FR, _Alloc, _Rp(_ArgTypes...)> _FF; + if (sizeof(_FF) <= sizeof(__buf_)) + { + __f_ = (__base*)&__buf_; + ::new (__f_) _FF(_VSTD::forward<_Fp>(__f)); + } + else + { + typedef typename __allocator_traits_rebind<_Alloc, _FF>::type _Ap; + _Ap __a(__a0); + typedef __allocator_destructor<_Ap> _Dp; + unique_ptr<__base, _Dp> __hold(__a.allocate(1), _Dp(__a, 1)); + ::new (static_cast(_VSTD::addressof(*__hold.get()))) + _FF(_VSTD::forward<_Fp>(__f), _Alloc(__a)); + __f_ = _VSTD::addressof(*__hold.release()); + } +} + +template +__packaged_task_function<_Rp(_ArgTypes...)>& +__packaged_task_function<_Rp(_ArgTypes...)>::operator=(__packaged_task_function&& __f) _NOEXCEPT +{ + if (__f_ == (__base*)&__buf_) + __f_->destroy(); + else if (__f_) + __f_->destroy_deallocate(); + __f_ = nullptr; + if (__f.__f_ == nullptr) + __f_ = nullptr; + else if (__f.__f_ == (__base*)&__f.__buf_) + { + __f_ = (__base*)&__buf_; + __f.__f_->__move_to(__f_); + } + else + { + __f_ = __f.__f_; + __f.__f_ = nullptr; + } + return *this; +} + +template +__packaged_task_function<_Rp(_ArgTypes...)>::~__packaged_task_function() +{ + if (__f_ == (__base*)&__buf_) + __f_->destroy(); + else if (__f_) + __f_->destroy_deallocate(); +} + +template +void +__packaged_task_function<_Rp(_ArgTypes...)>::swap(__packaged_task_function& __f) _NOEXCEPT +{ + if (__f_ == (__base*)&__buf_ && __f.__f_ == (__base*)&__f.__buf_) + { + typename aligned_storage::type __tempbuf; + __base* __t = (__base*)&__tempbuf; + __f_->__move_to(__t); + __f_->destroy(); + __f_ = nullptr; + __f.__f_->__move_to((__base*)&__buf_); + __f.__f_->destroy(); + __f.__f_ = nullptr; + __f_ = (__base*)&__buf_; + __t->__move_to((__base*)&__f.__buf_); + __t->destroy(); + __f.__f_ = (__base*)&__f.__buf_; + } + else if (__f_ == (__base*)&__buf_) + { + __f_->__move_to((__base*)&__f.__buf_); + __f_->destroy(); + __f_ = __f.__f_; + __f.__f_ = (__base*)&__f.__buf_; + } + else if (__f.__f_ == (__base*)&__f.__buf_) + { + __f.__f_->__move_to((__base*)&__buf_); + __f.__f_->destroy(); + __f.__f_ = __f_; + __f_ = (__base*)&__buf_; + } + else + _VSTD::swap(__f_, __f.__f_); +} + +template +inline +_Rp +__packaged_task_function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const +{ + return (*__f_)(_VSTD::forward<_ArgTypes>(__arg)...); +} + +template +class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FUTURE packaged_task<_Rp(_ArgTypes...)> +{ +public: + typedef _Rp result_type; // extension + +private: + __packaged_task_function __f_; + promise __p_; + +public: + // construction and destruction + _LIBCPP_INLINE_VISIBILITY + packaged_task() _NOEXCEPT : __p_(nullptr) {} + template ::type, + packaged_task + >::value + >::type + > + _LIBCPP_INLINE_VISIBILITY + explicit packaged_task(_Fp&& __f) : __f_(_VSTD::forward<_Fp>(__f)) {} + template ::type, + packaged_task + >::value + >::type + > + _LIBCPP_INLINE_VISIBILITY + packaged_task(allocator_arg_t, const _Allocator& __a, _Fp&& __f) + : __f_(allocator_arg, __a, _VSTD::forward<_Fp>(__f)), + __p_(allocator_arg, __a) {} + // ~packaged_task() = default; + + // no copy + packaged_task(const packaged_task&) = delete; + packaged_task& operator=(const packaged_task&) = delete; + + // move support + _LIBCPP_INLINE_VISIBILITY + packaged_task(packaged_task&& __other) _NOEXCEPT + : __f_(_VSTD::move(__other.__f_)), __p_(_VSTD::move(__other.__p_)) {} + _LIBCPP_INLINE_VISIBILITY + packaged_task& operator=(packaged_task&& __other) _NOEXCEPT + { + __f_ = _VSTD::move(__other.__f_); + __p_ = _VSTD::move(__other.__p_); + return *this; + } + _LIBCPP_INLINE_VISIBILITY + void swap(packaged_task& __other) _NOEXCEPT + { + __f_.swap(__other.__f_); + __p_.swap(__other.__p_); + } + + _LIBCPP_INLINE_VISIBILITY + bool valid() const _NOEXCEPT {return __p_.__state_ != nullptr;} + + // result retrieval + _LIBCPP_INLINE_VISIBILITY + future get_future() {return __p_.get_future();} + + // execution + void operator()(_ArgTypes... __args); + void make_ready_at_thread_exit(_ArgTypes... __args); + + void reset(); +}; + +template +void +packaged_task<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __args) +{ + if (__p_.__state_ == nullptr) + __throw_future_error(future_errc::no_state); + if (__p_.__state_->__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); +#ifndef _LIBCPP_NO_EXCEPTIONS + try + { +#endif // _LIBCPP_NO_EXCEPTIONS + __p_.set_value(__f_(_VSTD::forward<_ArgTypes>(__args)...)); +#ifndef _LIBCPP_NO_EXCEPTIONS + } + catch (...) + { + __p_.set_exception(current_exception()); + } +#endif // _LIBCPP_NO_EXCEPTIONS +} + +template +void +packaged_task<_Rp(_ArgTypes...)>::make_ready_at_thread_exit(_ArgTypes... __args) +{ + if (__p_.__state_ == nullptr) + __throw_future_error(future_errc::no_state); + if (__p_.__state_->__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); +#ifndef _LIBCPP_NO_EXCEPTIONS + try + { +#endif // _LIBCPP_NO_EXCEPTIONS + __p_.set_value_at_thread_exit(__f_(_VSTD::forward<_ArgTypes>(__args)...)); +#ifndef _LIBCPP_NO_EXCEPTIONS + } + catch (...) + { + __p_.set_exception_at_thread_exit(current_exception()); + } +#endif // _LIBCPP_NO_EXCEPTIONS +} + +template +void +packaged_task<_Rp(_ArgTypes...)>::reset() +{ + if (!valid()) + __throw_future_error(future_errc::no_state); + __p_ = promise(); +} + +template +class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FUTURE packaged_task +{ +public: + typedef void result_type; // extension + +private: + __packaged_task_function __f_; + promise __p_; + +public: + // construction and destruction + _LIBCPP_INLINE_VISIBILITY + packaged_task() _NOEXCEPT : __p_(nullptr) {} + template ::type, + packaged_task + >::value + >::type + > + _LIBCPP_INLINE_VISIBILITY + explicit packaged_task(_Fp&& __f) : __f_(_VSTD::forward<_Fp>(__f)) {} + template ::type, + packaged_task + >::value + >::type + > + _LIBCPP_INLINE_VISIBILITY + packaged_task(allocator_arg_t, const _Allocator& __a, _Fp&& __f) + : __f_(allocator_arg, __a, _VSTD::forward<_Fp>(__f)), + __p_(allocator_arg, __a) {} + // ~packaged_task() = default; + + // no copy + packaged_task(const packaged_task&) = delete; + packaged_task& operator=(const packaged_task&) = delete; + + // move support + _LIBCPP_INLINE_VISIBILITY + packaged_task(packaged_task&& __other) _NOEXCEPT + : __f_(_VSTD::move(__other.__f_)), __p_(_VSTD::move(__other.__p_)) {} + _LIBCPP_INLINE_VISIBILITY + packaged_task& operator=(packaged_task&& __other) _NOEXCEPT + { + __f_ = _VSTD::move(__other.__f_); + __p_ = _VSTD::move(__other.__p_); + return *this; + } + _LIBCPP_INLINE_VISIBILITY + void swap(packaged_task& __other) _NOEXCEPT + { + __f_.swap(__other.__f_); + __p_.swap(__other.__p_); + } + + _LIBCPP_INLINE_VISIBILITY + bool valid() const _NOEXCEPT {return __p_.__state_ != nullptr;} + + // result retrieval + _LIBCPP_INLINE_VISIBILITY + future get_future() {return __p_.get_future();} + + // execution + void operator()(_ArgTypes... __args); + void make_ready_at_thread_exit(_ArgTypes... __args); + + void reset(); +}; + +template +void +packaged_task::operator()(_ArgTypes... __args) +{ + if (__p_.__state_ == nullptr) + __throw_future_error(future_errc::no_state); + if (__p_.__state_->__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); +#ifndef _LIBCPP_NO_EXCEPTIONS + try + { +#endif // _LIBCPP_NO_EXCEPTIONS + __f_(_VSTD::forward<_ArgTypes>(__args)...); + __p_.set_value(); +#ifndef _LIBCPP_NO_EXCEPTIONS + } + catch (...) + { + __p_.set_exception(current_exception()); + } +#endif // _LIBCPP_NO_EXCEPTIONS +} + +template +void +packaged_task::make_ready_at_thread_exit(_ArgTypes... __args) +{ + if (__p_.__state_ == nullptr) + __throw_future_error(future_errc::no_state); + if (__p_.__state_->__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); +#ifndef _LIBCPP_NO_EXCEPTIONS + try + { +#endif // _LIBCPP_NO_EXCEPTIONS + __f_(_VSTD::forward<_ArgTypes>(__args)...); + __p_.set_value_at_thread_exit(); +#ifndef _LIBCPP_NO_EXCEPTIONS + } + catch (...) + { + __p_.set_exception_at_thread_exit(current_exception()); + } +#endif // _LIBCPP_NO_EXCEPTIONS +} + +template +void +packaged_task::reset() +{ + if (!valid()) + __throw_future_error(future_errc::no_state); + __p_ = promise(); +} + +template +inline _LIBCPP_INLINE_VISIBILITY +void +swap(packaged_task<_Callable>& __x, packaged_task<_Callable>& __y) _NOEXCEPT +{ + __x.swap(__y); +} + +template +struct _LIBCPP_TEMPLATE_VIS uses_allocator, _Alloc> + : public true_type {}; + +template +future<_Rp> +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES +__make_deferred_assoc_state(_Fp&& __f) +#else +__make_deferred_assoc_state(_Fp __f) +#endif +{ + unique_ptr<__deferred_assoc_state<_Rp, _Fp>, __release_shared_count> + __h(new __deferred_assoc_state<_Rp, _Fp>(_VSTD::forward<_Fp>(__f))); + return future<_Rp>(__h.get()); +} + +template +future<_Rp> +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES +__make_async_assoc_state(_Fp&& __f) +#else +__make_async_assoc_state(_Fp __f) +#endif +{ + unique_ptr<__async_assoc_state<_Rp, _Fp>, __release_shared_count> + __h(new __async_assoc_state<_Rp, _Fp>(_VSTD::forward<_Fp>(__f))); + _VSTD::thread(&__async_assoc_state<_Rp, _Fp>::__execute, __h.get()).detach(); + return future<_Rp>(__h.get()); +} + +template +class __async_func +{ + tuple<_Fp, _Args...> __f_; + +public: + typedef typename __invoke_of<_Fp, _Args...>::type _Rp; + + _LIBCPP_INLINE_VISIBILITY + explicit __async_func(_Fp&& __f, _Args&&... __args) + : __f_(_VSTD::move(__f), _VSTD::move(__args)...) {} + + _LIBCPP_INLINE_VISIBILITY + __async_func(__async_func&& __f) : __f_(_VSTD::move(__f.__f_)) {} + + _Rp operator()() + { + typedef typename __make_tuple_indices<1+sizeof...(_Args), 1>::type _Index; + return __execute(_Index()); + } +private: + template + _Rp + __execute(__tuple_indices<_Indices...>) + { + return __invoke(_VSTD::move(_VSTD::get<0>(__f_)), _VSTD::move(_VSTD::get<_Indices>(__f_))...); + } +}; + +inline _LIBCPP_INLINE_VISIBILITY bool __does_policy_contain(launch __policy, launch __value ) +{ return (int(__policy) & int(__value)) != 0; } + +template +_LIBCPP_NODISCARD_AFTER_CXX17 +future::type, typename decay<_Args>::type...>::type> +async(launch __policy, _Fp&& __f, _Args&&... __args) +{ + typedef __async_func::type, typename decay<_Args>::type...> _BF; + typedef typename _BF::_Rp _Rp; + +#ifndef _LIBCPP_NO_EXCEPTIONS + try + { +#endif + if (__does_policy_contain(__policy, launch::async)) + return _VSTD::__make_async_assoc_state<_Rp>(_BF(__decay_copy(_VSTD::forward<_Fp>(__f)), + __decay_copy(_VSTD::forward<_Args>(__args))...)); +#ifndef _LIBCPP_NO_EXCEPTIONS + } + catch ( ... ) { if (__policy == launch::async) throw ; } +#endif + + if (__does_policy_contain(__policy, launch::deferred)) + return _VSTD::__make_deferred_assoc_state<_Rp>(_BF(__decay_copy(_VSTD::forward<_Fp>(__f)), + __decay_copy(_VSTD::forward<_Args>(__args))...)); + return future<_Rp>{}; +} + +template +_LIBCPP_NODISCARD_AFTER_CXX17 inline _LIBCPP_INLINE_VISIBILITY +future::type, typename decay<_Args>::type...>::type> +async(_Fp&& __f, _Args&&... __args) +{ + return _VSTD::async(launch::any, _VSTD::forward<_Fp>(__f), + _VSTD::forward<_Args>(__args)...); +} + +#endif // _LIBCPP_HAS_NO_VARIADICS + +// shared_future + +template +class _LIBCPP_TEMPLATE_VIS shared_future +{ + __assoc_state<_Rp>* __state_; + +public: + _LIBCPP_INLINE_VISIBILITY + shared_future() _NOEXCEPT : __state_(nullptr) {} + _LIBCPP_INLINE_VISIBILITY + shared_future(const shared_future& __rhs) _NOEXCEPT : __state_(__rhs.__state_) + {if (__state_) __state_->__add_shared();} +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + shared_future(future<_Rp>&& __f) _NOEXCEPT : __state_(__f.__state_) + {__f.__state_ = nullptr;} + _LIBCPP_INLINE_VISIBILITY + shared_future(shared_future&& __rhs) _NOEXCEPT : __state_(__rhs.__state_) + {__rhs.__state_ = nullptr;} +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + ~shared_future(); + shared_future& operator=(const shared_future& __rhs) _NOEXCEPT; +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + shared_future& operator=(shared_future&& __rhs) _NOEXCEPT + { + shared_future(std::move(__rhs)).swap(*this); + return *this; + } +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + + // retrieving the value + _LIBCPP_INLINE_VISIBILITY + const _Rp& get() const {return __state_->copy();} + + _LIBCPP_INLINE_VISIBILITY + void swap(shared_future& __rhs) _NOEXCEPT {_VSTD::swap(__state_, __rhs.__state_);} + + // functions to check state + _LIBCPP_INLINE_VISIBILITY + bool valid() const _NOEXCEPT {return __state_ != nullptr;} + + _LIBCPP_INLINE_VISIBILITY + void wait() const {__state_->wait();} + template + _LIBCPP_INLINE_VISIBILITY + future_status + wait_for(const chrono::duration<_Rep, _Period>& __rel_time) const + {return __state_->wait_for(__rel_time);} + template + _LIBCPP_INLINE_VISIBILITY + future_status + wait_until(const chrono::time_point<_Clock, _Duration>& __abs_time) const + {return __state_->wait_until(__abs_time);} +}; + +template +shared_future<_Rp>::~shared_future() +{ + if (__state_) + __state_->__release_shared(); +} + +template +shared_future<_Rp>& +shared_future<_Rp>::operator=(const shared_future& __rhs) _NOEXCEPT +{ + if (__rhs.__state_) + __rhs.__state_->__add_shared(); + if (__state_) + __state_->__release_shared(); + __state_ = __rhs.__state_; + return *this; +} + +template +class _LIBCPP_TEMPLATE_VIS shared_future<_Rp&> +{ + __assoc_state<_Rp&>* __state_; + +public: + _LIBCPP_INLINE_VISIBILITY + shared_future() _NOEXCEPT : __state_(nullptr) {} + _LIBCPP_INLINE_VISIBILITY + shared_future(const shared_future& __rhs) : __state_(__rhs.__state_) + {if (__state_) __state_->__add_shared();} +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + shared_future(future<_Rp&>&& __f) _NOEXCEPT : __state_(__f.__state_) + {__f.__state_ = nullptr;} + _LIBCPP_INLINE_VISIBILITY + shared_future(shared_future&& __rhs) _NOEXCEPT : __state_(__rhs.__state_) + {__rhs.__state_ = nullptr;} +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + ~shared_future(); + shared_future& operator=(const shared_future& __rhs); +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + shared_future& operator=(shared_future&& __rhs) _NOEXCEPT + { + shared_future(std::move(__rhs)).swap(*this); + return *this; + } +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + + // retrieving the value + _LIBCPP_INLINE_VISIBILITY + _Rp& get() const {return __state_->copy();} + + _LIBCPP_INLINE_VISIBILITY + void swap(shared_future& __rhs) _NOEXCEPT {_VSTD::swap(__state_, __rhs.__state_);} + + // functions to check state + _LIBCPP_INLINE_VISIBILITY + bool valid() const _NOEXCEPT {return __state_ != nullptr;} + + _LIBCPP_INLINE_VISIBILITY + void wait() const {__state_->wait();} + template + _LIBCPP_INLINE_VISIBILITY + future_status + wait_for(const chrono::duration<_Rep, _Period>& __rel_time) const + {return __state_->wait_for(__rel_time);} + template + _LIBCPP_INLINE_VISIBILITY + future_status + wait_until(const chrono::time_point<_Clock, _Duration>& __abs_time) const + {return __state_->wait_until(__abs_time);} +}; + +template +shared_future<_Rp&>::~shared_future() +{ + if (__state_) + __state_->__release_shared(); +} + +template +shared_future<_Rp&>& +shared_future<_Rp&>::operator=(const shared_future& __rhs) +{ + if (__rhs.__state_) + __rhs.__state_->__add_shared(); + if (__state_) + __state_->__release_shared(); + __state_ = __rhs.__state_; + return *this; +} + +template <> +class _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_FUTURE shared_future +{ + __assoc_sub_state* __state_; + +public: + _LIBCPP_INLINE_VISIBILITY + shared_future() _NOEXCEPT : __state_(nullptr) {} + _LIBCPP_INLINE_VISIBILITY + shared_future(const shared_future& __rhs) : __state_(__rhs.__state_) + {if (__state_) __state_->__add_shared();} +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + shared_future(future&& __f) _NOEXCEPT : __state_(__f.__state_) + {__f.__state_ = nullptr;} + _LIBCPP_INLINE_VISIBILITY + shared_future(shared_future&& __rhs) _NOEXCEPT : __state_(__rhs.__state_) + {__rhs.__state_ = nullptr;} +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + ~shared_future(); + shared_future& operator=(const shared_future& __rhs); +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + _LIBCPP_INLINE_VISIBILITY + shared_future& operator=(shared_future&& __rhs) _NOEXCEPT + { + shared_future(std::move(__rhs)).swap(*this); + return *this; + } +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + + // retrieving the value + _LIBCPP_INLINE_VISIBILITY + void get() const {__state_->copy();} + + _LIBCPP_INLINE_VISIBILITY + void swap(shared_future& __rhs) _NOEXCEPT {_VSTD::swap(__state_, __rhs.__state_);} + + // functions to check state + _LIBCPP_INLINE_VISIBILITY + bool valid() const _NOEXCEPT {return __state_ != nullptr;} + + _LIBCPP_INLINE_VISIBILITY + void wait() const {__state_->wait();} + template + _LIBCPP_INLINE_VISIBILITY + future_status + wait_for(const chrono::duration<_Rep, _Period>& __rel_time) const + {return __state_->wait_for(__rel_time);} + template + _LIBCPP_INLINE_VISIBILITY + future_status + wait_until(const chrono::time_point<_Clock, _Duration>& __abs_time) const + {return __state_->wait_until(__abs_time);} +}; + +template +inline _LIBCPP_INLINE_VISIBILITY +void +swap(shared_future<_Rp>& __x, shared_future<_Rp>& __y) _NOEXCEPT +{ + __x.swap(__y); +} + +template +inline +shared_future<_Rp> +future<_Rp>::share() _NOEXCEPT +{ + return shared_future<_Rp>(_VSTD::move(*this)); +} + +template +inline +shared_future<_Rp&> +future<_Rp&>::share() _NOEXCEPT +{ + return shared_future<_Rp&>(_VSTD::move(*this)); +} + +#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES + +inline +shared_future +future::share() _NOEXCEPT +{ + return shared_future(_VSTD::move(*this)); +} + +#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES + +_LIBCPP_END_NAMESPACE_STD + +#endif // !_LIBCPP_HAS_NO_THREADS + +#endif // _LIBCPP_FUTURE diff --git a/third_party/libcxx/future.cc b/third_party/libcxx/future.cc new file mode 100644 index 000000000..90fcb5ad9 --- /dev/null +++ b/third_party/libcxx/future.cc @@ -0,0 +1,278 @@ +// clang-format off +//===------------------------- future.cpp ---------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "third_party/libcxx/__config" + +#ifndef _LIBCPP_HAS_NO_THREADS + +#include "third_party/libcxx/future" +#include "third_party/libcxx/string" + +_LIBCPP_BEGIN_NAMESPACE_STD + +class _LIBCPP_HIDDEN __future_error_category + : public __do_message +{ +public: + virtual const char* name() const _NOEXCEPT; + virtual string message(int ev) const; +}; + +const char* +__future_error_category::name() const _NOEXCEPT +{ + return "future"; +} + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wswitch" +#elif defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" +#endif + +string +__future_error_category::message(int ev) const +{ + switch (static_cast(ev)) + { + case future_errc(0): // For backwards compatibility with C++11 (LWG 2056) + case future_errc::broken_promise: + return string("The associated promise has been destructed prior " + "to the associated state becoming ready."); + case future_errc::future_already_retrieved: + return string("The future has already been retrieved from " + "the promise or packaged_task."); + case future_errc::promise_already_satisfied: + return string("The state of the promise has already been set."); + case future_errc::no_state: + return string("Operation not permitted on an object without " + "an associated state."); + } + return string("unspecified future_errc value\n"); +} + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic pop +#endif + +const error_category& +future_category() _NOEXCEPT +{ + static __future_error_category __f; + return __f; +} + +future_error::future_error(error_code __ec) + : logic_error(__ec.message()), + __ec_(__ec) +{ +} + +future_error::~future_error() _NOEXCEPT +{ +} + +void +__assoc_sub_state::__on_zero_shared() _NOEXCEPT +{ + delete this; +} + +void +__assoc_sub_state::set_value() +{ + unique_lock __lk(__mut_); + if (__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); + __state_ |= __constructed | ready; + __cv_.notify_all(); +} + +void +__assoc_sub_state::set_value_at_thread_exit() +{ + unique_lock __lk(__mut_); + if (__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); + __state_ |= __constructed; + __thread_local_data()->__make_ready_at_thread_exit(this); +} + +void +__assoc_sub_state::set_exception(exception_ptr __p) +{ + unique_lock __lk(__mut_); + if (__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); + __exception_ = __p; + __state_ |= ready; + __cv_.notify_all(); +} + +void +__assoc_sub_state::set_exception_at_thread_exit(exception_ptr __p) +{ + unique_lock __lk(__mut_); + if (__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); + __exception_ = __p; + __thread_local_data()->__make_ready_at_thread_exit(this); +} + +void +__assoc_sub_state::__make_ready() +{ + unique_lock __lk(__mut_); + __state_ |= ready; + __cv_.notify_all(); +} + +void +__assoc_sub_state::copy() +{ + unique_lock __lk(__mut_); + __sub_wait(__lk); + if (__exception_ != nullptr) + rethrow_exception(__exception_); +} + +void +__assoc_sub_state::wait() +{ + unique_lock __lk(__mut_); + __sub_wait(__lk); +} + +void +__assoc_sub_state::__sub_wait(unique_lock& __lk) +{ + if (!__is_ready()) + { + if (__state_ & static_cast(deferred)) + { + __state_ &= ~static_cast(deferred); + __lk.unlock(); + __execute(); + } + else + while (!__is_ready()) + __cv_.wait(__lk); + } +} + +void +__assoc_sub_state::__execute() +{ + __throw_future_error(future_errc::no_state); +} + +future::future(__assoc_sub_state* __state) + : __state_(__state) +{ + __state_->__attach_future(); +} + +future::~future() +{ + if (__state_) + __state_->__release_shared(); +} + +void +future::get() +{ + unique_ptr<__shared_count, __release_shared_count> __(__state_); + __assoc_sub_state* __s = __state_; + __state_ = nullptr; + __s->copy(); +} + +promise::promise() + : __state_(new __assoc_sub_state) +{ +} + +promise::~promise() +{ + if (__state_) + { +#ifndef _LIBCPP_NO_EXCEPTIONS + if (!__state_->__has_value() && __state_->use_count() > 1) + __state_->set_exception(make_exception_ptr( + future_error(make_error_code(future_errc::broken_promise)) + )); +#endif // _LIBCPP_NO_EXCEPTIONS + __state_->__release_shared(); + } +} + +future +promise::get_future() +{ + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + return future(__state_); +} + +void +promise::set_value() +{ + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_value(); +} + +void +promise::set_exception(exception_ptr __p) +{ + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_exception(__p); +} + +void +promise::set_value_at_thread_exit() +{ + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_value_at_thread_exit(); +} + +void +promise::set_exception_at_thread_exit(exception_ptr __p) +{ + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_exception_at_thread_exit(__p); +} + +shared_future::~shared_future() +{ + if (__state_) + __state_->__release_shared(); +} + +shared_future& +shared_future::operator=(const shared_future& __rhs) +{ + if (__rhs.__state_) + __rhs.__state_->__add_shared(); + if (__state_) + __state_->__release_shared(); + __state_ = __rhs.__state_; + return *this; +} + +_LIBCPP_END_NAMESPACE_STD + +#endif // !_LIBCPP_HAS_NO_THREADS diff --git a/third_party/libcxx/libcxx.mk b/third_party/libcxx/libcxx.mk index 473a95091..a8c98f37a 100644 --- a/third_party/libcxx/libcxx.mk +++ b/third_party/libcxx/libcxx.mk @@ -78,6 +78,7 @@ THIRD_PARTY_LIBCXX_A_HDRS = \ third_party/libcxx/forward_list \ third_party/libcxx/fstream \ third_party/libcxx/functional \ + third_party/libcxx/future \ third_party/libcxx/include/atomic_support.hh \ third_party/libcxx/include/config_elast.hh \ third_party/libcxx/initializer_list \ @@ -144,6 +145,7 @@ THIRD_PARTY_LIBCXX_A_SRCS_CC = \ third_party/libcxx/condition_variable_destructor.cc \ third_party/libcxx/exception.cc \ third_party/libcxx/functional.cc \ + third_party/libcxx/future.cc \ third_party/libcxx/hash.cc \ third_party/libcxx/ios.cc \ third_party/libcxx/iostream.cc \ @@ -161,6 +163,7 @@ THIRD_PARTY_LIBCXX_A_SRCS_CC = \ third_party/libcxx/string.cc \ third_party/libcxx/strstream.cc \ third_party/libcxx/system_error.cc \ + third_party/libcxx/thread.cc \ third_party/libcxx/valarray.cc \ third_party/libcxx/vector.cc @@ -189,6 +192,7 @@ THIRD_PARTY_LIBCXX_A_DIRECTDEPS = \ LIBC_STUBS \ LIBC_SYSV \ LIBC_TIME \ + LIBC_THREAD \ LIBC_TINYMATH \ THIRD_PARTY_GDTOA diff --git a/third_party/libcxx/memory b/third_party/libcxx/memory index 95073694b..a1634471c 100644 --- a/third_party/libcxx/memory +++ b/third_party/libcxx/memory @@ -25,7 +25,7 @@ #include "third_party/libcxx/stdexcept" #include "third_party/libcxx/cstring" #if !defined(_LIBCPP_HAS_NO_ATOMIC_HEADER) -# include "third_party/libcxx/atomic" +#include "third_party/libcxx/atomic" #endif #include "third_party/libcxx/version" @@ -1736,7 +1736,7 @@ struct _LIBCPP_TEMPLATE_VIS allocator_traits ptrdiff_t _Np = __end1 - __begin1; __end2 -= _Np; if (_Np > 0) - _VSTD::memcpy(__end2, __begin1, _Np * sizeof(_Tp)); + _VSTD::memcpy((void *)__end2, __begin1, _Np * sizeof(_Tp)); } private: diff --git a/third_party/libcxx/thread.cc b/third_party/libcxx/thread.cc new file mode 100644 index 000000000..eff899582 --- /dev/null +++ b/third_party/libcxx/thread.cc @@ -0,0 +1,267 @@ +// clang-format off +//===------------------------- thread.cpp----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "third_party/libcxx/__config" +#ifndef _LIBCPP_HAS_NO_THREADS + +#include "third_party/libcxx/thread" +#include "third_party/libcxx/exception" +#include "third_party/libcxx/vector" +#include "third_party/libcxx/future" +#include "third_party/libcxx/limits" +#include "libc/calls/makedev.h" +#include "libc/calls/weirdtypes.h" +#include "libc/intrin/newbie.h" +#include "libc/calls/typedef/u.h" +#include "libc/calls/weirdtypes.h" +#include "libc/sock/select.h" +#include "libc/sysv/consts/endian.h" + +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#include "libc/intrin/newbie.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/rlimit.h" +#include "libc/calls/struct/rusage.h" +#include "libc/calls/sysparam.h" +#include "libc/calls/weirdtypes.h" +#include "libc/limits.h" +#include "libc/sysv/consts/endian.h" +#include "libc/sysv/consts/prio.h" +#include "libc/sysv/consts/rlim.h" +#include "libc/sysv/consts/rlimit.h" +#include "libc/sysv/consts/rusage.h" +# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__) +// MISSING #include +# endif +#endif // defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) + +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__CloudABI__) || defined(__Fuchsia__) || defined(__wasi__) +#include "libc/calls/calls.h" +#include "libc/calls/weirdtypes.h" +#include "libc/runtime/sysconf.h" +#include "libc/sysv/consts/fileno.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/ok.h" +#include "third_party/getopt/getopt.h" +#endif // defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__CloudABI__) || defined(__Fuchsia__) || defined(__wasi__) + +#if defined(__NetBSD__) +#pragma weak pthread_create // Do not create libpthread dependency +#endif + +#if defined(_LIBCPP_WIN32API) +#include "libc/nt/accounting.h" +#include "libc/nt/automation.h" +#include "libc/nt/console.h" +#include "libc/nt/debug.h" +#include "libc/nt/dll.h" +#include "libc/nt/enum/keyaccess.h" +#include "libc/nt/enum/regtype.h" +#include "libc/nt/errors.h" +#include "libc/nt/events.h" +#include "libc/nt/files.h" +#include "libc/nt/ipc.h" +#include "libc/nt/memory.h" +#include "libc/nt/paint.h" +#include "libc/nt/process.h" +#include "libc/nt/registry.h" +#include "libc/nt/synchronization.h" +#include "libc/nt/thread.h" +#include "libc/nt/windows.h" +#include "libc/nt/winsock.h" +#endif + +#if defined(__unix__) && !defined(__ANDROID__) && defined(__ELF__) && defined(_LIBCPP_HAS_COMMENT_LIB_PRAGMA) +#pragma comment(lib, "pthread") +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +thread::~thread() +{ + if (!__libcpp_thread_isnull(&__t_)) + terminate(); +} + +void +thread::join() +{ + int ec = EINVAL; + if (!__libcpp_thread_isnull(&__t_)) + { + ec = __libcpp_thread_join(&__t_); + if (ec == 0) + __t_ = _LIBCPP_NULL_THREAD; + } + + if (ec) + __throw_system_error(ec, "thread::join failed"); +} + +void +thread::detach() +{ + int ec = EINVAL; + if (!__libcpp_thread_isnull(&__t_)) + { + ec = __libcpp_thread_detach(&__t_); + if (ec == 0) + __t_ = _LIBCPP_NULL_THREAD; + } + + if (ec) + __throw_system_error(ec, "thread::detach failed"); +} + +unsigned +thread::hardware_concurrency() _NOEXCEPT +{ +#if defined(CTL_HW) && defined(HW_NCPU) + unsigned n; + int mib[2] = {CTL_HW, HW_NCPU}; + std::size_t s = sizeof(n); + sysctl(mib, 2, &n, &s, 0, 0); + return n; +#elif defined(_SC_NPROCESSORS_ONLN) + long result = sysconf(_SC_NPROCESSORS_ONLN); + // sysconf returns -1 if the name is invalid, the option does not exist or + // does not have a definite limit. + // if sysconf returns some other negative number, we have no idea + // what is going on. Default to something safe. + if (result < 0) + return 0; + return static_cast(result); +#elif defined(_LIBCPP_WIN32API) + SYSTEM_INFO info; + GetSystemInfo(&info); + return info.dwNumberOfProcessors; +#else // defined(CTL_HW) && defined(HW_NCPU) + // TODO: grovel through /proc or check cpuid on x86 and similar + // instructions on other architectures. +# if defined(_LIBCPP_WARNING) + _LIBCPP_WARNING("hardware_concurrency not yet implemented") +# else +# warning hardware_concurrency not yet implemented +# endif + return 0; // Means not computable [thread.thread.static] +#endif // defined(CTL_HW) && defined(HW_NCPU) +} + +namespace this_thread +{ + +void +sleep_for(const chrono::nanoseconds& ns) +{ + if (ns > chrono::nanoseconds::zero()) + { + __libcpp_thread_sleep_for(ns); + } +} + +} // this_thread + +__thread_specific_ptr<__thread_struct>& +__thread_local_data() +{ + static __thread_specific_ptr<__thread_struct> __p; + return __p; +} + +// __thread_struct_imp + +template +class _LIBCPP_HIDDEN __hidden_allocator +{ +public: + typedef T value_type; + + T* allocate(size_t __n) + {return static_cast(::operator new(__n * sizeof(T)));} + void deallocate(T* __p, size_t) {::operator delete(static_cast(__p));} + + size_t max_size() const {return size_t(~0) / sizeof(T);} +}; + +class _LIBCPP_HIDDEN __thread_struct_imp +{ + typedef vector<__assoc_sub_state*, + __hidden_allocator<__assoc_sub_state*> > _AsyncStates; + typedef vector, + __hidden_allocator > > _Notify; + + _AsyncStates async_states_; + _Notify notify_; + + __thread_struct_imp(const __thread_struct_imp&); + __thread_struct_imp& operator=(const __thread_struct_imp&); +public: + __thread_struct_imp() {} + ~__thread_struct_imp(); + + void notify_all_at_thread_exit(condition_variable* cv, mutex* m); + void __make_ready_at_thread_exit(__assoc_sub_state* __s); +}; + +__thread_struct_imp::~__thread_struct_imp() +{ + for (_Notify::iterator i = notify_.begin(), e = notify_.end(); + i != e; ++i) + { + i->second->unlock(); + i->first->notify_all(); + } + for (_AsyncStates::iterator i = async_states_.begin(), e = async_states_.end(); + i != e; ++i) + { + (*i)->__make_ready(); + (*i)->__release_shared(); + } +} + +void +__thread_struct_imp::notify_all_at_thread_exit(condition_variable* cv, mutex* m) +{ + notify_.push_back(pair(cv, m)); +} + +void +__thread_struct_imp::__make_ready_at_thread_exit(__assoc_sub_state* __s) +{ + async_states_.push_back(__s); + __s->__add_shared(); +} + +// __thread_struct + +__thread_struct::__thread_struct() + : __p_(new __thread_struct_imp) +{ +} + +__thread_struct::~__thread_struct() +{ + delete __p_; +} + +void +__thread_struct::notify_all_at_thread_exit(condition_variable* cv, mutex* m) +{ + __p_->notify_all_at_thread_exit(cv, m); +} + +void +__thread_struct::__make_ready_at_thread_exit(__assoc_sub_state* __s) +{ + __p_->__make_ready_at_thread_exit(__s); +} + +_LIBCPP_END_NAMESPACE_STD + +#endif // !_LIBCPP_HAS_NO_THREADS diff --git a/tool/scripts/fix-third-party.py b/tool/scripts/fix-third-party.py index a262c460f..686a5bc8b 100755 --- a/tool/scripts/fix-third-party.py +++ b/tool/scripts/fix-third-party.py @@ -41,8 +41,8 @@ def FixQuotedPath(path, incl): return os.path.normpath(p2) def FixThirdParty(path): - if not path.endswith(EXTENSIONS): - return + # if not path.endswith(EXTENSIONS): + # return print(path) with open(path) as f: