Add pthread attributes and other libc functions

This commit is contained in:
Justine Tunney 2022-09-07 05:23:44 -07:00
parent d5c9308a43
commit 4339d9f15e
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
81 changed files with 1111 additions and 428 deletions

View file

@ -4,10 +4,8 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
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;
int _futex_wait(void *, int, char, struct timespec *) hidden;
int _futex_wake(void *, int, char) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -28,13 +28,14 @@
int _futex(void *, int, int, struct timespec *) hidden;
static dontinline int _futex_wait_impl(void *addr, int expect,
struct timespec *timeout, int private) {
int op, ax;
int _futex_wait(void *addr, int expect, char pshared,
struct timespec *timeout) {
int op, ax, pf;
if (IsLinux() || IsOpenbsd()) {
op = FUTEX_WAIT | private;
pf = pshared == PTHREAD_PROCESS_PRIVATE ? FUTEX_PRIVATE_FLAG : 0;
op = FUTEX_WAIT | pf;
ax = _futex(addr, op, expect, timeout);
if (SupportsLinux() && private && ax == -ENOSYS) {
if (SupportsLinux() && pf && ax == -ENOSYS) {
// RHEL5 doesn't support FUTEX_PRIVATE_FLAG
op = FUTEX_WAIT;
ax = _futex(addr, op, expect, timeout);
@ -47,11 +48,3 @@ static dontinline int _futex_wait_impl(void *addr, int expect,
return pthread_yield();
}
}
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

@ -21,16 +21,18 @@
#include "libc/errno.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/futex.internal.h"
#include "libc/intrin/pthread.h"
#include "libc/sysv/consts/futex.h"
int _futex(void *, int, int) hidden;
static dontinline int _futex_wake_impl(void *addr, int count, int private) {
int op, ax;
int _futex_wake(void *addr, int count, char pshared) {
int op, ax, pf;
if (IsLinux() || IsOpenbsd()) {
op = FUTEX_WAKE | private;
pf = pshared == PTHREAD_PROCESS_PRIVATE ? FUTEX_PRIVATE_FLAG : 0;
op = FUTEX_WAKE | pf;
ax = _futex(addr, op, count);
if (SupportsLinux() && private && ax == -ENOSYS) {
if (SupportsLinux() && pf && ax == -ENOSYS) {
// RHEL5 doesn't support FUTEX_PRIVATE_FLAG
op = FUTEX_WAKE;
ax = _futex(addr, op, count);
@ -42,11 +44,3 @@ static dontinline int _futex_wake_impl(void *addr, int count, int private) {
return 0;
}
}
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);
}

View file

@ -31,12 +31,10 @@ struct Fds g_fds;
static pthread_mutex_t __fds_lock_obj;
void(__fds_lock)(void) {
__fds_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_lock(&__fds_lock_obj);
}
void(__fds_unlock)(void) {
__fds_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_unlock(&__fds_lock_obj);
}
@ -51,6 +49,7 @@ static textwindows dontinline void SetupWinStd(struct Fds *fds, int i, int x) {
textstartup void InitializeFileDescriptors(void) {
struct Fds *fds;
__fds_lock_obj.type = PTHREAD_MUTEX_RECURSIVE;
fds = VEIL("r", &g_fds);
pushmov(&fds->n, ARRAYLEN(fds->__init_p));
fds->f = 3;

View file

@ -4,7 +4,7 @@
#define PTHREAD_ONCE_INIT 0
#define PTHREAD_KEYS_MAX 64
#define PTHREAD_STACK_MIN 2048
#define PTHREAD_STACK_MIN FRAMESIZE
#define PTHREAD_DESTRUCTOR_ITERATIONS 4
#define PTHREAD_BARRIER_SERIAL_THREAD 31337
@ -20,60 +20,70 @@
#define PTHREAD_PROCESS_PRIVATE 0
#define PTHREAD_PROCESS_SHARED 1
#define PTHREAD_CREATE_JOINABLE 0
#define PTHREAD_CREATE_DETACHED 1
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/* clang-format off */
#define PTHREAD_MUTEX_INITIALIZER {PTHREAD_MUTEX_DEFAULT}
#define PTHREAD_COND_INITIALIZER {PTHREAD_PROCESS_DEFAULT}
#define PTHREAD_BARRIER_INITIALIZER {PTHREAD_PROCESS_DEFAULT}
#define PTHREAD_RWLOCK_INITIALIZER {PTHREAD_PROCESS_DEFAULT}
#define PTHREAD_MUTEX_INITIALIZER {PTHREAD_MUTEX_DEFAULT, \
PTHREAD_PROCESS_DEFAULT}
/* clang-format on */
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 char pthread_condattr_t;
typedef char pthread_rwlockattr_t;
typedef char 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 {
int attr;
typedef struct pthread_mutex_s {
char type;
char pshared;
int reent;
_Atomic(int) lock;
_Atomic(int) waits;
} pthread_mutex_t;
typedef struct {
int attr;
typedef struct pthread_mutexattr_s {
char type;
char pshared;
} pthread_mutexattr_t;
typedef struct pthread_cond_s {
char pshared;
_Atomic(int) waits;
_Atomic(unsigned) seq;
} pthread_cond_t;
typedef struct {
int attr;
typedef struct pthread_barrier_s {
char pshared;
int count;
_Atomic(int) waits;
_Atomic(int) popped;
} pthread_barrier_t;
typedef struct {
int attr;
typedef struct pthread_rwlock_s {
char pshared;
_Atomic(int) lock;
_Atomic(int) waits;
} pthread_rwlock_t;
typedef struct {
typedef struct pthread_attr_s {
char detachstate;
size_t stacksize;
size_t guardsize;
void *stackaddr;
int scope;
int schedpolicy;
int detachstate;
int inheritsched;
size_t guardsize;
size_t stacksize;
} pthread_attr_t;
int pthread_yield(void);
@ -81,6 +91,7 @@ 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_getattr_np(pthread_t, pthread_attr_t *);
int pthread_attr_init(pthread_attr_t *);
int pthread_attr_destroy(pthread_attr_t *);
int pthread_attr_getdetachstate(const pthread_attr_t *, int *);
@ -156,7 +167,6 @@ int pthread_barrier_init(pthread_barrier_t *, const pthread_barrierattr_t *,
#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) \
@ -164,18 +174,18 @@ extern const errno_t EBUSY;
#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)
#define pthread_spin_lock(pSpin) _pthread_spin_lock_tiny(pSpin)
#else
#define pthread_spin_lock(pSpin) __pthread_spin_lock_cooperative(pSpin)
#define pthread_spin_lock(pSpin) _pthread_spin_lock_cooperative(pSpin)
#endif
#define __pthread_spin_lock_tiny(pSpin) \
#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) \
#define _pthread_spin_lock_cooperative(pSpin) \
({ \
char __x; \
volatile int __i; \
@ -197,59 +207,6 @@ extern const errno_t EBUSY;
})
#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 : PTHREAD_MUTEX_DEFAULT, \
}; \
0; \
})
#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_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_ */

View file

@ -3,6 +3,7 @@
#include "libc/calls/struct/sched_param.h"
#include "libc/calls/struct/timespec.h"
#include "libc/intrin/pthread.h"
#include "libc/runtime/stack.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_

View file

@ -23,6 +23,6 @@
* Destroys pthread attributes.
*/
int pthread_attr_destroy(pthread_attr_t *attr) {
bzero(attr, sizeof(*attr));
memset(attr, -1, sizeof(*attr));
return 0;
}

View file

@ -18,7 +18,15 @@
*/
#include "libc/intrin/pthread.h"
int pthread_attr_getdetachstate(const pthread_attr_t *a, int *x) {
*x = a->detachstate;
/**
* Gets thread detachable attribute.
*
* @param detachstate is set to one of the following
* - `PTHREAD_CREATE_JOINABLE` (default)
* - `PTHREAD_CREATE_DETACHED`
* @return 0 on success, or error on failure
*/
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) {
*detachstate = attr->detachstate;
return 0;
}

View file

@ -18,7 +18,13 @@
*/
#include "libc/intrin/pthread.h"
int pthread_attr_getguardsize(const pthread_attr_t *a, size_t *x) {
*x = a->guardsize;
/**
* Returns size of unmapped pages at bottom of stack.
*
* @param guardsize will be set to guard size in bytes
* @return 0 on success, or errno on error
*/
int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize) {
*guardsize = attr->guardsize;
return 0;
}

View file

@ -17,8 +17,22 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pthread.h"
#include "libc/runtime/stack.h"
/**
* Returns size of thread stack.
*
* This defaults to GetStackSize().
*
* @param x will be set to stack size in bytes
* @return 0 on success, or errno on error
* @see pthread_attr_setstacksize()
*/
int pthread_attr_getstacksize(const pthread_attr_t *a, size_t *x) {
*x = a->stacksize;
if (a->stacksize) {
*x = a->stacksize;
} else {
*x = GetStackSize();
}
return 0;
}

View file

@ -17,12 +17,18 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pthread.h"
#include "libc/str/str.h"
#include "libc/runtime/stack.h"
/**
* Initializes pthread attributes.
*
* @return 0 on success, or errno on error
*/
int pthread_attr_init(pthread_attr_t *attr) {
bzero(attr, sizeof(*attr));
*attr = (pthread_attr_t){
.detachstate = PTHREAD_CREATE_JOINABLE,
.stacksize = GetStackSize(),
.guardsize = PAGESIZE,
};
return 0;
}

View file

@ -16,9 +16,31 @@
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"
int pthread_attr_setdetachstate(pthread_attr_t *a, int x) {
a->detachstate = x;
return 0;
/**
* Sets thread detachable attribute, e.g.
*
* pthread_attr_t attr;
* pthread_attr_init(&attr);
* pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
* pthread_create(0, &attr, func, 0);
* pthread_attr_destroy(&attr);
*
* @param detachstate can be one of
* - `PTHREAD_CREATE_JOINABLE` (default)
* - `PTHREAD_CREATE_DETACHED`
* @return 0 on success, or error on failure
* @raises EINVAL if `detachstate` is invalid
*/
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) {
switch (detachstate) {
case PTHREAD_CREATE_JOINABLE:
case PTHREAD_CREATE_DETACHED:
attr->detachstate = detachstate;
return 0;
default:
return EINVAL;
}
}

View file

@ -16,9 +16,21 @@
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"
#include "libc/macros.internal.h"
int pthread_attr_setguardsize(pthread_attr_t *a, size_t x) {
a->guardsize = x;
/**
* Sets size of unmapped pages at bottom of stack.
*
* Cosmopolitan Libc stack guards always default to 4096 bytes. Setting
* `guardsize` to zero will disable automatic creation of guard pages.
* Your `guardsize` will be rounded up to `PAGESIZE`.
*
* @param guardsize contains guard size in bytes
* @return 0 on success, or errno on error
*/
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) {
attr->guardsize = ROUNDUP(guardsize, PAGESIZE);
return 0;
}

View file

@ -16,9 +16,38 @@
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 size of thread stack.
*
* Your stack must have at least `PTHREAD_STACK_MIN` bytes, which
* Cosmpolitan Libc defines as `GetStackSize()`. It's a link-time
* constant used by Actually Portable Executable that's 128 kb by
* default. See libc/runtime/stack.h for docs on your stack limit
* since the APE ELF phdrs are the one true source of truth here.
*
* Cosmpolitan Libc runtime magic (e.g. ftrace) and memory safety
* (e.g. kprintf) assumes that stack sizes are two-powers and are
* aligned to that two-power. Conformance isn't required since we
* say caveat emptor to those who don't maintain these invariants
*
* Unlike pthread_attr_setstack() this function should be used if
* you want the Cosmopolitan Libc runtime to allocate a stack for
* you. Since the runtime uses mmap(MAP_STACK) to do that, you'll
* need to choose a multiple of FRAMESIZE, due to Windows.
*
* If this function isn't called it'll default to GetStackSize().
*
* @param x contains stack size in bytes
* @return 0 on success, or errno on error
* @raise EINVAL if `x` is less than `PTHREAD_STACK_MIN`
*/
int pthread_attr_setstacksize(pthread_attr_t *a, size_t x) {
if (x < PTHREAD_STACK_MIN) return EINVAL;
if (x & (FRAMESIZE - 1)) return EINVAL;
if (x < FRAMESIZE) return EINVAL;
a->stacksize = x;
return 0;
}

View file

@ -19,6 +19,7 @@
#include "libc/assert.h"
#include "libc/errno.h"
#include "libc/intrin/pthread.h"
#include "libc/str/str.h"
/**
* Destroys barrier.
@ -31,6 +32,6 @@ int pthread_barrier_destroy(pthread_barrier_t *barrier) {
assert(!"deadlock");
return EINVAL;
}
*barrier = (pthread_barrier_t)PTHREAD_BARRIER_INITIALIZER;
memset(barrier, -1, sizeof(*barrier));
return 0;
}

View file

@ -42,11 +42,7 @@ int pthread_barrier_wait(pthread_barrier_t *barrier) {
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);
}
_futex_wake(&barrier->popped, INT_MAX, barrier->pshared);
} else {
pthread_yield();
}
@ -59,11 +55,7 @@ int pthread_barrier_wait(pthread_barrier_t *barrier) {
}
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);
}
_futex_wait(&barrier->popped, 0, barrier->pshared, 0);
} else {
pthread_yield();
}

View file

@ -17,12 +17,14 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pthread.h"
#include "libc/str/str.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);
int pthread_barrierattr_destroy(pthread_barrierattr_t *attr) {
memset(attr, -1, sizeof(*attr));
return 0;
}

View file

@ -26,7 +26,8 @@
* - `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);
int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr,
int *pshared) {
*pshared = *attr;
return 0;
}

View file

@ -23,6 +23,7 @@
*
* @return 0 on success, or error on failure
*/
int(pthread_barrierattr_init)(pthread_barrierattr_t *attr) {
return pthread_barrierattr_init(attr);
int pthread_barrierattr_init(pthread_barrierattr_t *attr) {
*attr = PTHREAD_PROCESS_DEFAULT;
return 0;
}

View file

@ -26,11 +26,7 @@ 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);
}
_futex_wake(&cond->seq, n, cond->pshared);
}
}
return 0;

View file

@ -19,6 +19,7 @@
#include "libc/assert.h"
#include "libc/errno.h"
#include "libc/intrin/pthread.h"
#include "libc/str/str.h"
/**
* Destroys condition.
@ -31,6 +32,6 @@ int pthread_cond_destroy(pthread_cond_t *cond) {
assert(!"deadlock");
return EINVAL;
}
*cond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
memset(cond, -1, sizeof(*cond));
return 0;
}

View file

@ -24,6 +24,7 @@
* @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);
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) {
*cond = (pthread_cond_t){attr ? *attr : PTHREAD_PROCESS_DEFAULT};
return 0;
}

View file

@ -17,12 +17,14 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pthread.h"
#include "libc/str/str.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);
int pthread_condattr_destroy(pthread_condattr_t *attr) {
memset(attr, -1, sizeof(*attr));
return 0;
}

View file

@ -26,6 +26,7 @@
* - `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);
int pthread_condattr_getpshared(const pthread_condattr_t *attr, int *pshared) {
*pshared = *attr;
return 0;
}

View file

@ -23,6 +23,7 @@
*
* @return 0 on success, or error on failure
*/
int(pthread_condattr_init)(pthread_condattr_t *attr) {
return pthread_condattr_init(attr);
int pthread_condattr_init(pthread_condattr_t *attr) {
*attr = PTHREAD_PROCESS_DEFAULT;
return 0;
}

View file

@ -19,6 +19,7 @@
#include "libc/assert.h"
#include "libc/errno.h"
#include "libc/intrin/pthread.h"
#include "libc/str/str.h"
/**
* Destroys mutex.
@ -31,6 +32,6 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex) {
assert(!"deadlock");
return EINVAL;
}
*mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
memset(mutex, -1, sizeof(*mutex));
return 0;
}

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pthread.h"
#include "libc/str/str.h"
/**
* Initializes mutex.
@ -25,7 +24,11 @@
* @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) {
return pthread_mutex_init(mutex, attr);
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr) {
*mutex = (pthread_mutex_t){
attr ? attr->type : PTHREAD_MUTEX_DEFAULT,
attr ? attr->pshared : PTHREAD_PROCESS_DEFAULT,
};
return 0;
}

View file

@ -39,7 +39,7 @@ 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_private(&mutex->lock, expect, &(struct timespec){1});
_futex_wait(&mutex->lock, expect, mutex->pshared, &(struct timespec){1});
atomic_fetch_sub(&mutex->waits, 1);
} else {
pthread_yield();
@ -87,24 +87,24 @@ static int pthread_mutex_lock_spin(pthread_mutex_t *mutex, int expect,
*
* Microbenchmarks for single-threaded lock + unlock:
*
* pthread_spinlock_t : 12c ( 4ns)
* PTHREAD_MUTEX_NORMAL : 37c ( 12ns)
* PTHREAD_MUTEX_RECURSIVE : 22c ( 7ns)
* PTHREAD_MUTEX_ERRORCHECK : 27c ( 9ns)
* pthread_spinlock_t : 12c ( 4ns)
* PTHREAD_MUTEX_NORMAL : 37c ( 12ns)
* PTHREAD_MUTEX_RECURSIVE : 22c ( 7ns)
* PTHREAD_MUTEX_ERRORCHECK : 27c ( 9ns)
*
* Microbenchmarks for multi-threaded lock + unlock:
*
* pthread_spinlock_t : 6,162c (1,990ns)
* PTHREAD_MUTEX_NORMAL : 780c ( 252ns)
* PTHREAD_MUTEX_RECURSIVE : 1,047c ( 338ns)
* PTHREAD_MUTEX_ERRORCHECK : 1,044c ( 337ns)
* pthread_spinlock_t : 2,396c (774ns)
* PTHREAD_MUTEX_NORMAL : 535c (173ns)
* PTHREAD_MUTEX_RECURSIVE : 1,045c (338ns)
* PTHREAD_MUTEX_ERRORCHECK : 917c (296ns)
*
* @return 0 on success, or error number on failure
* @see pthread_spin_lock
*/
int pthread_mutex_lock(pthread_mutex_t *mutex) {
int c, me, owner, tries;
switch (mutex->attr) {
switch (mutex->type) {
case PTHREAD_MUTEX_NORMAL:
// From Futexes Are Tricky Version 1.1 § Mutex, Take 3;
// Ulrich Drepper, Red Hat Incorporated, June 27, 2004.
@ -116,7 +116,7 @@ int pthread_mutex_lock(pthread_mutex_t *mutex) {
c = atomic_exchange_explicit(&mutex->lock, 2, memory_order_acquire);
}
while (c) {
_futex_wait_private(&mutex->lock, 2, 0);
_futex_wait(&mutex->lock, 2, mutex->pshared, 0);
c = atomic_exchange_explicit(&mutex->lock, 2, memory_order_acquire);
}
}
@ -130,7 +130,7 @@ int pthread_mutex_lock(pthread_mutex_t *mutex) {
memory_order_relaxed)) {
break;
} else if (owner == me) {
if (mutex->attr != PTHREAD_MUTEX_ERRORCHECK) {
if (mutex->type != PTHREAD_MUTEX_ERRORCHECK) {
break;
} else {
assert(!"deadlock");

View file

@ -34,7 +34,7 @@
*/
int pthread_mutex_trylock(pthread_mutex_t *mutex) {
int c, me, owner;
switch (mutex->attr) {
switch (mutex->type) {
case PTHREAD_MUTEX_NORMAL:
c = 0;
if (atomic_compare_exchange_strong_explicit(&mutex->lock, &c, 1,
@ -52,7 +52,7 @@ int pthread_mutex_trylock(pthread_mutex_t *mutex) {
memory_order_acquire,
memory_order_relaxed)) {
if (owner == me) {
if (mutex->attr == PTHREAD_MUTEX_ERRORCHECK) {
if (mutex->type == PTHREAD_MUTEX_ERRORCHECK) {
return EBUSY;
}
} else {

View file

@ -18,6 +18,7 @@
*/
#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/futex.internal.h"
@ -31,14 +32,14 @@
*/
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
int c, me, owner;
switch (mutex->attr) {
switch (mutex->type) {
case PTHREAD_MUTEX_NORMAL:
// From Futexes Are Tricky Version 1.1 § Mutex, Take 3;
// Ulrich Drepper, Red Hat Incorporated, June 27, 2004.
if ((c = atomic_fetch_sub_explicit(&mutex->lock, 1,
memory_order_release)) != 1) {
atomic_store_explicit(&mutex->lock, 0, memory_order_release);
_futex_wake_private(&mutex->lock, 1);
_futex_wake(&mutex->lock, 1, mutex->pshared);
}
return 0;
case PTHREAD_MUTEX_ERRORCHECK:
@ -52,8 +53,9 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex) {
case PTHREAD_MUTEX_RECURSIVE:
if (--mutex->reent) return 0;
atomic_store_explicit(&mutex->lock, 0, memory_order_relaxed);
if (atomic_load_explicit(&mutex->waits, memory_order_relaxed) > 0) {
_pthread_mutex_wake(mutex);
if ((IsLinux() || IsOpenbsd()) &&
atomic_load_explicit(&mutex->waits, memory_order_relaxed) > 0) {
return _futex_wake(&mutex->lock, 1, mutex->pshared);
}
return 0;
default:

View file

@ -1,31 +0,0 @@
/*-*- 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"
int _pthread_mutex_wake(pthread_mutex_t *mutex) {
if ((IsLinux() || IsOpenbsd()) &&
atomic_load_explicit(&mutex->waits, memory_order_relaxed)) {
return _futex_wake_private(&mutex->lock, 1);
} else {
return 0;
}
}

View file

@ -17,11 +17,13 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pthread.h"
#include "libc/str/str.h"
/**
* Destroys mutex attr.
* @return 0 on success, or error number on failure
*/
int(pthread_mutexattr_destroy)(pthread_mutexattr_t *attr) {
return pthread_mutexattr_destroy(attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) {
memset(attr, -1, sizeof(*attr));
return 0;
}

View file

@ -26,7 +26,8 @@
* - `PTHREAD_PROCESS_PRIVATE`
* @return 0 on success, or error on failure
*/
int(pthread_mutexattr_getpshared)(const pthread_mutexattr_t *attr,
int *pshared) {
return pthread_mutexattr_getpshared(attr, pshared);
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr,
int *pshared) {
*pshared = attr->pshared;
return 0;
}

View file

@ -28,6 +28,7 @@
* - `PTHREAD_MUTEX_ERRORCHECK`
* @return 0 on success, or error on failure
*/
int(pthread_mutexattr_gettype)(const pthread_mutexattr_t *attr, int *type) {
return pthread_mutexattr_gettype(attr, type);
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) {
*type = attr->type;
return 0;
}

View file

@ -22,6 +22,10 @@
* Initializes mutex attr.
* @return 0 on success, or error number on failure
*/
int(pthread_mutexattr_init)(pthread_mutexattr_t *attr) {
return pthread_mutexattr_init(attr);
int pthread_mutexattr_init(pthread_mutexattr_t *attr) {
*attr = (pthread_mutexattr_t){
PTHREAD_MUTEX_DEFAULT,
PTHREAD_PROCESS_DEFAULT,
};
return 0;
}

View file

@ -32,7 +32,7 @@ int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) {
switch (pshared) {
case PTHREAD_PROCESS_SHARED:
case PTHREAD_PROCESS_PRIVATE:
*attr = pshared;
attr->pshared = pshared;
return 0;
default:
return EINVAL;

View file

@ -35,7 +35,7 @@ int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) {
case PTHREAD_MUTEX_NORMAL:
case PTHREAD_MUTEX_RECURSIVE:
case PTHREAD_MUTEX_ERRORCHECK:
*attr = type;
attr->type = type;
return 0;
default:
return EINVAL;

View file

@ -19,6 +19,7 @@
#include "libc/assert.h"
#include "libc/errno.h"
#include "libc/intrin/pthread.h"
#include "libc/str/str.h"
/**
* Destroys read-write lock.
@ -31,6 +32,6 @@ int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) {
assert(!"deadlock");
return EINVAL;
}
*rwlock = (pthread_rwlock_t)PTHREAD_RWLOCK_INITIALIZER;
memset(rwlock, -1, sizeof(*rwlock));
return 0;
}

View file

@ -24,7 +24,8 @@
* @param attr may be null
* @return 0 on success, or error number on failure
*/
int(pthread_rwlock_init)(pthread_rwlock_t *rwlock,
const pthread_rwlockattr_t *attr) {
return pthread_rwlock_init(rwlock, attr);
int pthread_rwlock_init(pthread_rwlock_t *rwlock,
const pthread_rwlockattr_t *attr) {
*rwlock = (pthread_rwlock_t){attr ? *attr : PTHREAD_PROCESS_DEFAULT};
return 0;
}

View file

@ -30,7 +30,7 @@ static int pthread_rwlock_rdlock_spin(pthread_rwlock_t *rwlock, int expect,
tries++;
} else if (IsLinux() || IsOpenbsd()) {
atomic_fetch_add(&rwlock->waits, 1);
_futex_wait_private(&rwlock->lock, expect, &(struct timespec){1});
_futex_wait(&rwlock->lock, expect, rwlock->pshared, &(struct timespec){1});
atomic_fetch_sub(&rwlock->waits, 1);
} else {
pthread_yield();

View file

@ -43,11 +43,7 @@ int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) {
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);
}
_futex_wake(&rwlock->lock, 1, rwlock->pshared);
}
return 0;
}

View file

@ -30,7 +30,7 @@ static int pthread_rwlock_wrlock_spin(pthread_rwlock_t *rwlock, int expect,
tries++;
} else if (IsLinux() || IsOpenbsd()) {
atomic_fetch_add(&rwlock->waits, 1);
_futex_wait_private(&rwlock->lock, expect, &(struct timespec){1});
_futex_wait(&rwlock->lock, expect, rwlock->pshared, &(struct timespec){1});
atomic_fetch_sub(&rwlock->waits, 1);
} else {
pthread_yield();

View file

@ -17,12 +17,14 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pthread.h"
#include "libc/str/str.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);
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) {
memset(attr, -1, sizeof(*attr));
return 0;
}

View file

@ -26,7 +26,8 @@
* - `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);
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr,
int *pshared) {
*pshared = *attr;
return 0;
}

View file

@ -23,6 +23,7 @@
*
* @return 0 on success, or error on failure
*/
int(pthread_rwlockattr_init)(pthread_rwlockattr_t *attr) {
return pthread_rwlockattr_init(attr);
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) {
*attr = PTHREAD_PROCESS_DEFAULT;
return 0;
}

View file

@ -38,7 +38,7 @@ void _wait0(const int *ctid) {
if (!(x = atomic_load_explicit(ctid, memory_order_relaxed))) {
break;
} else if (IsLinux() || IsOpenbsd()) {
_futex_wait_public(ctid, x, &(struct timespec){2});
_futex_wait(ctid, x, PTHREAD_PROCESS_SHARED, &(struct timespec){2});
} else {
pthread_yield();
}