From 4339d9f15ef9d1679ecba2fac97601c8cdaafb6a Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 7 Sep 2022 05:23:44 -0700 Subject: [PATCH] Add pthread attributes and other libc functions --- .../{struct/timespec_gt.c => _timespec_gt.c} | 0 libc/calls/fadvise64.S | 23 +++ libc/calls/getdents64.S | 23 +++ libc/calls/open64.S | 22 +++ libc/calls/pread64.S | 23 +++ libc/calls/prlimit64.S | 23 +++ libc/calls/pwrite64.S | 23 +++ libc/calls/siglock.c | 6 +- libc/calls/struct/utsname.h | 2 +- libc/fmt/fcvt.c | 128 --------------- libc/intrin/futex.internal.h | 6 +- libc/intrin/futex_wait.c | 19 +-- libc/intrin/futex_wake.c | 18 +-- libc/intrin/g_fds.c | 3 +- libc/intrin/pthread.h | 109 ++++--------- libc/intrin/pthread2.h | 1 + libc/intrin/pthread_attr_destroy.c | 2 +- libc/intrin/pthread_attr_getdetachstate.c | 12 +- libc/intrin/pthread_attr_getguardsize.c | 10 +- libc/intrin/pthread_attr_getstacksize.c | 16 +- libc/intrin/pthread_attr_init.c | 10 +- libc/intrin/pthread_attr_setdetachstate.c | 28 +++- libc/intrin/pthread_attr_setguardsize.c | 16 +- libc/intrin/pthread_attr_setstacksize.c | 29 ++++ libc/intrin/pthread_barrier_destroy.c | 3 +- libc/intrin/pthread_barrier_wait.c | 12 +- libc/intrin/pthread_barrierattr_destroy.c | 6 +- libc/intrin/pthread_barrierattr_getpshared.c | 7 +- libc/intrin/pthread_barrierattr_init.c | 5 +- libc/intrin/pthread_cond_broadcast.c | 6 +- libc/intrin/pthread_cond_destroy.c | 3 +- libc/intrin/pthread_cond_init.c | 5 +- libc/intrin/pthread_condattr_destroy.c | 6 +- libc/intrin/pthread_condattr_getpshared.c | 5 +- libc/intrin/pthread_condattr_init.c | 5 +- libc/intrin/pthread_mutex_destroy.c | 3 +- libc/intrin/pthread_mutex_init.c | 11 +- libc/intrin/pthread_mutex_lock.c | 24 +-- libc/intrin/pthread_mutex_trylock.c | 4 +- libc/intrin/pthread_mutex_unlock.c | 10 +- libc/intrin/pthread_mutexattr_destroy.c | 6 +- libc/intrin/pthread_mutexattr_getpshared.c | 7 +- libc/intrin/pthread_mutexattr_gettype.c | 5 +- libc/intrin/pthread_mutexattr_init.c | 8 +- libc/intrin/pthread_mutexattr_setpshared.c | 2 +- libc/intrin/pthread_mutexattr_settype.c | 2 +- libc/intrin/pthread_rwlock_destroy.c | 3 +- libc/intrin/pthread_rwlock_init.c | 7 +- libc/intrin/pthread_rwlock_rdlock.c | 2 +- libc/intrin/pthread_rwlock_unlock.c | 6 +- libc/intrin/pthread_rwlock_wrlock.c | 2 +- libc/intrin/pthread_rwlockattr_destroy.c | 6 +- libc/intrin/pthread_rwlockattr_getpshared.c | 7 +- libc/intrin/pthread_rwlockattr_init.c | 5 +- libc/intrin/wait0.c | 2 +- libc/stdio/ecvt.c | 110 +++++++++++++ libc/stdio/fdopen.c | 2 + libc/stdio/fflush_unlocked.c | 8 +- libc/stdio/flockfile.c | 1 - libc/stdio/fmemopen.c | 1 + libc/stdio/funlockfile.c | 1 - libc/stdio/gcvt.c | 141 +++++++++++++--- libc/stdio/stderr-init.S | 2 + libc/stdio/stdin-init.S | 2 + libc/stdio/stdout-init.S | 2 + libc/thread/posixthread.internal.h | 18 ++- libc/thread/pthread_attr_getstack.c | 38 +++++ libc/thread/pthread_attr_setstack.c | 106 ++++++++++++ libc/thread/pthread_cond_timedwait.c | 6 +- libc/thread/pthread_create.c | 153 ++++++++++++++++-- libc/thread/pthread_detach.c | 6 +- libc/thread/pthread_equal.c | 2 +- .../pthread_getattr_np.c} | 17 +- libc/thread/pthread_getunique_np.c | 2 +- libc/thread/pthread_join.c | 4 +- libc/thread/wait.c | 5 +- libc/thread/zombie.c | 10 +- test/libc/mem/qsort_test.c | 4 +- test/libc/str/a64l_test.c | 76 ++++++++- test/libc/thread/pthread_create_test.c | 81 ++++++++-- test/libc/thread/test.mk | 4 + 81 files changed, 1111 insertions(+), 428 deletions(-) rename libc/calls/{struct/timespec_gt.c => _timespec_gt.c} (100%) create mode 100644 libc/calls/fadvise64.S create mode 100644 libc/calls/getdents64.S create mode 100644 libc/calls/open64.S create mode 100644 libc/calls/pread64.S create mode 100644 libc/calls/prlimit64.S create mode 100644 libc/calls/pwrite64.S delete mode 100644 libc/fmt/fcvt.c create mode 100644 libc/stdio/ecvt.c create mode 100644 libc/thread/pthread_attr_getstack.c create mode 100644 libc/thread/pthread_attr_setstack.c rename libc/{intrin/pthread_mutex_wake.c => thread/pthread_getattr_np.c} (85%) diff --git a/libc/calls/struct/timespec_gt.c b/libc/calls/_timespec_gt.c similarity index 100% rename from libc/calls/struct/timespec_gt.c rename to libc/calls/_timespec_gt.c diff --git a/libc/calls/fadvise64.S b/libc/calls/fadvise64.S new file mode 100644 index 000000000..317fdd306 --- /dev/null +++ b/libc/calls/fadvise64.S @@ -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 diff --git a/libc/calls/getdents64.S b/libc/calls/getdents64.S new file mode 100644 index 000000000..2259f7cbb --- /dev/null +++ b/libc/calls/getdents64.S @@ -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 diff --git a/libc/calls/open64.S b/libc/calls/open64.S new file mode 100644 index 000000000..f6fe23521 --- /dev/null +++ b/libc/calls/open64.S @@ -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 diff --git a/libc/calls/pread64.S b/libc/calls/pread64.S new file mode 100644 index 000000000..25682b540 --- /dev/null +++ b/libc/calls/pread64.S @@ -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 diff --git a/libc/calls/prlimit64.S b/libc/calls/prlimit64.S new file mode 100644 index 000000000..261bc74a0 --- /dev/null +++ b/libc/calls/prlimit64.S @@ -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 diff --git a/libc/calls/pwrite64.S b/libc/calls/pwrite64.S new file mode 100644 index 000000000..389b984e3 --- /dev/null +++ b/libc/calls/pwrite64.S @@ -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 diff --git a/libc/calls/siglock.c b/libc/calls/siglock.c index 20f5a5611..c9feb3ba2 100644 --- a/libc/calls/siglock.c +++ b/libc/calls/siglock.c @@ -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; +} diff --git a/libc/calls/struct/utsname.h b/libc/calls/struct/utsname.h index 92d5b0713..f98f07b3d 100644 --- a/libc/calls/struct/utsname.h +++ b/libc/calls/struct/utsname.h @@ -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 */ diff --git a/libc/fmt/fcvt.c b/libc/fmt/fcvt.c deleted file mode 100644 index 5d81ed541..000000000 --- a/libc/fmt/fcvt.c +++ /dev/null @@ -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); -} diff --git a/libc/intrin/futex.internal.h b/libc/intrin/futex.internal.h index 79840e1d3..732c10891 100644 --- a/libc/intrin/futex.internal.h +++ b/libc/intrin/futex.internal.h @@ -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) */ diff --git a/libc/intrin/futex_wait.c b/libc/intrin/futex_wait.c index 134e6bc84..bb6a6c5c6 100644 --- a/libc/intrin/futex_wait.c +++ b/libc/intrin/futex_wait.c @@ -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); -} diff --git a/libc/intrin/futex_wake.c b/libc/intrin/futex_wake.c index 7a80f4ccd..d53330a57 100644 --- a/libc/intrin/futex_wake.c +++ b/libc/intrin/futex_wake.c @@ -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); -} diff --git a/libc/intrin/g_fds.c b/libc/intrin/g_fds.c index b90a1435b..fb7908dbf 100644 --- a/libc/intrin/g_fds.c +++ b/libc/intrin/g_fds.c @@ -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; diff --git a/libc/intrin/pthread.h b/libc/intrin/pthread.h index fa05174ea..62dfe4dfc 100644 --- a/libc/intrin/pthread.h +++ b/libc/intrin/pthread.h @@ -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_ */ diff --git a/libc/intrin/pthread2.h b/libc/intrin/pthread2.h index 31319ce06..5d3c18332 100644 --- a/libc/intrin/pthread2.h +++ b/libc/intrin/pthread2.h @@ -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_ diff --git a/libc/intrin/pthread_attr_destroy.c b/libc/intrin/pthread_attr_destroy.c index 59c1e7e71..b8690c31f 100644 --- a/libc/intrin/pthread_attr_destroy.c +++ b/libc/intrin/pthread_attr_destroy.c @@ -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; } diff --git a/libc/intrin/pthread_attr_getdetachstate.c b/libc/intrin/pthread_attr_getdetachstate.c index b229aa8aa..ada3daf78 100644 --- a/libc/intrin/pthread_attr_getdetachstate.c +++ b/libc/intrin/pthread_attr_getdetachstate.c @@ -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; } diff --git a/libc/intrin/pthread_attr_getguardsize.c b/libc/intrin/pthread_attr_getguardsize.c index 94a5d25ea..b91416e39 100644 --- a/libc/intrin/pthread_attr_getguardsize.c +++ b/libc/intrin/pthread_attr_getguardsize.c @@ -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; } diff --git a/libc/intrin/pthread_attr_getstacksize.c b/libc/intrin/pthread_attr_getstacksize.c index a20785dc0..d4c5489b1 100644 --- a/libc/intrin/pthread_attr_getstacksize.c +++ b/libc/intrin/pthread_attr_getstacksize.c @@ -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; } diff --git a/libc/intrin/pthread_attr_init.c b/libc/intrin/pthread_attr_init.c index fc674e5ad..ee00ed36f 100644 --- a/libc/intrin/pthread_attr_init.c +++ b/libc/intrin/pthread_attr_init.c @@ -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; } diff --git a/libc/intrin/pthread_attr_setdetachstate.c b/libc/intrin/pthread_attr_setdetachstate.c index 84d0d2345..5d37579a5 100644 --- a/libc/intrin/pthread_attr_setdetachstate.c +++ b/libc/intrin/pthread_attr_setdetachstate.c @@ -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; + } } diff --git a/libc/intrin/pthread_attr_setguardsize.c b/libc/intrin/pthread_attr_setguardsize.c index f1e19a1b5..4391aafc6 100644 --- a/libc/intrin/pthread_attr_setguardsize.c +++ b/libc/intrin/pthread_attr_setguardsize.c @@ -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; } diff --git a/libc/intrin/pthread_attr_setstacksize.c b/libc/intrin/pthread_attr_setstacksize.c index 5ab1e5a6d..0c89c831c 100644 --- a/libc/intrin/pthread_attr_setstacksize.c +++ b/libc/intrin/pthread_attr_setstacksize.c @@ -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; } diff --git a/libc/intrin/pthread_barrier_destroy.c b/libc/intrin/pthread_barrier_destroy.c index 502a6e64b..9dca7a62a 100644 --- a/libc/intrin/pthread_barrier_destroy.c +++ b/libc/intrin/pthread_barrier_destroy.c @@ -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; } diff --git a/libc/intrin/pthread_barrier_wait.c b/libc/intrin/pthread_barrier_wait.c index f5aea8782..95a6c424a 100644 --- a/libc/intrin/pthread_barrier_wait.c +++ b/libc/intrin/pthread_barrier_wait.c @@ -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(); } diff --git a/libc/intrin/pthread_barrierattr_destroy.c b/libc/intrin/pthread_barrierattr_destroy.c index f0cc9598a..9a51e9f7f 100644 --- a/libc/intrin/pthread_barrierattr_destroy.c +++ b/libc/intrin/pthread_barrierattr_destroy.c @@ -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; } diff --git a/libc/intrin/pthread_barrierattr_getpshared.c b/libc/intrin/pthread_barrierattr_getpshared.c index 5a432d434..63e9228a8 100644 --- a/libc/intrin/pthread_barrierattr_getpshared.c +++ b/libc/intrin/pthread_barrierattr_getpshared.c @@ -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; } diff --git a/libc/intrin/pthread_barrierattr_init.c b/libc/intrin/pthread_barrierattr_init.c index 20083ec15..9a79d1460 100644 --- a/libc/intrin/pthread_barrierattr_init.c +++ b/libc/intrin/pthread_barrierattr_init.c @@ -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; } diff --git a/libc/intrin/pthread_cond_broadcast.c b/libc/intrin/pthread_cond_broadcast.c index cafd530c1..5c1cb8264 100644 --- a/libc/intrin/pthread_cond_broadcast.c +++ b/libc/intrin/pthread_cond_broadcast.c @@ -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; diff --git a/libc/intrin/pthread_cond_destroy.c b/libc/intrin/pthread_cond_destroy.c index c06653784..0fc53ba88 100644 --- a/libc/intrin/pthread_cond_destroy.c +++ b/libc/intrin/pthread_cond_destroy.c @@ -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; } diff --git a/libc/intrin/pthread_cond_init.c b/libc/intrin/pthread_cond_init.c index 6a57842b3..f683b6350 100644 --- a/libc/intrin/pthread_cond_init.c +++ b/libc/intrin/pthread_cond_init.c @@ -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; } diff --git a/libc/intrin/pthread_condattr_destroy.c b/libc/intrin/pthread_condattr_destroy.c index 509fd64ef..7ea243c59 100644 --- a/libc/intrin/pthread_condattr_destroy.c +++ b/libc/intrin/pthread_condattr_destroy.c @@ -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; } diff --git a/libc/intrin/pthread_condattr_getpshared.c b/libc/intrin/pthread_condattr_getpshared.c index a1ef93147..b340781ff 100644 --- a/libc/intrin/pthread_condattr_getpshared.c +++ b/libc/intrin/pthread_condattr_getpshared.c @@ -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; } diff --git a/libc/intrin/pthread_condattr_init.c b/libc/intrin/pthread_condattr_init.c index d0a5f90e0..40a35f179 100644 --- a/libc/intrin/pthread_condattr_init.c +++ b/libc/intrin/pthread_condattr_init.c @@ -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; } diff --git a/libc/intrin/pthread_mutex_destroy.c b/libc/intrin/pthread_mutex_destroy.c index 2bbc2c169..5a5312910 100644 --- a/libc/intrin/pthread_mutex_destroy.c +++ b/libc/intrin/pthread_mutex_destroy.c @@ -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; } diff --git a/libc/intrin/pthread_mutex_init.c b/libc/intrin/pthread_mutex_init.c index 24f6dc4eb..0645c6a0e 100644 --- a/libc/intrin/pthread_mutex_init.c +++ b/libc/intrin/pthread_mutex_init.c @@ -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; } diff --git a/libc/intrin/pthread_mutex_lock.c b/libc/intrin/pthread_mutex_lock.c index 48486d411..c92332b1b 100644 --- a/libc/intrin/pthread_mutex_lock.c +++ b/libc/intrin/pthread_mutex_lock.c @@ -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"); diff --git a/libc/intrin/pthread_mutex_trylock.c b/libc/intrin/pthread_mutex_trylock.c index 3a6a58a23..6d1a20151 100644 --- a/libc/intrin/pthread_mutex_trylock.c +++ b/libc/intrin/pthread_mutex_trylock.c @@ -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 { diff --git a/libc/intrin/pthread_mutex_unlock.c b/libc/intrin/pthread_mutex_unlock.c index 63252c9b4..430b9c8c6 100644 --- a/libc/intrin/pthread_mutex_unlock.c +++ b/libc/intrin/pthread_mutex_unlock.c @@ -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: diff --git a/libc/intrin/pthread_mutexattr_destroy.c b/libc/intrin/pthread_mutexattr_destroy.c index 754065950..a6fc17e3e 100644 --- a/libc/intrin/pthread_mutexattr_destroy.c +++ b/libc/intrin/pthread_mutexattr_destroy.c @@ -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; } diff --git a/libc/intrin/pthread_mutexattr_getpshared.c b/libc/intrin/pthread_mutexattr_getpshared.c index 36037e9f3..8d76df79b 100644 --- a/libc/intrin/pthread_mutexattr_getpshared.c +++ b/libc/intrin/pthread_mutexattr_getpshared.c @@ -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; } diff --git a/libc/intrin/pthread_mutexattr_gettype.c b/libc/intrin/pthread_mutexattr_gettype.c index 6fbae531c..2c7ef3e0a 100644 --- a/libc/intrin/pthread_mutexattr_gettype.c +++ b/libc/intrin/pthread_mutexattr_gettype.c @@ -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; } diff --git a/libc/intrin/pthread_mutexattr_init.c b/libc/intrin/pthread_mutexattr_init.c index 10856580d..5bb326829 100644 --- a/libc/intrin/pthread_mutexattr_init.c +++ b/libc/intrin/pthread_mutexattr_init.c @@ -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; } diff --git a/libc/intrin/pthread_mutexattr_setpshared.c b/libc/intrin/pthread_mutexattr_setpshared.c index 0ace6170e..8152edce2 100644 --- a/libc/intrin/pthread_mutexattr_setpshared.c +++ b/libc/intrin/pthread_mutexattr_setpshared.c @@ -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; diff --git a/libc/intrin/pthread_mutexattr_settype.c b/libc/intrin/pthread_mutexattr_settype.c index 9f875881c..546459156 100644 --- a/libc/intrin/pthread_mutexattr_settype.c +++ b/libc/intrin/pthread_mutexattr_settype.c @@ -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; diff --git a/libc/intrin/pthread_rwlock_destroy.c b/libc/intrin/pthread_rwlock_destroy.c index 9135b46d0..eb0b3c906 100644 --- a/libc/intrin/pthread_rwlock_destroy.c +++ b/libc/intrin/pthread_rwlock_destroy.c @@ -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; } diff --git a/libc/intrin/pthread_rwlock_init.c b/libc/intrin/pthread_rwlock_init.c index 71a0198b3..392dde1c0 100644 --- a/libc/intrin/pthread_rwlock_init.c +++ b/libc/intrin/pthread_rwlock_init.c @@ -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; } diff --git a/libc/intrin/pthread_rwlock_rdlock.c b/libc/intrin/pthread_rwlock_rdlock.c index 0ecbf3be5..23a9c7941 100644 --- a/libc/intrin/pthread_rwlock_rdlock.c +++ b/libc/intrin/pthread_rwlock_rdlock.c @@ -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(); diff --git a/libc/intrin/pthread_rwlock_unlock.c b/libc/intrin/pthread_rwlock_unlock.c index 72dbef1e3..168fc644d 100644 --- a/libc/intrin/pthread_rwlock_unlock.c +++ b/libc/intrin/pthread_rwlock_unlock.c @@ -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; } diff --git a/libc/intrin/pthread_rwlock_wrlock.c b/libc/intrin/pthread_rwlock_wrlock.c index 1e2e9b332..4adbc2d1b 100644 --- a/libc/intrin/pthread_rwlock_wrlock.c +++ b/libc/intrin/pthread_rwlock_wrlock.c @@ -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(); diff --git a/libc/intrin/pthread_rwlockattr_destroy.c b/libc/intrin/pthread_rwlockattr_destroy.c index 4cd78e5f4..b190001ce 100644 --- a/libc/intrin/pthread_rwlockattr_destroy.c +++ b/libc/intrin/pthread_rwlockattr_destroy.c @@ -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; } diff --git a/libc/intrin/pthread_rwlockattr_getpshared.c b/libc/intrin/pthread_rwlockattr_getpshared.c index 6f4d91e51..aafde2afe 100644 --- a/libc/intrin/pthread_rwlockattr_getpshared.c +++ b/libc/intrin/pthread_rwlockattr_getpshared.c @@ -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; } diff --git a/libc/intrin/pthread_rwlockattr_init.c b/libc/intrin/pthread_rwlockattr_init.c index adca969f1..f64ded572 100644 --- a/libc/intrin/pthread_rwlockattr_init.c +++ b/libc/intrin/pthread_rwlockattr_init.c @@ -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; } diff --git a/libc/intrin/wait0.c b/libc/intrin/wait0.c index df7c84b29..e850e4249 100644 --- a/libc/intrin/wait0.c +++ b/libc/intrin/wait0.c @@ -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(); } diff --git a/libc/stdio/ecvt.c b/libc/stdio/ecvt.c new file mode 100644 index 000000000..61f4455d9 --- /dev/null +++ b/libc/stdio/ecvt.c @@ -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 │ +│ │ +│ 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 \""); +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)); +} diff --git a/libc/stdio/fdopen.c b/libc/stdio/fdopen.c index 0f786faba..4a25238cd 100644 --- a/libc/stdio/fdopen.c +++ b/libc/stdio/fdopen.c @@ -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) { diff --git a/libc/stdio/fflush_unlocked.c b/libc/stdio/fflush_unlocked.c index e98fb6b41..b5beec78d 100644 --- a/libc/stdio/fflush_unlocked.c +++ b/libc/stdio/fflush_unlocked.c @@ -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); } diff --git a/libc/stdio/flockfile.c b/libc/stdio/flockfile.c index f1a537aeb..e1a3872ca 100644 --- a/libc/stdio/flockfile.c +++ b/libc/stdio/flockfile.c @@ -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); } diff --git a/libc/stdio/fmemopen.c b/libc/stdio/fmemopen.c index d928dfa6e..28c12cd74 100644 --- a/libc/stdio/fmemopen.c +++ b/libc/stdio/fmemopen.c @@ -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; diff --git a/libc/stdio/funlockfile.c b/libc/stdio/funlockfile.c index b812edd39..bef058320 100644 --- a/libc/stdio/funlockfile.c +++ b/libc/stdio/funlockfile.c @@ -22,6 +22,5 @@ * Releases lock on stdio object. */ void(funlockfile)(FILE *f) { - f->lock.attr = PTHREAD_MUTEX_RECURSIVE; pthread_mutex_unlock(&f->lock); } diff --git a/libc/stdio/gcvt.c b/libc/stdio/gcvt.c index 462959915..633856e09 100644 --- a/libc/stdio/gcvt.c +++ b/libc/stdio/gcvt.c @@ -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 │ │ │ -│ 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 \""); +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); } diff --git a/libc/stdio/stderr-init.S b/libc/stdio/stderr-init.S index c99e018e4..b5be107f5 100644 --- a/libc/stdio/stderr-init.S +++ b/libc/stdio/stderr-init.S @@ -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 diff --git a/libc/stdio/stdin-init.S b/libc/stdio/stdin-init.S index 3842f3e7f..2f6d03d0d 100644 --- a/libc/stdio/stdin-init.S +++ b/libc/stdio/stdin-init.S @@ -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 diff --git a/libc/stdio/stdout-init.S b/libc/stdio/stdout-init.S index 19bed44af..19c549e10 100644 --- a/libc/stdio/stdout-init.S +++ b/libc/stdio/stdout-init.S @@ -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 diff --git a/libc/thread/posixthread.internal.h b/libc/thread/posixthread.internal.h index 9fe68e47c..c21716d15 100644 --- a/libc/thread/posixthread.internal.h +++ b/libc/thread/posixthread.internal.h @@ -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) */ diff --git a/libc/thread/pthread_attr_getstack.c b/libc/thread/pthread_attr_getstack.c new file mode 100644 index 000000000..bed6b5c46 --- /dev/null +++ b/libc/thread/pthread_attr_getstack.c @@ -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; +} diff --git a/libc/thread/pthread_attr_setstack.c b/libc/thread/pthread_attr_setstack.c new file mode 100644 index 000000000..1772c6195 --- /dev/null +++ b/libc/thread/pthread_attr_setstack.c @@ -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; +} diff --git a/libc/thread/pthread_cond_timedwait.c b/libc/thread/pthread_cond_timedwait.c index cb2d0263c..b10684053 100644 --- a/libc/thread/pthread_cond_timedwait.c +++ b/libc/thread/pthread_cond_timedwait.c @@ -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(); } diff --git a/libc/thread/pthread_create.c b/libc/thread/pthread_create.c index e1dd14eff..d186e1617 100644 --- a/libc/thread/pthread_create.c +++ b/libc/thread/pthread_create.c @@ -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; } diff --git a/libc/thread/pthread_detach.c b/libc/thread/pthread_detach.c index 28655b9f8..56342d288 100644 --- a/libc/thread/pthread_detach.c +++ b/libc/thread/pthread_detach.c @@ -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)) { diff --git a/libc/thread/pthread_equal.c b/libc/thread/pthread_equal.c index 0f2fd79cb..85b60dfb5 100644 --- a/libc/thread/pthread_equal.c +++ b/libc/thread/pthread_equal.c @@ -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; } diff --git a/libc/intrin/pthread_mutex_wake.c b/libc/thread/pthread_getattr_np.c similarity index 85% rename from libc/intrin/pthread_mutex_wake.c rename to libc/thread/pthread_getattr_np.c index 5060e035d..a3b66ea20 100644 --- a/libc/intrin/pthread_mutex_wake.c +++ b/libc/thread/pthread_getattr_np.c @@ -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; } diff --git a/libc/thread/pthread_getunique_np.c b/libc/thread/pthread_getunique_np.c index f1b477881..f4aea3f1a 100644 --- a/libc/thread/pthread_getunique_np.c +++ b/libc/thread/pthread_getunique_np.c @@ -24,5 +24,5 @@ */ int64_t pthread_getunique_np(pthread_t thread) { struct PosixThread *pt = thread; - return pt->tid; + return pt->spawn.ptid; } diff --git a/libc/thread/pthread_join.c b/libc/thread/pthread_join.c index 949d6b8f0..4361bfbf9 100644 --- a/libc/thread/pthread_join.c +++ b/libc/thread/pthread_join.c @@ -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; } diff --git a/libc/thread/wait.c b/libc/thread/wait.c index 33b9c1b09..d301b5a02 100644 --- a/libc/thread/wait.c +++ b/libc/thread/wait.c @@ -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; } diff --git a/libc/thread/zombie.c b/libc/thread/zombie.c index ea5d78ad8..d9a43a676 100644 --- a/libc/thread/zombie.c +++ b/libc/thread/zombie.c @@ -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); } } } diff --git a/test/libc/mem/qsort_test.c b/test/libc/mem/qsort_test.c index a420e4c97..f8cb2f45e 100644 --- a/test/libc/mem/qsort_test.c +++ b/test/libc/mem/qsort_test.c @@ -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))); diff --git a/test/libc/str/a64l_test.c b/test/libc/str/a64l_test.c index 7d171a9fc..ad3a77ad3 100644 --- a/test/libc/str/a64l_test.c +++ b/test/libc/str/a64l_test.c @@ -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")); } diff --git a/test/libc/thread/pthread_create_test.c b/test/libc/thread/pthread_create_test.c index 8d7eadd22..106238def 100644 --- a/test/libc/thread/pthread_create_test.c +++ b/test/libc/thread/pthread_create_test.c @@ -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); } diff --git a/test/libc/thread/test.mk b/test/libc/thread/test.mk index e0f51e580..bbd33da99 100644 --- a/test/libc/thread/test.mk +++ b/test/libc/thread/test.mk @@ -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) \