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

23
libc/calls/fadvise64.S Normal file
View file

@ -0,0 +1,23 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
fadvise64:
jmp fadvise
.endfn fadvise64,globl

23
libc/calls/getdents64.S Normal file
View file

@ -0,0 +1,23 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
getdents64:
jmp getdents
.endfn getdents64,globl

22
libc/calls/open64.S Normal file
View file

@ -0,0 +1,22 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
open64: jmp open
.endfn open64,globl

23
libc/calls/pread64.S Normal file
View file

@ -0,0 +1,23 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
pread64:
jmp pread
.endfn pread64,globl

23
libc/calls/prlimit64.S Normal file
View file

@ -0,0 +1,23 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
prlimit64:
jmp prlimit
.endfn prlimit64,globl

23
libc/calls/pwrite64.S Normal file
View file

@ -0,0 +1,23 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
pwrite64:
jmp pwrite
.endfn pwrite64,globl

View file

@ -22,11 +22,13 @@
static pthread_mutex_t __sig_lock_obj;
void(__sig_lock)(void) {
__sig_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_lock(&__sig_lock_obj);
}
void(__sig_unlock)(void) {
__sig_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_unlock(&__sig_lock_obj);
}
__attribute__((__constructor__)) static void init(void) {
__sig_lock_obj.type = PTHREAD_MUTEX_RECURSIVE;
}

View file

@ -6,7 +6,7 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct utsname {
struct utsname { /* cosmo abi */
char sysname[SYS_NMLN]; /* name of os */
char nodename[SYS_NMLN]; /* name of network node */
char release[SYS_NMLN]; /* release level */

View file

@ -1,128 +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 2009 Ian Piumarta
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the 'Software'), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to
do so, provided that the above copyright notice(s) and this permission
notice appear in all copies of the Software. Inclusion of the above
copyright notice(s) and this permission notice in supporting documentation
would be appreciated but is not required.
THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
*/
#include "libc/assert.h"
#include "libc/fmt/fmt.h"
#include "libc/macros.internal.h"
#include "libc/math.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
asm(".ident\t\"\\n\\n\
ecvt, fcvt (MIT License)\\n\
Copyright 2009 Ian Piumarta\"");
/**
* @fileoverview Replacements for the functions ecvt() and fcvt()
*
* These functions were recently deprecated in POSIX. The interface and
* behaviour is identical to the functions that they replace and faster.
*
* For details on the use of these functions, see your ecvt(3) manual
* page. If you don't have one handy, there might still be one available
* here: http://opengroup.org/onlinepubs/007908799/xsh/ecvt.html
*
* @see https://www.piumarta.com/software/fcvt/
*/
static char *Fcvt(double value, int ndigit, int *decpt, int *sign, int fflag) {
static char buf[128];
double i;
uint64_t l, mant;
int exp2, exp10, ptr;
memcpy(&l, &value, 8);
exp2 = (0x7ff & (l >> 52)) - 1023;
mant = l & 0x000fffffffffffffULL;
if ((*sign = l >> 63)) value = -value;
if (exp2 == 0x400) {
*decpt = 0;
return mant ? "nan" : "inf";
}
exp10 = (value == 0) ? !fflag : (int)ceil(log10(value));
if (exp10 < -307) exp10 = -307; /* otherwise overflow in pow() */
value *= pow(10.0, -exp10);
if (value) {
while (value < 0.1) {
value *= 10;
--exp10;
}
while (value >= 1.0) {
value /= 10;
++exp10;
}
}
assert(value == 0 || (0.1 <= value && value < 1.0));
if (fflag) {
if (ndigit + exp10 < 0) {
*decpt = -ndigit;
return "";
}
ndigit += exp10;
}
*decpt = exp10;
if (ARRAYLEN(buf) < ndigit + 2) abort();
ptr = 1;
#if 0 /* slow and safe (and dreadfully boring) */
while (ptr <= ndigit) {
i;
value = modf(value * 10, &i);
buf[ptr++] = '0' + (int)i;
}
if (value >= 0.5) {
while (--ptr && ++buf[ptr] > '9') {
buf[ptr] = '0';
}
}
#else /* faster */
memcpy(&l, &value, 8);
exp2 = (0x7ff & (l >> 52)) - 1023;
assert(value == 0 || (-4 <= exp2 && exp2 <= -1));
mant = l & 0x000fffffffffffffULL;
if (exp2 == -1023) {
++exp2;
} else {
mant |= 0x0010000000000000ULL;
}
mant <<= (exp2 + 4); /* 56-bit denormalised signifier */
while (ptr <= ndigit) {
mant &= 0x00ffffffffffffffULL; /* mod 1.0 */
mant = (mant << 1) + (mant << 3);
buf[ptr++] = '0' + (mant >> 56);
}
if (mant & 0x0080000000000000ULL) /* 1/2 << 56 */
while (--ptr && ++buf[ptr] > '9') buf[ptr] = '0';
#endif
if (ptr) {
buf[ndigit + 1] = 0;
return buf + 1;
}
if (fflag) {
++ndigit;
++*decpt;
}
buf[0] = '1';
buf[ndigit] = 0;
return buf;
}
char *ecvt(double value, int ndigit, int *decpt, int *sign) {
return Fcvt(value, ndigit, decpt, sign, 0);
}
char *fcvt(double value, int ndigit, int *decpt, int *sign) {
return Fcvt(value, ndigit, decpt, sign, 1);
}

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

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

110
libc/stdio/ecvt.c Normal file
View file

@ -0,0 +1,110 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
$OpenBSD: ecvt.c,v 1.11 2019/01/25 00:19:25 millert Exp $
Copyright (c) 2002, 2006 Todd C. Miller <millert@openbsd.org>
Permission to use, copy, modify, and 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.
Sponsored in part by the Defense Advanced Research Projects
Agency (DARPA) and Air Force Research Laboratory, Air Force
Materiel Command, USAF, under agreement number F39502-99-1-0512.
SUCH DAMAGE.
*/
#include "libc/fmt/fmt.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "third_party/gdtoa/gdtoa.h"
asm(".ident\t\"\\n\\n\
OpenBSD ecvt/gcvt (MIT)\\n\
Copyright (c) 2002, 2006, 2010 Todd C. Miller <millert@openbsd.org>\"");
asm(".include \"libc/disclaimer.inc\"");
// clang-format off
static char *
__cvt(double value, int ndigit, int *decpt, int *sign, int fmode, int pad)
{
static char *s;
char *p, *rve, c;
size_t siz;
if (ndigit == 0) {
*sign = value < 0.0;
*decpt = 0;
return ("");
}
free(s);
s = NULL;
if (ndigit < 0)
siz = -ndigit + 1;
else
siz = ndigit + 1;
/* __dtoa() doesn't allocate space for 0 so we do it by hand */
if (value == 0.0) {
*decpt = 1 - fmode; /* 1 for 'e', 0 for 'f' */
*sign = 0;
if ((rve = s = malloc(siz)) == NULL)
return(NULL);
*rve++ = '0';
*rve = '\0';
} else {
p = dtoa(value, fmode + 2, ndigit, decpt, sign, &rve);
if (p == NULL)
return (NULL);
if (*decpt == 9999) {
/* Infinity or Nan, convert to inf or nan like printf */
*decpt = 0;
c = *p;
freedtoa(p);
return(c == 'I' ? "inf" : "nan");
}
/* Make a local copy and adjust rve to be in terms of s */
if (pad && fmode)
siz += *decpt;
if ((s = malloc(siz)) == NULL) {
freedtoa(p);
return(NULL);
}
(void) strlcpy(s, p, siz);
rve = s + (rve - p);
freedtoa(p);
}
/* Add trailing zeros */
if (pad) {
siz -= rve - s;
while (--siz)
*rve++ = '0';
*rve = '\0';
}
return(s);
}
char *
ecvt(double value, int ndigit, int *decpt, int *sign)
{
return(__cvt(value, ndigit, decpt, sign, 0, 1));
}
char *
fcvt(double value, int ndigit, int *decpt, int *sign)
{
return(__cvt(value, ndigit, decpt, sign, 1, 1));
}

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/intrin/pthread.h"
#include "libc/mem/mem.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
@ -37,6 +38,7 @@ FILE *fdopen(int fd, const char *mode) {
f->fd = fd;
f->bufmode = ischardev(fd) ? _IOLBF : _IOFBF;
f->iomode = fopenflags(mode);
f->lock.type = PTHREAD_MUTEX_RECURSIVE;
f->size = BUFSIZ;
if ((f->buf = malloc(f->size))) {
if ((f->iomode & O_ACCMODE) != O_RDONLY) {

View file

@ -16,14 +16,14 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/mem/arraylist.internal.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/pushpop.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/pthread.h"
#include "libc/intrin/pushpop.h"
#include "libc/intrin/spinlock.h"
#include "libc/macros.internal.h"
#include "libc/mem/arraylist.internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/fflush.internal.h"
@ -34,12 +34,10 @@
static pthread_mutex_t __fflush_lock_obj;
void(__fflush_lock)(void) {
__fflush_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_lock(&__fflush_lock_obj);
}
void(__fflush_unlock)(void) {
__fflush_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_unlock(&__fflush_lock_obj);
}

View file

@ -23,6 +23,5 @@
* Acquires reentrant lock on stdio object, blocking if needed.
*/
void(flockfile)(FILE *f) {
f->lock.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_lock(&f->lock);
}

View file

@ -54,6 +54,7 @@ FILE *fmemopen(void *buf, size_t size, const char *mode) {
f->end = size;
f->size = size;
f->iomode = fopenflags(mode);
f->lock.type = PTHREAD_MUTEX_RECURSIVE;
if (f->iomode & O_APPEND) {
if ((p = memchr(buf, '\0', size))) {
f->beg = p - (char *)buf;

View file

@ -22,6 +22,5 @@
* Releases lock on stdio object.
*/
void(funlockfile)(FILE *f) {
f->lock.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_unlock(&f->lock);
}

View file

@ -1,25 +1,130 @@
/*-*- 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
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
$OpenBSD: ecvt.c,v 1.11 2019/01/25 00:19:25 millert Exp $
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.
Copyright (c) 2002, 2006, 2010 Todd C. Miller <millert@openbsd.org>
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.
Permission to use, copy, modify, and 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.
Sponsored in part by the Defense Advanced Research Projects
Agency (DARPA) and Air Force Research Laboratory, Air Force
Materiel Command, USAF, under agreement number F39502-99-1-0512.
SUCH DAMAGE.
*/
#include "libc/fmt/fmt.h"
#include "libc/stdio/stdio.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/str/unicode.h"
#include "third_party/gdtoa/gdtoa.h"
char *gcvt(double x, int n, char *b) {
sprintf(b, "%.*g", n, x);
return b;
asm(".ident\t\"\\n\\n\
OpenBSD ecvt/gcvt (MIT)\\n\
Copyright (c) 2002, 2006, 2010 Todd C. Miller <millert@openbsd.org>\"");
asm(".include \"libc/disclaimer.inc\"");
// clang-format off
#define DEFPREC 6
char *
gcvt(double value, int ndigit, char *buf)
{
char *digits, *dst, *src;
int i, decpt, sign;
struct lconv *lconv;
lconv = localeconv();
if (ndigit <= 0) {
/* Match printf(3) behavior. */
ndigit = ndigit ? DEFPREC : 1;
}
digits = dtoa(value, 2, ndigit, &decpt, &sign, NULL);
if (digits == NULL)
return (NULL);
if (decpt == 9999) {
/*
* Infinity or NaN, convert to inf or nan with sign.
* We can't infer buffer size based on ndigit.
* We have to assume it is at least 5 chars.
*/
snprintf(buf, 5, "%s%s", sign ? "-" : "",
*digits == 'I' ? "inf" : "nan");
freedtoa(digits);
return (buf);
}
dst = buf;
if (sign)
*dst++ = '-';
/* Match printf(3) behavior for exponential vs. regular fomatting. */
if (decpt <= -4 || decpt > ndigit) {
/* exponential format (e.g. 1.2345e+13) */
if (--decpt < 0) {
sign = 1;
decpt = -decpt;
} else
sign = 0;
src = digits;
*dst++ = *src++;
if (*src != '\0') {
*dst++ = *lconv->decimal_point;
do {
*dst++ = *src++;
} while (*src != '\0');
}
*dst++ = 'e';
if (sign)
*dst++ = '-';
else
*dst++ = '+';
if (decpt < 10) {
*dst++ = '0';
*dst++ = '0' + decpt;
*dst = '\0';
} else {
/* XXX - optimize */
for (sign = decpt, i = 0; (sign /= 10) != 0; i++)
continue;
dst[i + 1] = '\0';
while (decpt != 0) {
dst[i--] = '0' + decpt % 10;
decpt /= 10;
}
}
} else {
/* standard format */
for (i = 0, src = digits; i < decpt; i++) {
if (*src != '\0')
*dst++ = *src++;
else
*dst++ = '0';
}
if (*src != '\0') {
if (src == digits)
*dst++ = '0'; /* zero before decimal point */
*dst++ = *lconv->decimal_point;
while (decpt < 0) {
*dst++ = '0';
decpt++;
}
for (i = decpt; digits[i] != '\0'; i++) {
*dst++ = digits[i];
}
}
*dst = '\0';
}
freedtoa(digits);
return (buf);
}

View file

@ -20,6 +20,7 @@
#include "libc/dce.h"
#include "libc/calls/calls.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/intrin/pthread.h"
#include "libc/macros.internal.h"
.init.start 400,_init_stderr
@ -33,5 +34,6 @@
ezlea __stderr_buf,cx
mov %rcx,0x18(%rax) # f.buf
movl $BUFSIZ,0x20(%rax) # f.size
movb $PTHREAD_MUTEX_RECURSIVE,0x38(%rax) # f.lock.attr
mov %rax,stderr(%rip)
.init.end 400,_init_stderr,globl,hidden

View file

@ -20,6 +20,7 @@
#include "libc/dce.h"
#include "libc/calls/calls.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/intrin/pthread.h"
#include "libc/macros.internal.h"
.init.start 400,_init_stdin
@ -29,5 +30,6 @@
ezlea __stdin_buf,cx
mov %rcx,0x18(%rax) # f.buf
movl $BUFSIZ,0x20(%rax) # f.size
movb $PTHREAD_MUTEX_RECURSIVE,0x38(%rax) # f.lock.attr
mov %rax,stdin(%rip)
.init.end 400,_init_stdin,globl,hidden

View file

@ -20,6 +20,7 @@
#include "libc/dce.h"
#include "libc/calls/calls.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/intrin/pthread.h"
#include "libc/macros.internal.h"
.init.start 400,_init_stdout
@ -31,5 +32,6 @@
ezlea __stdout_buf,cx
mov %rcx,0x18(%rax) # f.buf
movl $BUFSIZ,0x20(%rax) # f.size
movb $PTHREAD_MUTEX_RECURSIVE,0x38(%rax) # f.lock.attr
mov %rax,stdout(%rip)
.init.end 400,_init_stdout,globl,hidden

View file

@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
#define COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
#include "libc/intrin/pthread.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/spawn.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
@ -10,7 +11,7 @@ COSMOPOLITAN_C_START_
*/
enum PosixThreadStatus {
kPosixThreadStarted,
kPosixThreadJoinable,
kPosixThreadDetached,
kPosixThreadTerminated,
kPosixThreadZombie,
@ -19,16 +20,19 @@ enum PosixThreadStatus {
struct PosixThread {
struct spawn spawn;
void *(*start_routine)(void *);
void *arg;
void *rc;
int tid;
void *arg; // start_routine's parameter
void *rc; // start_routine's return value
_Atomic(enum PosixThreadStatus) status;
jmp_buf exiter;
size_t stacksize;
pthread_attr_t attr;
};
void pthread_zombies_add(struct PosixThread *);
void pthread_zombies_decimate(void);
void pthread_zombies_harvest(void);
void pthread_free(struct PosixThread *) hidden;
void pthread_wait(struct PosixThread *) hidden;
void pthread_zombies_add(struct PosixThread *) hidden;
void pthread_zombies_decimate(void) hidden;
void pthread_zombies_harvest(void) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -0,0 +1,38 @@
/*-*- 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/runtime/stack.h"
/**
* Returns configuration for thread stack.
*
* This is a getter for a configuration attribute. By default, zeros are
* returned. If pthread_attr_setstack() was called earlier, then this'll
* return those earlier supplied values.
*
* @param stackaddr will be set to stack address in bytes
* @return 0 on success, or errno on error
* @see pthread_attr_setstacksize()
*/
int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr,
size_t *stacksize) {
*stackaddr = attr->stackaddr;
*stacksize = attr->stacksize;
return 0;
}

View file

@ -0,0 +1,106 @@
/*-*- 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/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/pthread.h"
#include "libc/macros.internal.h"
#include "libc/runtime/stack.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#define MAP_ANON_OPENBSD 0x1000
#define MAP_STACK_OPENBSD 0x4000
/**
* Configures custom allocated stack for thread, e.g.
*
* pthread_t id;
* pthread_attr_t attr;
* pthread_attr_init(&attr);
* pthread_attr_setstack(&attr, gc(malloc(GetStackSize())),
* GetStackSize());
* pthread_create(&id, &attr, func, 0);
* pthread_attr_destroy(&attr);
* pthread_join(id, 0);
*
* 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_setstacksize(), this function permits just
* about any parameters and will change the values and allocation
* as needed to conform to the mandatory requirements of the host
* operating system.
*
* @param stackaddr is address of stack allocated by caller, and
* may be NULL in which case default behavior is restored
* @param stacksize is size of caller allocated stack
* @return 0 on success, or errno on error
* @raise EINVAL if parameters were unacceptable
* @see pthread_attr_setstacksize()
*/
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr,
size_t stacksize) {
if (!stackaddr) {
attr->stackaddr = 0;
attr->stacksize = 0;
return 0;
}
if (stacksize < PTHREAD_STACK_MIN ||
(IsAsan() && !__asan_is_valid(stackaddr, stacksize))) {
return EINVAL;
}
if (IsOpenbsd()) {
// OpenBSD: Only permits RSP to occupy memory that's been explicitly
// defined as stack memory. We need to squeeze the provided interval
// in order to successfully call mmap(), which will return EINVAL if
// these calculations should overflow.
size_t n;
int e, rc;
uintptr_t x, y;
n = stacksize;
x = (uintptr_t)stackaddr;
y = ROUNDUP(x, PAGESIZE);
n -= y - x;
n = ROUNDDOWN(n, PAGESIZE);
stackaddr = (void *)y;
stacksize = n;
e = errno;
if (__sys_mmap(stackaddr, stacksize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON_OPENBSD | MAP_STACK_OPENBSD, -1, 0,
0) == MAP_FAILED) {
rc = errno;
errno = e;
return rc;
}
}
attr->stackaddr = stackaddr;
attr->stacksize = stacksize;
return 0;
}

View file

@ -78,11 +78,7 @@ int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
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);
}
_futex_wait(&cond->seq, seq, cond->pshared, tsp);
} else {
sched_yield();
}

View file

@ -16,19 +16,37 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/pthread.h"
#include "libc/intrin/wait0.internal.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/gettls.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/clone.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/spawn.h"
#include "libc/thread/thread.h"
void pthread_wait(struct PosixThread *pt) {
_wait0(pt->spawn.ctid);
}
void pthread_free(struct PosixThread *pt) {
free(pt->spawn.tls);
if (pt->stacksize) {
munmap(&pt->spawn.stk, pt->stacksize);
}
free(pt);
}
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);
@ -66,27 +84,128 @@ static int PosixThread(void *arg, int tid) {
* bsdthread_create thr_new
*
*
* @param thread if non-null is used to output the thread id
* upon successful completion
* @param attr points to launch configuration, or may be null
* to use sensible defaults; it must be initialized using
* pthread_attr_init()
* @param start_routine is your thread's callback function
* @param arg is an arbitrary value passed to `start_routine`
* @return 0 on success, or errno on error
* @raise EAGAIN if resources to create thread weren't available
* @raise EINVAL if `attr` was supplied and had unnaceptable data
* @raise EPERM if scheduling policy was requested and user account
* isn't authorized to use it
*/
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg) {
int e, rc;
int rc, e = errno;
struct PosixThread *pt;
e = errno;
pthread_attr_t default_attr;
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;
// default attributes
if (!attr) {
pthread_attr_init(&default_attr);
attr = &default_attr;
}
errno = e;
return rc;
// create posix thread object
if (!(pt = calloc(1, sizeof(struct PosixThread)))) {
errno = e;
return EAGAIN;
}
pt->start_routine = start_routine;
pt->arg = arg;
// create thread local storage memory
if (!(pt->spawn.tls = _mktls(&pt->spawn.tib))) {
free(pt);
errno = e;
return EAGAIN;
}
// child thread id is also a condition variable
pt->spawn.ctid = (int *)(pt->spawn.tib + 0x38);
// create stack
if (attr && attr->stackaddr) {
// caller is responsible for creating stacks
pt->spawn.stk = attr->stackaddr;
} else {
// cosmo posix threads is managing the stack
pt->spawn.stk = mmap(0, attr->stacksize, PROT_READ | PROT_WRITE,
MAP_STACK | MAP_ANONYMOUS, -1, 0);
if (pt->spawn.stk != MAP_FAILED) {
pt->stacksize = attr->stacksize;
} else {
rc = errno;
pthread_free(pt);
errno = e;
if (rc == EINVAL || rc == EOVERFLOW) {
return EINVAL;
} else {
return EAGAIN;
}
}
// mmap(MAP_STACK) creates a 4096 guard by default
if (attr->guardsize != PAGESIZE) {
// user requested special guard size
if (attr->guardsize) {
rc = mprotect(pt->spawn.stk, attr->guardsize, PROT_NONE);
} else {
rc = mprotect(pt->spawn.stk, PAGESIZE, PROT_READ | PROT_WRITE);
}
if (rc) {
notpossible;
}
}
if (IsAsan()) {
if (attr->guardsize) {
__asan_poison(pt->spawn.stk, attr->guardsize, kAsanStackOverflow);
}
__asan_poison(
pt->spawn.stk + attr->stacksize - 16 /* openbsd:stackbound */, 16,
kAsanStackOverflow);
}
}
// save the attributes for descriptive purposes
pt->attr = *attr;
// set initial status
switch (attr->detachstate) {
case PTHREAD_CREATE_JOINABLE:
pt->status = kPosixThreadJoinable;
break;
case PTHREAD_CREATE_DETACHED:
pt->status = kPosixThreadDetached;
pthread_zombies_add(pt);
break;
default:
pthread_free(pt);
return EINVAL;
}
// launch PosixThread(pt) in new thread
if (clone(PosixThread, pt->spawn.stk,
attr->stacksize - 16 /* openbsd:stackbound */,
CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID |
CLONE_CHILD_CLEARTID,
pt, &pt->spawn.ptid, pt->spawn.tib, pt->spawn.ctid) == -1) {
rc = errno;
pthread_free(pt);
errno = e;
if (rc == EINVAL) {
return EINVAL;
} else {
return EAGAIN;
}
}
if (thread) {
*thread = pt;
}
return 0;
}

View file

@ -36,10 +36,10 @@ int pthread_detach(pthread_t thread) {
if (status == kPosixThreadDetached || status == kPosixThreadZombie) {
break;
} else if (status == kPosixThreadTerminated) {
_join(&pt->spawn);
free(pt);
pthread_wait(pt);
pthread_free(pt);
break;
} else if (status == kPosixThreadStarted &&
} else if (status == kPosixThreadJoinable &&
atomic_compare_exchange_weak_explicit(
&pt->status, &status, kPosixThreadDetached,
memory_order_acquire, memory_order_relaxed)) {

View file

@ -27,5 +27,5 @@
int pthread_equal(pthread_t t1, pthread_t t2) {
struct PosixThread *a = t1;
struct PosixThread *b = t2;
return a->tid == b->tid;
return a->spawn.ptid == b->spawn.ptid;
}

View file

@ -16,16 +16,13 @@
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/str/str.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.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;
}
int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr) {
struct PosixThread *pt = thread;
memcpy(attr, &pt->attr, sizeof(pt->attr));
return 0;
}

View file

@ -24,5 +24,5 @@
*/
int64_t pthread_getunique_np(pthread_t thread) {
struct PosixThread *pt = thread;
return pt->tid;
return pt->spawn.ptid;
}

View file

@ -36,10 +36,10 @@ int pthread_join(pthread_t thread, void **value_ptr) {
assert(!"badjoin");
return EDEADLK;
}
_join(&pt->spawn);
pthread_wait(pt);
if (value_ptr) {
*value_ptr = pt->rc;
}
free(pt);
pthread_free(pt);
return 0;
}

View file

@ -22,12 +22,13 @@
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/futex.internal.h"
#include "libc/intrin/pthread.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_public(addr, val, timeout);
return _futex_wait(addr, val, PTHREAD_PROCESS_SHARED, timeout);
} else {
return sched_yield();
}
@ -35,7 +36,7 @@ int cthread_memory_wait32(int* addr, int val, const struct timespec* timeout) {
int cthread_memory_wake32(int* addr, int n) {
if (IsLinux() || IsOpenbsd()) {
return _futex_wake_public(addr, n);
return _futex_wake(addr, n, PTHREAD_PROCESS_SHARED);
} else {
return 0;
}

View file

@ -44,9 +44,9 @@ void pthread_zombies_add(struct PosixThread *pt) {
}
}
void pthread_zombies_destroy(struct Zombie *z) {
_join(&z->pt->spawn);
free(z->pt);
static void pthread_zombies_collect(struct Zombie *z) {
pthread_wait(z->pt);
pthread_free(z->pt);
free(z);
}
@ -55,7 +55,7 @@ void pthread_zombies_decimate(void) {
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);
pthread_zombies_collect(z);
}
}
}
@ -64,7 +64,7 @@ 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);
pthread_zombies_collect(z);
}
}
}

View file

@ -50,7 +50,7 @@ TEST(qsort, test) {
free(M);
}
BENCH(qsort, equivalence_random) {
TEST(qsort, equivalence_random) {
size_t i;
size_t n = 1000;
long *a = gc(malloc(n * sizeof(long)));
@ -68,7 +68,7 @@ BENCH(qsort, equivalence_random) {
ASSERT_EQ(0, memcmp(b, c, n * sizeof(long)));
}
BENCH(qsort, equivalence_reverse) {
TEST(qsort, equivalence_reverse) {
size_t i;
size_t n = 1000;
long *a = gc(malloc(n * sizeof(long)));

View file

@ -16,24 +16,96 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/errno.h"
#include "libc/limits.h"
#include "libc/str/str.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
TEST(l64a, test) {
EXPECT_STREQ("", l64a(0));
EXPECT_STREQ("zzzzz1", l64a(-1));
EXPECT_STREQ("zzzzz/", l64a(0x7fffffff));
}
TEST(a64l, test) {
EXPECT_EQ(0, a64l(""));
EXPECT_EQ(-1, a64l("zzzzz1"));
EXPECT_EQ(0x7fffffff, a64l("zzzzz/"));
}
dontinline long openbsd_a64l(const char *s) {
long value, digit, shift;
int i;
if (s == NULL) {
errno = EINVAL;
return (-1L);
}
value = 0;
shift = 0;
for (i = 0; *s && i < 6; i++, s++) {
if (*s >= '.' && *s <= '/')
digit = *s - '.';
else if (*s >= '0' && *s <= '9')
digit = *s - '0' + 2;
else if (*s >= 'A' && *s <= 'Z')
digit = *s - 'A' + 12;
else if (*s >= 'a' && *s <= 'z')
digit = *s - 'a' + 38;
else {
errno = EINVAL;
return (-1L);
}
value |= digit << shift;
shift += 6;
}
return (value);
}
dontinline char *openbsd_l64a(long value) {
static char buf[8];
char *s = buf;
int digit;
int i;
if (value < 0) {
errno = EINVAL;
return (NULL);
}
for (i = 0; value != 0 && i < 6; i++) {
digit = value & 0x3f;
if (digit < 2)
*s = digit + '.';
else if (digit < 12)
*s = digit + '0' - 2;
else if (digit < 38)
*s = digit + 'A' - 12;
else
*s = digit + 'a' - 38;
value >>= 6;
s++;
}
*s = '\0';
return (buf);
}
TEST(openbsd, consistency) {
long i;
for (i = 0; i < 512; ++i) {
ASSERT_STREQ(openbsd_l64a(i), l64a(i));
ASSERT_EQ(openbsd_a64l(openbsd_l64a(i)), a64l(l64a(i)));
}
for (i = INT_MAX; i > INT_MAX - 100; --i) {
ASSERT_STREQ(openbsd_l64a(i), l64a(i));
ASSERT_EQ(openbsd_a64l(openbsd_l64a(i)), a64l(l64a(i)));
}
}
BENCH(a64l, bench) {
EZBENCH2("l64a", donothing, l64a(INT_MAX));
EZBENCH2(
"a64l", donothing,
a64l("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"));
EZBENCH2("openbsd_l64a", donothing, openbsd_l64a(INT_MAX));
EZBENCH2(
"openbsd_a64l", donothing,
openbsd_a64l(
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"));
}

View file

@ -16,24 +16,85 @@
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"
#include "libc/mem/mem.h"
#include "libc/runtime/stack.h"
#include "libc/testlib/testlib.h"
#include "libc/thread/thread.h"
pthread_t thread;
static void *ReturnArg(void *arg) {
return arg;
static void *Increment(void *arg) {
ASSERT_EQ(gettid(), pthread_getthreadid_np());
return (void *)((uintptr_t)arg + 1);
}
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);
void *rc;
pthread_t id;
ASSERT_EQ(0, pthread_create(&id, 0, Increment, (void *)1));
ASSERT_EQ(0, pthread_join(id, &rc));
ASSERT_EQ((void *)2, rc);
}
static void *IncExit(void *arg) {
pthread_exit((void *)((uintptr_t)arg + 1));
}
TEST(pthread_create, testCreateExitJoin) {
void *rc;
pthread_t id;
ASSERT_EQ(0, pthread_create(&id, 0, IncExit, (void *)2));
ASSERT_EQ(0, pthread_join(id, &rc));
ASSERT_EQ((void *)3, rc);
}
TEST(pthread_detach, testCreateReturn) {
ASSERT_EQ(0, pthread_create(&thread, 0, ReturnArg, ReturnArg));
ASSERT_EQ(0, pthread_detach(thread));
pthread_t id;
ASSERT_EQ(0, pthread_create(&id, 0, Increment, 0));
ASSERT_EQ(0, pthread_detach(id));
}
TEST(pthread_detach, testDetachUponCreation) {
pthread_attr_t attr;
ASSERT_EQ(0, pthread_attr_init(&attr));
ASSERT_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
ASSERT_EQ(0, pthread_create(0, &attr, Increment, 0));
ASSERT_EQ(0, pthread_attr_destroy(&attr));
}
static void *CheckStack(void *arg) {
char buf[1024 * 1024];
CheckLargeStackAllocation(buf, 1024 * 1024);
return 0;
}
TEST(pthread_detach, testBigStack) {
pthread_t id;
pthread_attr_t attr;
ASSERT_EQ(0, pthread_attr_init(&attr));
ASSERT_EQ(0, pthread_attr_setstacksize(&attr, 2 * 1024 * 1024));
ASSERT_EQ(0, pthread_create(&id, &attr, CheckStack, 0));
ASSERT_EQ(0, pthread_attr_destroy(&attr));
ASSERT_EQ(0, pthread_join(id, 0));
}
TEST(pthread_detach, testCustomStack_withReallySmallSize) {
char *stk;
size_t siz;
pthread_t id;
pthread_attr_t attr;
siz = PTHREAD_STACK_MIN;
stk = malloc(siz);
ASSERT_EQ(0, pthread_attr_init(&attr));
ASSERT_EQ(0, pthread_attr_setstack(&attr, stk, siz));
ASSERT_EQ(0, pthread_create(&id, &attr, Increment, 0));
ASSERT_EQ(0, pthread_attr_destroy(&attr));
ASSERT_EQ(0, pthread_join(id, 0));
// we still own the stack memory
ASSERT_EQ(0, pthread_attr_init(&attr));
ASSERT_EQ(0, pthread_attr_setstack(&attr, stk, siz));
ASSERT_EQ(0, pthread_create(&id, &attr, Increment, 0));
ASSERT_EQ(0, pthread_attr_destroy(&attr));
ASSERT_EQ(0, pthread_join(id, 0));
free(stk);
}

View file

@ -52,6 +52,10 @@ o/$(MODE)/test/libc/thread/%.com.dbg: \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/test/libc/thread/pthread_create_test.o: \
private OVERRIDE_CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED
.PHONY: o/$(MODE)/test/libc/thread
o/$(MODE)/test/libc/thread: \
$(TEST_LIBC_THREAD_BINS) \