Implement POSIX threads API

This commit is contained in:
Justine Tunney 2022-09-05 08:26:03 -07:00
parent af24f21556
commit 9be364d40a
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
95 changed files with 6029 additions and 317 deletions

View file

@ -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 =

0
libc/bits/atomic.h Executable file
View file

View file

@ -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);

View file

@ -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_???";
}

View file

@ -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) */

View file

@ -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);
}

View file

@ -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);
}

0
libc/intrin/intrin.h Executable file
View file

View file

@ -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 \

View file

@ -2,6 +2,8 @@
#define COSMOPOLITAN_LIBC_INTRIN_ONCE_H_
#include "libc/intrin/spinlock.h"
/* TODO(jart): DELETE */
#define _once(x) \
({ \
typeof(x) oncerc; \

View file

@ -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_

16
libc/intrin/pthread2.h Normal file
View file

@ -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_ */

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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;
}
}
}

View file

@ -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);
}
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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

View file

@ -3,6 +3,8 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/* TODO(jart): DELETE */
#ifdef TINY
#define _spinlock(lock) _spinlock_tiny(lock)
#else

View file

@ -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();
}
}

View file

@ -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_ */

View file

@ -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_ */

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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) */

View file

@ -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;
}

75
libc/thread/zombie.c Normal file
View file

@ -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);
}

View file

@ -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));
}

View file

@ -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));
}

View file

@ -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());

View file

@ -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));
}

View file

@ -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);
}

View file

@ -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));
}

View file

@ -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));
}

View file

@ -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)

View file

@ -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

File diff suppressed because it is too large Load diff

2609
third_party/libcxx/future vendored Normal file

File diff suppressed because it is too large Load diff

278
third_party/libcxx/future.cc vendored Normal file
View file

@ -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<future_errc>(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<mutex> __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<mutex> __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<mutex> __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<mutex> __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<mutex> __lk(__mut_);
__state_ |= ready;
__cv_.notify_all();
}
void
__assoc_sub_state::copy()
{
unique_lock<mutex> __lk(__mut_);
__sub_wait(__lk);
if (__exception_ != nullptr)
rethrow_exception(__exception_);
}
void
__assoc_sub_state::wait()
{
unique_lock<mutex> __lk(__mut_);
__sub_wait(__lk);
}
void
__assoc_sub_state::__sub_wait(unique_lock<mutex>& __lk)
{
if (!__is_ready())
{
if (__state_ & static_cast<unsigned>(deferred))
{
__state_ &= ~static_cast<unsigned>(deferred);
__lk.unlock();
__execute();
}
else
while (!__is_ready())
__cv_.wait(__lk);
}
}
void
__assoc_sub_state::__execute()
{
__throw_future_error(future_errc::no_state);
}
future<void>::future(__assoc_sub_state* __state)
: __state_(__state)
{
__state_->__attach_future();
}
future<void>::~future()
{
if (__state_)
__state_->__release_shared();
}
void
future<void>::get()
{
unique_ptr<__shared_count, __release_shared_count> __(__state_);
__assoc_sub_state* __s = __state_;
__state_ = nullptr;
__s->copy();
}
promise<void>::promise()
: __state_(new __assoc_sub_state)
{
}
promise<void>::~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<void>
promise<void>::get_future()
{
if (__state_ == nullptr)
__throw_future_error(future_errc::no_state);
return future<void>(__state_);
}
void
promise<void>::set_value()
{
if (__state_ == nullptr)
__throw_future_error(future_errc::no_state);
__state_->set_value();
}
void
promise<void>::set_exception(exception_ptr __p)
{
if (__state_ == nullptr)
__throw_future_error(future_errc::no_state);
__state_->set_exception(__p);
}
void
promise<void>::set_value_at_thread_exit()
{
if (__state_ == nullptr)
__throw_future_error(future_errc::no_state);
__state_->set_value_at_thread_exit();
}
void
promise<void>::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<void>::~shared_future()
{
if (__state_)
__state_->__release_shared();
}
shared_future<void>&
shared_future<void>::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

View file

@ -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

View file

@ -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:

267
third_party/libcxx/thread.cc vendored Normal file
View file

@ -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 <sys/sysctl.h>
# 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<unsigned>(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 T>
class _LIBCPP_HIDDEN __hidden_allocator
{
public:
typedef T value_type;
T* allocate(size_t __n)
{return static_cast<T*>(::operator new(__n * sizeof(T)));}
void deallocate(T* __p, size_t) {::operator delete(static_cast<void*>(__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<pair<condition_variable*, mutex*>,
__hidden_allocator<pair<condition_variable*, mutex*> > > _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<condition_variable*, mutex*>(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

View file

@ -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: