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; static pthread_mutex_t __sig_lock_obj;
void(__sig_lock)(void) { void(__sig_lock)(void) {
__sig_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_lock(&__sig_lock_obj); pthread_mutex_lock(&__sig_lock_obj);
} }
void(__sig_unlock)(void) { void(__sig_unlock)(void) {
__sig_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_unlock(&__sig_lock_obj); 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) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
struct utsname { struct utsname { /* cosmo abi */
char sysname[SYS_NMLN]; /* name of os */ char sysname[SYS_NMLN]; /* name of os */
char nodename[SYS_NMLN]; /* name of network node */ char nodename[SYS_NMLN]; /* name of network node */
char release[SYS_NMLN]; /* release level */ 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) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
int _futex_wait_public(void *, int, struct timespec *) hidden; int _futex_wait(void *, int, char, struct timespec *) hidden;
int _futex_wait_private(void *, int, struct timespec *) hidden; int _futex_wake(void *, int, char) hidden;
int _futex_wake_public(void *, int) hidden;
int _futex_wake_private(void *, int) hidden;
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -28,13 +28,14 @@
int _futex(void *, int, int, struct timespec *) hidden; int _futex(void *, int, int, struct timespec *) hidden;
static dontinline int _futex_wait_impl(void *addr, int expect, int _futex_wait(void *addr, int expect, char pshared,
struct timespec *timeout, int private) { struct timespec *timeout) {
int op, ax; int op, ax, pf;
if (IsLinux() || IsOpenbsd()) { 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); ax = _futex(addr, op, expect, timeout);
if (SupportsLinux() && private && ax == -ENOSYS) { if (SupportsLinux() && pf && ax == -ENOSYS) {
// RHEL5 doesn't support FUTEX_PRIVATE_FLAG // RHEL5 doesn't support FUTEX_PRIVATE_FLAG
op = FUTEX_WAIT; op = FUTEX_WAIT;
ax = _futex(addr, op, expect, timeout); ax = _futex(addr, op, expect, timeout);
@ -47,11 +48,3 @@ static dontinline int _futex_wait_impl(void *addr, int expect,
return pthread_yield(); 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/errno.h"
#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/futex.internal.h" #include "libc/intrin/futex.internal.h"
#include "libc/intrin/pthread.h"
#include "libc/sysv/consts/futex.h" #include "libc/sysv/consts/futex.h"
int _futex(void *, int, int) hidden; int _futex(void *, int, int) hidden;
static dontinline int _futex_wake_impl(void *addr, int count, int private) { int _futex_wake(void *addr, int count, char pshared) {
int op, ax; int op, ax, pf;
if (IsLinux() || IsOpenbsd()) { 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); ax = _futex(addr, op, count);
if (SupportsLinux() && private && ax == -ENOSYS) { if (SupportsLinux() && pf && ax == -ENOSYS) {
// RHEL5 doesn't support FUTEX_PRIVATE_FLAG // RHEL5 doesn't support FUTEX_PRIVATE_FLAG
op = FUTEX_WAKE; op = FUTEX_WAKE;
ax = _futex(addr, op, count); ax = _futex(addr, op, count);
@ -42,11 +44,3 @@ static dontinline int _futex_wake_impl(void *addr, int count, int private) {
return 0; 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; static pthread_mutex_t __fds_lock_obj;
void(__fds_lock)(void) { void(__fds_lock)(void) {
__fds_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_lock(&__fds_lock_obj); pthread_mutex_lock(&__fds_lock_obj);
} }
void(__fds_unlock)(void) { void(__fds_unlock)(void) {
__fds_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_unlock(&__fds_lock_obj); 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) { textstartup void InitializeFileDescriptors(void) {
struct Fds *fds; struct Fds *fds;
__fds_lock_obj.type = PTHREAD_MUTEX_RECURSIVE;
fds = VEIL("r", &g_fds); fds = VEIL("r", &g_fds);
pushmov(&fds->n, ARRAYLEN(fds->__init_p)); pushmov(&fds->n, ARRAYLEN(fds->__init_p));
fds->f = 3; fds->f = 3;

View file

@ -4,7 +4,7 @@
#define PTHREAD_ONCE_INIT 0 #define PTHREAD_ONCE_INIT 0
#define PTHREAD_KEYS_MAX 64 #define PTHREAD_KEYS_MAX 64
#define PTHREAD_STACK_MIN 2048 #define PTHREAD_STACK_MIN FRAMESIZE
#define PTHREAD_DESTRUCTOR_ITERATIONS 4 #define PTHREAD_DESTRUCTOR_ITERATIONS 4
#define PTHREAD_BARRIER_SERIAL_THREAD 31337 #define PTHREAD_BARRIER_SERIAL_THREAD 31337
@ -20,60 +20,70 @@
#define PTHREAD_PROCESS_PRIVATE 0 #define PTHREAD_PROCESS_PRIVATE 0
#define PTHREAD_PROCESS_SHARED 1 #define PTHREAD_PROCESS_SHARED 1
#define PTHREAD_CREATE_JOINABLE 0
#define PTHREAD_CREATE_DETACHED 1
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
/* clang-format off */ /* clang-format off */
#define PTHREAD_MUTEX_INITIALIZER {PTHREAD_MUTEX_DEFAULT}
#define PTHREAD_COND_INITIALIZER {PTHREAD_PROCESS_DEFAULT} #define PTHREAD_COND_INITIALIZER {PTHREAD_PROCESS_DEFAULT}
#define PTHREAD_BARRIER_INITIALIZER {PTHREAD_PROCESS_DEFAULT} #define PTHREAD_BARRIER_INITIALIZER {PTHREAD_PROCESS_DEFAULT}
#define PTHREAD_RWLOCK_INITIALIZER {PTHREAD_PROCESS_DEFAULT} #define PTHREAD_RWLOCK_INITIALIZER {PTHREAD_PROCESS_DEFAULT}
#define PTHREAD_MUTEX_INITIALIZER {PTHREAD_MUTEX_DEFAULT, \
PTHREAD_PROCESS_DEFAULT}
/* clang-format on */ /* clang-format on */
typedef void *pthread_t; typedef void *pthread_t;
typedef int pthread_id_np_t; typedef int pthread_id_np_t;
typedef int pthread_condattr_t; typedef char pthread_condattr_t;
typedef int pthread_mutexattr_t; typedef char pthread_rwlockattr_t;
typedef int pthread_rwlockattr_t; typedef char pthread_barrierattr_t;
typedef int pthread_barrierattr_t;
typedef unsigned pthread_key_t; typedef unsigned pthread_key_t;
typedef _Atomic(char) pthread_once_t; typedef _Atomic(char) pthread_once_t;
typedef _Atomic(char) pthread_spinlock_t; typedef _Atomic(char) pthread_spinlock_t;
typedef void (*pthread_key_dtor)(void *); typedef void (*pthread_key_dtor)(void *);
typedef struct { typedef struct pthread_mutex_s {
int attr; char type;
char pshared;
int reent; int reent;
_Atomic(int) lock; _Atomic(int) lock;
_Atomic(int) waits; _Atomic(int) waits;
} pthread_mutex_t; } pthread_mutex_t;
typedef struct { typedef struct pthread_mutexattr_s {
int attr; char type;
char pshared;
} pthread_mutexattr_t;
typedef struct pthread_cond_s {
char pshared;
_Atomic(int) waits; _Atomic(int) waits;
_Atomic(unsigned) seq; _Atomic(unsigned) seq;
} pthread_cond_t; } pthread_cond_t;
typedef struct { typedef struct pthread_barrier_s {
int attr; char pshared;
int count; int count;
_Atomic(int) waits; _Atomic(int) waits;
_Atomic(int) popped; _Atomic(int) popped;
} pthread_barrier_t; } pthread_barrier_t;
typedef struct { typedef struct pthread_rwlock_s {
int attr; char pshared;
_Atomic(int) lock; _Atomic(int) lock;
_Atomic(int) waits; _Atomic(int) waits;
} pthread_rwlock_t; } pthread_rwlock_t;
typedef struct { typedef struct pthread_attr_s {
char detachstate;
size_t stacksize;
size_t guardsize;
void *stackaddr;
int scope; int scope;
int schedpolicy; int schedpolicy;
int detachstate;
int inheritsched; int inheritsched;
size_t guardsize;
size_t stacksize;
} pthread_attr_t; } pthread_attr_t;
int pthread_yield(void); int pthread_yield(void);
@ -81,6 +91,7 @@ void pthread_exit(void *) wontreturn;
pthread_t pthread_self(void) pureconst; pthread_t pthread_self(void) pureconst;
pthread_id_np_t pthread_getthreadid_np(void); pthread_id_np_t pthread_getthreadid_np(void);
int64_t pthread_getunique_np(pthread_t); 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_init(pthread_attr_t *);
int pthread_attr_destroy(pthread_attr_t *); int pthread_attr_destroy(pthread_attr_t *);
int pthread_attr_getdetachstate(const pthread_attr_t *, int *); 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_init(pSpin, multiprocess) (*(pSpin) = 0)
#define pthread_spin_destroy(pSpin) (*(pSpin) = 0) #define pthread_spin_destroy(pSpin) (*(pSpin) = 0)
#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 #if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407
extern const errno_t EBUSY; extern const errno_t EBUSY;
#define pthread_spin_unlock(pSpin) \ #define pthread_spin_unlock(pSpin) \
@ -164,18 +174,18 @@ extern const errno_t EBUSY;
#define pthread_spin_trylock(pSpin) \ #define pthread_spin_trylock(pSpin) \
(__atomic_test_and_set(pSpin, __ATOMIC_SEQ_CST) ? EBUSY : 0) (__atomic_test_and_set(pSpin, __ATOMIC_SEQ_CST) ? EBUSY : 0)
#ifdef TINY #ifdef TINY
#define pthread_spin_lock(pSpin) __pthread_spin_lock_tiny(pSpin) #define pthread_spin_lock(pSpin) _pthread_spin_lock_tiny(pSpin)
#else #else
#define pthread_spin_lock(pSpin) __pthread_spin_lock_cooperative(pSpin) #define pthread_spin_lock(pSpin) _pthread_spin_lock_cooperative(pSpin)
#endif #endif
#define __pthread_spin_lock_tiny(pSpin) \ #define _pthread_spin_lock_tiny(pSpin) \
({ \ ({ \
while (__atomic_test_and_set(pSpin, __ATOMIC_SEQ_CST)) { \ while (__atomic_test_and_set(pSpin, __ATOMIC_SEQ_CST)) { \
__builtin_ia32_pause(); \ __builtin_ia32_pause(); \
} \ } \
0; \ 0; \
}) })
#define __pthread_spin_lock_cooperative(pSpin) \ #define _pthread_spin_lock_cooperative(pSpin) \
({ \ ({ \
char __x; \ char __x; \
volatile int __i; \ volatile int __i; \
@ -197,59 +207,6 @@ extern const errno_t EBUSY;
}) })
#endif /* GCC 4.7+ */ #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_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_ */ #endif /* COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_ */

View file

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

View file

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

View file

@ -18,7 +18,15 @@
*/ */
#include "libc/intrin/pthread.h" #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; return 0;
} }

View file

@ -18,7 +18,13 @@
*/ */
#include "libc/intrin/pthread.h" #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; return 0;
} }

View file

@ -17,8 +17,22 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/intrin/pthread.h" #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) { 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; return 0;
} }

View file

@ -17,12 +17,18 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/intrin/pthread.h" #include "libc/intrin/pthread.h"
#include "libc/str/str.h" #include "libc/runtime/stack.h"
/** /**
* Initializes pthread attributes. * Initializes pthread attributes.
*
* @return 0 on success, or errno on error
*/ */
int pthread_attr_init(pthread_attr_t *attr) { 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; return 0;
} }

View file

@ -16,9 +16,31 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/errno.h"
#include "libc/intrin/pthread.h" #include "libc/intrin/pthread.h"
int pthread_attr_setdetachstate(pthread_attr_t *a, int x) { /**
a->detachstate = x; * Sets thread detachable attribute, e.g.
return 0; *
* 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 TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/errno.h"
#include "libc/intrin/pthread.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; return 0;
} }

View file

@ -16,9 +16,38 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/errno.h"
#include "libc/intrin/pthread.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) { 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; a->stacksize = x;
return 0; return 0;
} }

View file

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

View file

@ -42,11 +42,7 @@ int pthread_barrier_wait(pthread_barrier_t *barrier) {
atomic_store(&barrier->popped, 1); atomic_store(&barrier->popped, 1);
do { do {
if (IsLinux() || IsOpenbsd()) { if (IsLinux() || IsOpenbsd()) {
if (barrier->attr == PTHREAD_PROCESS_SHARED) { _futex_wake(&barrier->popped, INT_MAX, barrier->pshared);
_futex_wake_public(&barrier->popped, INT_MAX);
} else {
_futex_wake_private(&barrier->popped, INT_MAX);
}
} else { } else {
pthread_yield(); pthread_yield();
} }
@ -59,11 +55,7 @@ int pthread_barrier_wait(pthread_barrier_t *barrier) {
} }
do { do {
if (IsLinux() || IsOpenbsd()) { if (IsLinux() || IsOpenbsd()) {
if (barrier->attr == PTHREAD_PROCESS_SHARED) { _futex_wait(&barrier->popped, 0, barrier->pshared, 0);
_futex_wait_public(&barrier->popped, 0, 0);
} else {
_futex_wait_private(&barrier->popped, 0, 0);
}
} else { } else {
pthread_yield(); pthread_yield();
} }

View file

@ -17,12 +17,14 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/intrin/pthread.h" #include "libc/intrin/pthread.h"
#include "libc/str/str.h"
/** /**
* Destroys barrier attributes. * Destroys barrier attributes.
* *
* @return 0 on success, or error on failure * @return 0 on success, or error on failure
*/ */
int(pthread_barrierattr_destroy)(pthread_barrierattr_t *attr) { int pthread_barrierattr_destroy(pthread_barrierattr_t *attr) {
return pthread_barrierattr_destroy(attr); memset(attr, -1, sizeof(*attr));
return 0;
} }

View file

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

View file

@ -23,6 +23,7 @@
* *
* @return 0 on success, or error on failure * @return 0 on success, or error on failure
*/ */
int(pthread_barrierattr_init)(pthread_barrierattr_t *attr) { int pthread_barrierattr_init(pthread_barrierattr_t *attr) {
return pthread_barrierattr_init(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)) { if (atomic_load_explicit(&cond->waits, memory_order_relaxed)) {
atomic_fetch_add(&cond->seq, 1); atomic_fetch_add(&cond->seq, 1);
if (IsLinux() || IsOpenbsd()) { if (IsLinux() || IsOpenbsd()) {
if (cond->attr == PTHREAD_PROCESS_SHARED) { _futex_wake(&cond->seq, n, cond->pshared);
_futex_wake_public(&cond->seq, n);
} else {
_futex_wake_private(&cond->seq, n);
}
} }
} }
return 0; return 0;

View file

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

View file

@ -24,6 +24,7 @@
* @param attr may be null * @param attr may be null
* @return 0 on success, or error number on failure * @return 0 on success, or error number on failure
*/ */
int(pthread_cond_init)(pthread_cond_t *cond, const pthread_condattr_t *attr) { int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) {
return pthread_cond_init(cond, attr); *cond = (pthread_cond_t){attr ? *attr : PTHREAD_PROCESS_DEFAULT};
return 0;
} }

View file

@ -17,12 +17,14 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/intrin/pthread.h" #include "libc/intrin/pthread.h"
#include "libc/str/str.h"
/** /**
* Destroys condition attributes. * Destroys condition attributes.
* *
* @return 0 on success, or error on failure * @return 0 on success, or error on failure
*/ */
int(pthread_condattr_destroy)(pthread_condattr_t *attr) { int pthread_condattr_destroy(pthread_condattr_t *attr) {
return pthread_condattr_destroy(attr); memset(attr, -1, sizeof(*attr));
return 0;
} }

View file

@ -26,6 +26,7 @@
* - `PTHREAD_PROCESS_PRIVATE` * - `PTHREAD_PROCESS_PRIVATE`
* @return 0 on success, or error on failure * @return 0 on success, or error on failure
*/ */
int(pthread_condattr_getpshared)(const pthread_condattr_t *attr, int *pshared) { int pthread_condattr_getpshared(const pthread_condattr_t *attr, int *pshared) {
return pthread_condattr_getpshared(attr, pshared); *pshared = *attr;
return 0;
} }

View file

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

View file

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

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/intrin/pthread.h" #include "libc/intrin/pthread.h"
#include "libc/str/str.h"
/** /**
* Initializes mutex. * Initializes mutex.
@ -25,7 +24,11 @@
* @param attr may be null * @param attr may be null
* @return 0 on success, or error number on failure * @return 0 on success, or error number on failure
*/ */
int(pthread_mutex_init)(pthread_mutex_t *mutex, int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr) { const pthread_mutexattr_t *attr) {
return pthread_mutex_init(mutex, 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++; tries++;
} else if (IsLinux() || IsOpenbsd()) { } else if (IsLinux() || IsOpenbsd()) {
atomic_fetch_add(&mutex->waits, 1); 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); atomic_fetch_sub(&mutex->waits, 1);
} else { } else {
pthread_yield(); pthread_yield();
@ -87,24 +87,24 @@ static int pthread_mutex_lock_spin(pthread_mutex_t *mutex, int expect,
* *
* Microbenchmarks for single-threaded lock + unlock: * Microbenchmarks for single-threaded lock + unlock:
* *
* pthread_spinlock_t : 12c ( 4ns) * pthread_spinlock_t : 12c ( 4ns)
* PTHREAD_MUTEX_NORMAL : 37c ( 12ns) * PTHREAD_MUTEX_NORMAL : 37c ( 12ns)
* PTHREAD_MUTEX_RECURSIVE : 22c ( 7ns) * PTHREAD_MUTEX_RECURSIVE : 22c ( 7ns)
* PTHREAD_MUTEX_ERRORCHECK : 27c ( 9ns) * PTHREAD_MUTEX_ERRORCHECK : 27c ( 9ns)
* *
* Microbenchmarks for multi-threaded lock + unlock: * Microbenchmarks for multi-threaded lock + unlock:
* *
* pthread_spinlock_t : 6,162c (1,990ns) * pthread_spinlock_t : 2,396c (774ns)
* PTHREAD_MUTEX_NORMAL : 780c ( 252ns) * PTHREAD_MUTEX_NORMAL : 535c (173ns)
* PTHREAD_MUTEX_RECURSIVE : 1,047c ( 338ns) * PTHREAD_MUTEX_RECURSIVE : 1,045c (338ns)
* PTHREAD_MUTEX_ERRORCHECK : 1,044c ( 337ns) * PTHREAD_MUTEX_ERRORCHECK : 917c (296ns)
* *
* @return 0 on success, or error number on failure * @return 0 on success, or error number on failure
* @see pthread_spin_lock * @see pthread_spin_lock
*/ */
int pthread_mutex_lock(pthread_mutex_t *mutex) { int pthread_mutex_lock(pthread_mutex_t *mutex) {
int c, me, owner, tries; int c, me, owner, tries;
switch (mutex->attr) { switch (mutex->type) {
case PTHREAD_MUTEX_NORMAL: case PTHREAD_MUTEX_NORMAL:
// From Futexes Are Tricky Version 1.1 § Mutex, Take 3; // From Futexes Are Tricky Version 1.1 § Mutex, Take 3;
// Ulrich Drepper, Red Hat Incorporated, June 27, 2004. // 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); c = atomic_exchange_explicit(&mutex->lock, 2, memory_order_acquire);
} }
while (c) { 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); 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)) { memory_order_relaxed)) {
break; break;
} else if (owner == me) { } else if (owner == me) {
if (mutex->attr != PTHREAD_MUTEX_ERRORCHECK) { if (mutex->type != PTHREAD_MUTEX_ERRORCHECK) {
break; break;
} else { } else {
assert(!"deadlock"); assert(!"deadlock");

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -22,6 +22,10 @@
* Initializes mutex attr. * Initializes mutex attr.
* @return 0 on success, or error number on failure * @return 0 on success, or error number on failure
*/ */
int(pthread_mutexattr_init)(pthread_mutexattr_t *attr) { int pthread_mutexattr_init(pthread_mutexattr_t *attr) {
return pthread_mutexattr_init(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) { switch (pshared) {
case PTHREAD_PROCESS_SHARED: case PTHREAD_PROCESS_SHARED:
case PTHREAD_PROCESS_PRIVATE: case PTHREAD_PROCESS_PRIVATE:
*attr = pshared; attr->pshared = pshared;
return 0; return 0;
default: default:
return EINVAL; 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_NORMAL:
case PTHREAD_MUTEX_RECURSIVE: case PTHREAD_MUTEX_RECURSIVE:
case PTHREAD_MUTEX_ERRORCHECK: case PTHREAD_MUTEX_ERRORCHECK:
*attr = type; attr->type = type;
return 0; return 0;
default: default:
return EINVAL; return EINVAL;

View file

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

View file

@ -24,7 +24,8 @@
* @param attr may be null * @param attr may be null
* @return 0 on success, or error number on failure * @return 0 on success, or error number on failure
*/ */
int(pthread_rwlock_init)(pthread_rwlock_t *rwlock, int pthread_rwlock_init(pthread_rwlock_t *rwlock,
const pthread_rwlockattr_t *attr) { const pthread_rwlockattr_t *attr) {
return pthread_rwlock_init(rwlock, 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++; tries++;
} else if (IsLinux() || IsOpenbsd()) { } else if (IsLinux() || IsOpenbsd()) {
atomic_fetch_add(&rwlock->waits, 1); 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); atomic_fetch_sub(&rwlock->waits, 1);
} else { } else {
pthread_yield(); pthread_yield();

View file

@ -43,11 +43,7 @@ int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) {
memory_order_acquire, memory_order_acquire,
memory_order_relaxed)) { memory_order_relaxed)) {
if (waits && (IsLinux() || IsOpenbsd())) { if (waits && (IsLinux() || IsOpenbsd())) {
if (rwlock->attr == PTHREAD_PROCESS_SHARED) { _futex_wake(&rwlock->lock, 1, rwlock->pshared);
_futex_wake_public(&rwlock->lock, 1);
} else {
_futex_wake_private(&rwlock->lock, 1);
}
} }
return 0; return 0;
} }

View file

@ -30,7 +30,7 @@ static int pthread_rwlock_wrlock_spin(pthread_rwlock_t *rwlock, int expect,
tries++; tries++;
} else if (IsLinux() || IsOpenbsd()) { } else if (IsLinux() || IsOpenbsd()) {
atomic_fetch_add(&rwlock->waits, 1); 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); atomic_fetch_sub(&rwlock->waits, 1);
} else { } else {
pthread_yield(); pthread_yield();

View file

@ -17,12 +17,14 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/intrin/pthread.h" #include "libc/intrin/pthread.h"
#include "libc/str/str.h"
/** /**
* Destroys read-write lock attributes. * Destroys read-write lock attributes.
* *
* @return 0 on success, or error on failure * @return 0 on success, or error on failure
*/ */
int(pthread_rwlockattr_destroy)(pthread_rwlockattr_t *attr) { int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) {
return pthread_rwlockattr_destroy(attr); memset(attr, -1, sizeof(*attr));
return 0;
} }

View file

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

View file

@ -23,6 +23,7 @@
* *
* @return 0 on success, or error on failure * @return 0 on success, or error on failure
*/ */
int(pthread_rwlockattr_init)(pthread_rwlockattr_t *attr) { int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) {
return pthread_rwlockattr_init(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))) { if (!(x = atomic_load_explicit(ctid, memory_order_relaxed))) {
break; break;
} else if (IsLinux() || IsOpenbsd()) { } else if (IsLinux() || IsOpenbsd()) {
_futex_wait_public(ctid, x, &(struct timespec){2}); _futex_wait(ctid, x, PTHREAD_PROCESS_SHARED, &(struct timespec){2});
} else { } else {
pthread_yield(); 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. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/intrin/pthread.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/stdio/internal.h" #include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
@ -37,6 +38,7 @@ FILE *fdopen(int fd, const char *mode) {
f->fd = fd; f->fd = fd;
f->bufmode = ischardev(fd) ? _IOLBF : _IOFBF; f->bufmode = ischardev(fd) ? _IOLBF : _IOFBF;
f->iomode = fopenflags(mode); f->iomode = fopenflags(mode);
f->lock.type = PTHREAD_MUTEX_RECURSIVE;
f->size = BUFSIZ; f->size = BUFSIZ;
if ((f->buf = malloc(f->size))) { if ((f->buf = malloc(f->size))) {
if ((f->iomode & O_ACCMODE) != O_RDONLY) { 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 TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. 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/calls/calls.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/pthread.h" #include "libc/intrin/pthread.h"
#include "libc/intrin/pushpop.h"
#include "libc/intrin/spinlock.h" #include "libc/intrin/spinlock.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/mem/arraylist.internal.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/stdio/fflush.internal.h" #include "libc/stdio/fflush.internal.h"
@ -34,12 +34,10 @@
static pthread_mutex_t __fflush_lock_obj; static pthread_mutex_t __fflush_lock_obj;
void(__fflush_lock)(void) { void(__fflush_lock)(void) {
__fflush_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_lock(&__fflush_lock_obj); pthread_mutex_lock(&__fflush_lock_obj);
} }
void(__fflush_unlock)(void) { void(__fflush_unlock)(void) {
__fflush_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_unlock(&__fflush_lock_obj); pthread_mutex_unlock(&__fflush_lock_obj);
} }

View file

@ -23,6 +23,5 @@
* Acquires reentrant lock on stdio object, blocking if needed. * Acquires reentrant lock on stdio object, blocking if needed.
*/ */
void(flockfile)(FILE *f) { void(flockfile)(FILE *f) {
f->lock.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_lock(&f->lock); 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->end = size;
f->size = size; f->size = size;
f->iomode = fopenflags(mode); f->iomode = fopenflags(mode);
f->lock.type = PTHREAD_MUTEX_RECURSIVE;
if (f->iomode & O_APPEND) { if (f->iomode & O_APPEND) {
if ((p = memchr(buf, '\0', size))) { if ((p = memchr(buf, '\0', size))) {
f->beg = p - (char *)buf; f->beg = p - (char *)buf;

View file

@ -22,6 +22,5 @@
* Releases lock on stdio object. * Releases lock on stdio object.
*/ */
void(funlockfile)(FILE *f) { void(funlockfile)(FILE *f) {
f->lock.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_unlock(&f->lock); 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 -*-│ /*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi 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 Copyright (c) 2002, 2006, 2010 Todd C. Miller <millert@openbsd.org>
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 Permission to use, copy, modify, and distribute this software for any
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED purpose with or without fee is hereby granted, provided that the above
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE copyright notice and this permission notice appear in all copies.
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
PERFORMANCE OF THIS SOFTWARE. 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/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) { asm(".ident\t\"\\n\\n\
sprintf(b, "%.*g", n, x); OpenBSD ecvt/gcvt (MIT)\\n\
return b; 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/dce.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/fileno.h"
#include "libc/intrin/pthread.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
.init.start 400,_init_stderr .init.start 400,_init_stderr
@ -33,5 +34,6 @@
ezlea __stderr_buf,cx ezlea __stderr_buf,cx
mov %rcx,0x18(%rax) # f.buf mov %rcx,0x18(%rax) # f.buf
movl $BUFSIZ,0x20(%rax) # f.size movl $BUFSIZ,0x20(%rax) # f.size
movb $PTHREAD_MUTEX_RECURSIVE,0x38(%rax) # f.lock.attr
mov %rax,stderr(%rip) mov %rax,stderr(%rip)
.init.end 400,_init_stderr,globl,hidden .init.end 400,_init_stderr,globl,hidden

View file

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

View file

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

View file

@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_ #ifndef COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
#define 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/runtime/runtime.h"
#include "libc/thread/spawn.h" #include "libc/thread/spawn.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
@ -10,7 +11,7 @@ COSMOPOLITAN_C_START_
*/ */
enum PosixThreadStatus { enum PosixThreadStatus {
kPosixThreadStarted, kPosixThreadJoinable,
kPosixThreadDetached, kPosixThreadDetached,
kPosixThreadTerminated, kPosixThreadTerminated,
kPosixThreadZombie, kPosixThreadZombie,
@ -19,16 +20,19 @@ enum PosixThreadStatus {
struct PosixThread { struct PosixThread {
struct spawn spawn; struct spawn spawn;
void *(*start_routine)(void *); void *(*start_routine)(void *);
void *arg; void *arg; // start_routine's parameter
void *rc; void *rc; // start_routine's return value
int tid;
_Atomic(enum PosixThreadStatus) status; _Atomic(enum PosixThreadStatus) status;
jmp_buf exiter; jmp_buf exiter;
size_t stacksize;
pthread_attr_t attr;
}; };
void pthread_zombies_add(struct PosixThread *); void pthread_free(struct PosixThread *) hidden;
void pthread_zombies_decimate(void); void pthread_wait(struct PosixThread *) hidden;
void pthread_zombies_harvest(void); void pthread_zombies_add(struct PosixThread *) hidden;
void pthread_zombies_decimate(void) hidden;
void pthread_zombies_harvest(void) hidden;
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #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; tsp = &rel;
} }
if (IsLinux() || IsOpenbsd()) { if (IsLinux() || IsOpenbsd()) {
if (cond->attr == PTHREAD_PROCESS_SHARED) { _futex_wait(&cond->seq, seq, cond->pshared, tsp);
_futex_wait_public(&cond->seq, seq, tsp);
} else {
_futex_wait_private(&cond->seq, seq, tsp);
}
} else { } else {
sched_yield(); sched_yield();
} }

View file

@ -16,19 +16,37 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/atomic.h" #include "libc/intrin/atomic.h"
#include "libc/intrin/pthread.h" #include "libc/intrin/pthread.h"
#include "libc/intrin/wait0.internal.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/nexgen32e/gettls.h" #include "libc/nexgen32e/gettls.h"
#include "libc/runtime/runtime.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/posixthread.internal.h"
#include "libc/thread/spawn.h"
#include "libc/thread/thread.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) { static int PosixThread(void *arg, int tid) {
struct PosixThread *pt = arg; struct PosixThread *pt = arg;
enum PosixThreadStatus status; enum PosixThreadStatus status;
pt->tid = tid;
if (!setjmp(pt->exiter)) { if (!setjmp(pt->exiter)) {
((cthread_t)__get_tls())->pthread = pt; ((cthread_t)__get_tls())->pthread = pt;
pt->rc = pt->start_routine(pt->arg); pt->rc = pt->start_routine(pt->arg);
@ -66,27 +84,128 @@ static int PosixThread(void *arg, int tid) {
* bsdthread_create thr_new * 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 * @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, int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg) { void *(*start_routine)(void *), void *arg) {
int e, rc; int rc, e = errno;
struct PosixThread *pt; struct PosixThread *pt;
e = errno; pthread_attr_t default_attr;
pthread_zombies_decimate(); pthread_zombies_decimate();
if ((pt = calloc(1, sizeof(struct PosixThread)))) {
pt->start_routine = start_routine; // default attributes
pt->arg = arg; if (!attr) {
if (!_spawn(PosixThread, pt, &pt->spawn)) { pthread_attr_init(&default_attr);
*thread = pt; attr = &default_attr;
rc = 0;
} else {
free(pt);
rc = errno;
}
} else {
rc = errno;
} }
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) { if (status == kPosixThreadDetached || status == kPosixThreadZombie) {
break; break;
} else if (status == kPosixThreadTerminated) { } else if (status == kPosixThreadTerminated) {
_join(&pt->spawn); pthread_wait(pt);
free(pt); pthread_free(pt);
break; break;
} else if (status == kPosixThreadStarted && } else if (status == kPosixThreadJoinable &&
atomic_compare_exchange_weak_explicit( atomic_compare_exchange_weak_explicit(
&pt->status, &status, kPosixThreadDetached, &pt->status, &status, kPosixThreadDetached,
memory_order_acquire, memory_order_relaxed)) { memory_order_acquire, memory_order_relaxed)) {

View file

@ -27,5 +27,5 @@
int pthread_equal(pthread_t t1, pthread_t t2) { int pthread_equal(pthread_t t1, pthread_t t2) {
struct PosixThread *a = t1; struct PosixThread *a = t1;
struct PosixThread *b = t2; 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 TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. 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/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) { int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr) {
if ((IsLinux() || IsOpenbsd()) && struct PosixThread *pt = thread;
atomic_load_explicit(&mutex->waits, memory_order_relaxed)) { memcpy(attr, &pt->attr, sizeof(pt->attr));
return _futex_wake_private(&mutex->lock, 1); return 0;
} else {
return 0;
}
} }

View file

@ -24,5 +24,5 @@
*/ */
int64_t pthread_getunique_np(pthread_t thread) { int64_t pthread_getunique_np(pthread_t thread) {
struct PosixThread *pt = 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"); assert(!"badjoin");
return EDEADLK; return EDEADLK;
} }
_join(&pt->spawn); pthread_wait(pt);
if (value_ptr) { if (value_ptr) {
*value_ptr = pt->rc; *value_ptr = pt->rc;
} }
free(pt); pthread_free(pt);
return 0; return 0;
} }

View file

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

View file

@ -44,9 +44,9 @@ void pthread_zombies_add(struct PosixThread *pt) {
} }
} }
void pthread_zombies_destroy(struct Zombie *z) { static void pthread_zombies_collect(struct Zombie *z) {
_join(&z->pt->spawn); pthread_wait(z->pt);
free(z->pt); pthread_free(z->pt);
free(z); free(z);
} }
@ -55,7 +55,7 @@ void pthread_zombies_decimate(void) {
while ((z = atomic_load(&pthread_zombies)) && while ((z = atomic_load(&pthread_zombies)) &&
atomic_load(&z->pt->status) == kPosixThreadZombie) { atomic_load(&z->pt->status) == kPosixThreadZombie) {
if (atomic_compare_exchange_strong(&pthread_zombies, &z, z->next)) { 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; struct Zombie *z;
while ((z = atomic_load(&pthread_zombies))) { while ((z = atomic_load(&pthread_zombies))) {
if (atomic_compare_exchange_weak(&pthread_zombies, &z, z->next)) { 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); free(M);
} }
BENCH(qsort, equivalence_random) { TEST(qsort, equivalence_random) {
size_t i; size_t i;
size_t n = 1000; size_t n = 1000;
long *a = gc(malloc(n * sizeof(long))); long *a = gc(malloc(n * sizeof(long)));
@ -68,7 +68,7 @@ BENCH(qsort, equivalence_random) {
ASSERT_EQ(0, memcmp(b, c, n * sizeof(long))); ASSERT_EQ(0, memcmp(b, c, n * sizeof(long)));
} }
BENCH(qsort, equivalence_reverse) { TEST(qsort, equivalence_reverse) {
size_t i; size_t i;
size_t n = 1000; size_t n = 1000;
long *a = gc(malloc(n * sizeof(long))); 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 TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/errno.h"
#include "libc/limits.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/testlib/ezbench.h" #include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
TEST(l64a, test) { TEST(l64a, test) {
EXPECT_STREQ("", l64a(0)); EXPECT_STREQ("", l64a(0));
EXPECT_STREQ("zzzzz1", l64a(-1));
EXPECT_STREQ("zzzzz/", l64a(0x7fffffff)); EXPECT_STREQ("zzzzz/", l64a(0x7fffffff));
} }
TEST(a64l, test) { TEST(a64l, test) {
EXPECT_EQ(0, a64l("")); EXPECT_EQ(0, a64l(""));
EXPECT_EQ(-1, a64l("zzzzz1"));
EXPECT_EQ(0x7fffffff, a64l("zzzzz/")); 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) { BENCH(a64l, bench) {
EZBENCH2("l64a", donothing, l64a(INT_MAX));
EZBENCH2( EZBENCH2(
"a64l", donothing, "a64l", donothing,
a64l("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")); 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 TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h"
#include "libc/intrin/pthread.h" #include "libc/intrin/pthread.h"
#include "libc/mem/mem.h"
#include "libc/runtime/stack.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
pthread_t thread; static void *Increment(void *arg) {
ASSERT_EQ(gettid(), pthread_getthreadid_np());
static void *ReturnArg(void *arg) { return (void *)((uintptr_t)arg + 1);
return arg;
} }
TEST(pthread_create, testCreateReturnJoin) { TEST(pthread_create, testCreateReturnJoin) {
void *exitcode; void *rc;
ASSERT_EQ(0, pthread_create(&thread, 0, ReturnArg, ReturnArg)); pthread_t id;
ASSERT_EQ(0, pthread_join(thread, &exitcode)); ASSERT_EQ(0, pthread_create(&id, 0, Increment, (void *)1));
ASSERT_EQ(ReturnArg, exitcode); 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) { TEST(pthread_detach, testCreateReturn) {
ASSERT_EQ(0, pthread_create(&thread, 0, ReturnArg, ReturnArg)); pthread_t id;
ASSERT_EQ(0, pthread_detach(thread)); 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) $(APE_NO_MODIFY_SELF)
@$(APELINK) @$(APELINK)
o/$(MODE)/test/libc/thread/pthread_create_test.o: \
private OVERRIDE_CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED
.PHONY: o/$(MODE)/test/libc/thread .PHONY: o/$(MODE)/test/libc/thread
o/$(MODE)/test/libc/thread: \ o/$(MODE)/test/libc/thread: \
$(TEST_LIBC_THREAD_BINS) \ $(TEST_LIBC_THREAD_BINS) \