mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-07 06:04:44 +00:00
Add pthread attributes and other libc functions
This commit is contained in:
parent
d5c9308a43
commit
4339d9f15e
81 changed files with 1111 additions and 428 deletions
23
libc/calls/fadvise64.S
Normal file
23
libc/calls/fadvise64.S
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||||
|
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
|
fadvise64:
|
||||||
|
jmp fadvise
|
||||||
|
.endfn fadvise64,globl
|
23
libc/calls/getdents64.S
Normal file
23
libc/calls/getdents64.S
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||||
|
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
|
getdents64:
|
||||||
|
jmp getdents
|
||||||
|
.endfn getdents64,globl
|
22
libc/calls/open64.S
Normal file
22
libc/calls/open64.S
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||||
|
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
|
open64: jmp open
|
||||||
|
.endfn open64,globl
|
23
libc/calls/pread64.S
Normal file
23
libc/calls/pread64.S
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||||
|
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
|
pread64:
|
||||||
|
jmp pread
|
||||||
|
.endfn pread64,globl
|
23
libc/calls/prlimit64.S
Normal file
23
libc/calls/prlimit64.S
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||||
|
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
|
prlimit64:
|
||||||
|
jmp prlimit
|
||||||
|
.endfn prlimit64,globl
|
23
libc/calls/pwrite64.S
Normal file
23
libc/calls/pwrite64.S
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||||
|
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
|
pwrite64:
|
||||||
|
jmp pwrite
|
||||||
|
.endfn pwrite64,globl
|
|
@ -22,11 +22,13 @@
|
||||||
static pthread_mutex_t __sig_lock_obj;
|
static pthread_mutex_t __sig_lock_obj;
|
||||||
|
|
||||||
void(__sig_lock)(void) {
|
void(__sig_lock)(void) {
|
||||||
__sig_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
|
|
||||||
pthread_mutex_lock(&__sig_lock_obj);
|
pthread_mutex_lock(&__sig_lock_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void(__sig_unlock)(void) {
|
void(__sig_unlock)(void) {
|
||||||
__sig_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
|
|
||||||
pthread_mutex_unlock(&__sig_lock_obj);
|
pthread_mutex_unlock(&__sig_lock_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__attribute__((__constructor__)) static void init(void) {
|
||||||
|
__sig_lock_obj.type = PTHREAD_MUTEX_RECURSIVE;
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
struct utsname {
|
struct utsname { /* cosmo abi */
|
||||||
char sysname[SYS_NMLN]; /* name of os */
|
char sysname[SYS_NMLN]; /* name of os */
|
||||||
char nodename[SYS_NMLN]; /* name of network node */
|
char nodename[SYS_NMLN]; /* name of network node */
|
||||||
char release[SYS_NMLN]; /* release level */
|
char release[SYS_NMLN]; /* release level */
|
||||||
|
|
128
libc/fmt/fcvt.c
128
libc/fmt/fcvt.c
|
@ -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);
|
|
||||||
}
|
|
|
@ -4,10 +4,8 @@
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
int _futex_wait_public(void *, int, struct timespec *) hidden;
|
int _futex_wait(void *, int, char, struct timespec *) hidden;
|
||||||
int _futex_wait_private(void *, int, struct timespec *) hidden;
|
int _futex_wake(void *, int, char) hidden;
|
||||||
int _futex_wake_public(void *, int) hidden;
|
|
||||||
int _futex_wake_private(void *, int) hidden;
|
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
|
@ -28,13 +28,14 @@
|
||||||
|
|
||||||
int _futex(void *, int, int, struct timespec *) hidden;
|
int _futex(void *, int, int, struct timespec *) hidden;
|
||||||
|
|
||||||
static dontinline int _futex_wait_impl(void *addr, int expect,
|
int _futex_wait(void *addr, int expect, char pshared,
|
||||||
struct timespec *timeout, int private) {
|
struct timespec *timeout) {
|
||||||
int op, ax;
|
int op, ax, pf;
|
||||||
if (IsLinux() || IsOpenbsd()) {
|
if (IsLinux() || IsOpenbsd()) {
|
||||||
op = FUTEX_WAIT | private;
|
pf = pshared == PTHREAD_PROCESS_PRIVATE ? FUTEX_PRIVATE_FLAG : 0;
|
||||||
|
op = FUTEX_WAIT | pf;
|
||||||
ax = _futex(addr, op, expect, timeout);
|
ax = _futex(addr, op, expect, timeout);
|
||||||
if (SupportsLinux() && private && ax == -ENOSYS) {
|
if (SupportsLinux() && pf && ax == -ENOSYS) {
|
||||||
// RHEL5 doesn't support FUTEX_PRIVATE_FLAG
|
// RHEL5 doesn't support FUTEX_PRIVATE_FLAG
|
||||||
op = FUTEX_WAIT;
|
op = FUTEX_WAIT;
|
||||||
ax = _futex(addr, op, expect, timeout);
|
ax = _futex(addr, op, expect, timeout);
|
||||||
|
@ -47,11 +48,3 @@ static dontinline int _futex_wait_impl(void *addr, int expect,
|
||||||
return pthread_yield();
|
return pthread_yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int _futex_wait_public(void *addr, int expect, struct timespec *timeout) {
|
|
||||||
return _futex_wait_impl(addr, expect, timeout, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int _futex_wait_private(void *addr, int expect, struct timespec *timeout) {
|
|
||||||
return _futex_wait_impl(addr, expect, timeout, FUTEX_PRIVATE_FLAG);
|
|
||||||
}
|
|
||||||
|
|
|
@ -21,16 +21,18 @@
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/describeflags.internal.h"
|
#include "libc/intrin/describeflags.internal.h"
|
||||||
#include "libc/intrin/futex.internal.h"
|
#include "libc/intrin/futex.internal.h"
|
||||||
|
#include "libc/intrin/pthread.h"
|
||||||
#include "libc/sysv/consts/futex.h"
|
#include "libc/sysv/consts/futex.h"
|
||||||
|
|
||||||
int _futex(void *, int, int) hidden;
|
int _futex(void *, int, int) hidden;
|
||||||
|
|
||||||
static dontinline int _futex_wake_impl(void *addr, int count, int private) {
|
int _futex_wake(void *addr, int count, char pshared) {
|
||||||
int op, ax;
|
int op, ax, pf;
|
||||||
if (IsLinux() || IsOpenbsd()) {
|
if (IsLinux() || IsOpenbsd()) {
|
||||||
op = FUTEX_WAKE | private;
|
pf = pshared == PTHREAD_PROCESS_PRIVATE ? FUTEX_PRIVATE_FLAG : 0;
|
||||||
|
op = FUTEX_WAKE | pf;
|
||||||
ax = _futex(addr, op, count);
|
ax = _futex(addr, op, count);
|
||||||
if (SupportsLinux() && private && ax == -ENOSYS) {
|
if (SupportsLinux() && pf && ax == -ENOSYS) {
|
||||||
// RHEL5 doesn't support FUTEX_PRIVATE_FLAG
|
// RHEL5 doesn't support FUTEX_PRIVATE_FLAG
|
||||||
op = FUTEX_WAKE;
|
op = FUTEX_WAKE;
|
||||||
ax = _futex(addr, op, count);
|
ax = _futex(addr, op, count);
|
||||||
|
@ -42,11 +44,3 @@ static dontinline int _futex_wake_impl(void *addr, int count, int private) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int _futex_wake_public(void *addr, int count) {
|
|
||||||
return _futex_wake_impl(addr, count, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int _futex_wake_private(void *addr, int count) {
|
|
||||||
return _futex_wake_impl(addr, count, FUTEX_PRIVATE_FLAG);
|
|
||||||
}
|
|
||||||
|
|
|
@ -31,12 +31,10 @@ struct Fds g_fds;
|
||||||
static pthread_mutex_t __fds_lock_obj;
|
static pthread_mutex_t __fds_lock_obj;
|
||||||
|
|
||||||
void(__fds_lock)(void) {
|
void(__fds_lock)(void) {
|
||||||
__fds_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
|
|
||||||
pthread_mutex_lock(&__fds_lock_obj);
|
pthread_mutex_lock(&__fds_lock_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void(__fds_unlock)(void) {
|
void(__fds_unlock)(void) {
|
||||||
__fds_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
|
|
||||||
pthread_mutex_unlock(&__fds_lock_obj);
|
pthread_mutex_unlock(&__fds_lock_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +49,7 @@ static textwindows dontinline void SetupWinStd(struct Fds *fds, int i, int x) {
|
||||||
|
|
||||||
textstartup void InitializeFileDescriptors(void) {
|
textstartup void InitializeFileDescriptors(void) {
|
||||||
struct Fds *fds;
|
struct Fds *fds;
|
||||||
|
__fds_lock_obj.type = PTHREAD_MUTEX_RECURSIVE;
|
||||||
fds = VEIL("r", &g_fds);
|
fds = VEIL("r", &g_fds);
|
||||||
pushmov(&fds->n, ARRAYLEN(fds->__init_p));
|
pushmov(&fds->n, ARRAYLEN(fds->__init_p));
|
||||||
fds->f = 3;
|
fds->f = 3;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#define PTHREAD_ONCE_INIT 0
|
#define PTHREAD_ONCE_INIT 0
|
||||||
|
|
||||||
#define PTHREAD_KEYS_MAX 64
|
#define PTHREAD_KEYS_MAX 64
|
||||||
#define PTHREAD_STACK_MIN 2048
|
#define PTHREAD_STACK_MIN FRAMESIZE
|
||||||
#define PTHREAD_DESTRUCTOR_ITERATIONS 4
|
#define PTHREAD_DESTRUCTOR_ITERATIONS 4
|
||||||
|
|
||||||
#define PTHREAD_BARRIER_SERIAL_THREAD 31337
|
#define PTHREAD_BARRIER_SERIAL_THREAD 31337
|
||||||
|
@ -20,60 +20,70 @@
|
||||||
#define PTHREAD_PROCESS_PRIVATE 0
|
#define PTHREAD_PROCESS_PRIVATE 0
|
||||||
#define PTHREAD_PROCESS_SHARED 1
|
#define PTHREAD_PROCESS_SHARED 1
|
||||||
|
|
||||||
|
#define PTHREAD_CREATE_JOINABLE 0
|
||||||
|
#define PTHREAD_CREATE_DETACHED 1
|
||||||
|
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
#define PTHREAD_MUTEX_INITIALIZER {PTHREAD_MUTEX_DEFAULT}
|
|
||||||
#define PTHREAD_COND_INITIALIZER {PTHREAD_PROCESS_DEFAULT}
|
#define PTHREAD_COND_INITIALIZER {PTHREAD_PROCESS_DEFAULT}
|
||||||
#define PTHREAD_BARRIER_INITIALIZER {PTHREAD_PROCESS_DEFAULT}
|
#define PTHREAD_BARRIER_INITIALIZER {PTHREAD_PROCESS_DEFAULT}
|
||||||
#define PTHREAD_RWLOCK_INITIALIZER {PTHREAD_PROCESS_DEFAULT}
|
#define PTHREAD_RWLOCK_INITIALIZER {PTHREAD_PROCESS_DEFAULT}
|
||||||
|
#define PTHREAD_MUTEX_INITIALIZER {PTHREAD_MUTEX_DEFAULT, \
|
||||||
|
PTHREAD_PROCESS_DEFAULT}
|
||||||
/* clang-format on */
|
/* clang-format on */
|
||||||
|
|
||||||
typedef void *pthread_t;
|
typedef void *pthread_t;
|
||||||
typedef int pthread_id_np_t;
|
typedef int pthread_id_np_t;
|
||||||
typedef int pthread_condattr_t;
|
typedef char pthread_condattr_t;
|
||||||
typedef int pthread_mutexattr_t;
|
typedef char pthread_rwlockattr_t;
|
||||||
typedef int pthread_rwlockattr_t;
|
typedef char pthread_barrierattr_t;
|
||||||
typedef int pthread_barrierattr_t;
|
|
||||||
typedef unsigned pthread_key_t;
|
typedef unsigned pthread_key_t;
|
||||||
typedef _Atomic(char) pthread_once_t;
|
typedef _Atomic(char) pthread_once_t;
|
||||||
typedef _Atomic(char) pthread_spinlock_t;
|
typedef _Atomic(char) pthread_spinlock_t;
|
||||||
typedef void (*pthread_key_dtor)(void *);
|
typedef void (*pthread_key_dtor)(void *);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct pthread_mutex_s {
|
||||||
int attr;
|
char type;
|
||||||
|
char pshared;
|
||||||
int reent;
|
int reent;
|
||||||
_Atomic(int) lock;
|
_Atomic(int) lock;
|
||||||
_Atomic(int) waits;
|
_Atomic(int) waits;
|
||||||
} pthread_mutex_t;
|
} pthread_mutex_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct pthread_mutexattr_s {
|
||||||
int attr;
|
char type;
|
||||||
|
char pshared;
|
||||||
|
} pthread_mutexattr_t;
|
||||||
|
|
||||||
|
typedef struct pthread_cond_s {
|
||||||
|
char pshared;
|
||||||
_Atomic(int) waits;
|
_Atomic(int) waits;
|
||||||
_Atomic(unsigned) seq;
|
_Atomic(unsigned) seq;
|
||||||
} pthread_cond_t;
|
} pthread_cond_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct pthread_barrier_s {
|
||||||
int attr;
|
char pshared;
|
||||||
int count;
|
int count;
|
||||||
_Atomic(int) waits;
|
_Atomic(int) waits;
|
||||||
_Atomic(int) popped;
|
_Atomic(int) popped;
|
||||||
} pthread_barrier_t;
|
} pthread_barrier_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct pthread_rwlock_s {
|
||||||
int attr;
|
char pshared;
|
||||||
_Atomic(int) lock;
|
_Atomic(int) lock;
|
||||||
_Atomic(int) waits;
|
_Atomic(int) waits;
|
||||||
} pthread_rwlock_t;
|
} pthread_rwlock_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct pthread_attr_s {
|
||||||
|
char detachstate;
|
||||||
|
size_t stacksize;
|
||||||
|
size_t guardsize;
|
||||||
|
void *stackaddr;
|
||||||
int scope;
|
int scope;
|
||||||
int schedpolicy;
|
int schedpolicy;
|
||||||
int detachstate;
|
|
||||||
int inheritsched;
|
int inheritsched;
|
||||||
size_t guardsize;
|
|
||||||
size_t stacksize;
|
|
||||||
} pthread_attr_t;
|
} pthread_attr_t;
|
||||||
|
|
||||||
int pthread_yield(void);
|
int pthread_yield(void);
|
||||||
|
@ -81,6 +91,7 @@ void pthread_exit(void *) wontreturn;
|
||||||
pthread_t pthread_self(void) pureconst;
|
pthread_t pthread_self(void) pureconst;
|
||||||
pthread_id_np_t pthread_getthreadid_np(void);
|
pthread_id_np_t pthread_getthreadid_np(void);
|
||||||
int64_t pthread_getunique_np(pthread_t);
|
int64_t pthread_getunique_np(pthread_t);
|
||||||
|
int pthread_getattr_np(pthread_t, pthread_attr_t *);
|
||||||
int pthread_attr_init(pthread_attr_t *);
|
int pthread_attr_init(pthread_attr_t *);
|
||||||
int pthread_attr_destroy(pthread_attr_t *);
|
int pthread_attr_destroy(pthread_attr_t *);
|
||||||
int pthread_attr_getdetachstate(const pthread_attr_t *, int *);
|
int pthread_attr_getdetachstate(const pthread_attr_t *, int *);
|
||||||
|
@ -156,7 +167,6 @@ int pthread_barrier_init(pthread_barrier_t *, const pthread_barrierattr_t *,
|
||||||
|
|
||||||
#define pthread_spin_init(pSpin, multiprocess) (*(pSpin) = 0)
|
#define pthread_spin_init(pSpin, multiprocess) (*(pSpin) = 0)
|
||||||
#define pthread_spin_destroy(pSpin) (*(pSpin) = 0)
|
#define pthread_spin_destroy(pSpin) (*(pSpin) = 0)
|
||||||
|
|
||||||
#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407
|
#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407
|
||||||
extern const errno_t EBUSY;
|
extern const errno_t EBUSY;
|
||||||
#define pthread_spin_unlock(pSpin) \
|
#define pthread_spin_unlock(pSpin) \
|
||||||
|
@ -164,18 +174,18 @@ extern const errno_t EBUSY;
|
||||||
#define pthread_spin_trylock(pSpin) \
|
#define pthread_spin_trylock(pSpin) \
|
||||||
(__atomic_test_and_set(pSpin, __ATOMIC_SEQ_CST) ? EBUSY : 0)
|
(__atomic_test_and_set(pSpin, __ATOMIC_SEQ_CST) ? EBUSY : 0)
|
||||||
#ifdef TINY
|
#ifdef TINY
|
||||||
#define pthread_spin_lock(pSpin) __pthread_spin_lock_tiny(pSpin)
|
#define pthread_spin_lock(pSpin) _pthread_spin_lock_tiny(pSpin)
|
||||||
#else
|
#else
|
||||||
#define pthread_spin_lock(pSpin) __pthread_spin_lock_cooperative(pSpin)
|
#define pthread_spin_lock(pSpin) _pthread_spin_lock_cooperative(pSpin)
|
||||||
#endif
|
#endif
|
||||||
#define __pthread_spin_lock_tiny(pSpin) \
|
#define _pthread_spin_lock_tiny(pSpin) \
|
||||||
({ \
|
({ \
|
||||||
while (__atomic_test_and_set(pSpin, __ATOMIC_SEQ_CST)) { \
|
while (__atomic_test_and_set(pSpin, __ATOMIC_SEQ_CST)) { \
|
||||||
__builtin_ia32_pause(); \
|
__builtin_ia32_pause(); \
|
||||||
} \
|
} \
|
||||||
0; \
|
0; \
|
||||||
})
|
})
|
||||||
#define __pthread_spin_lock_cooperative(pSpin) \
|
#define _pthread_spin_lock_cooperative(pSpin) \
|
||||||
({ \
|
({ \
|
||||||
char __x; \
|
char __x; \
|
||||||
volatile int __i; \
|
volatile int __i; \
|
||||||
|
@ -197,59 +207,6 @@ extern const errno_t EBUSY;
|
||||||
})
|
})
|
||||||
#endif /* GCC 4.7+ */
|
#endif /* GCC 4.7+ */
|
||||||
|
|
||||||
#define pthread_mutexattr_init(pAttr) (*(pAttr) = PTHREAD_MUTEX_DEFAULT, 0)
|
|
||||||
#define pthread_mutexattr_destroy(pAttr) (*(pAttr) = 0)
|
|
||||||
#define pthread_mutexattr_gettype(pAttr, pType) (*(pType) = *(pAttr), 0)
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#define pthread_mutex_init(mutex, pAttr) \
|
|
||||||
({ \
|
|
||||||
pthread_mutexattr_t *_pAttr = (pAttr); \
|
|
||||||
*(mutex) = (pthread_mutex_t){ \
|
|
||||||
_pAttr ? *_pAttr : PTHREAD_MUTEX_DEFAULT, \
|
|
||||||
}; \
|
|
||||||
0; \
|
|
||||||
})
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define pthread_condattr_init(pAttr) (*(pAttr) = PTHREAD_PROCESS_DEFAULT, 0)
|
|
||||||
#define pthread_condattr_destroy(pAttr) (*(pAttr) = 0)
|
|
||||||
#define pthread_condattr_getpshared(pAttr, pPshared) (*(pPshared) = *(pAttr), 0)
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#define pthread_cond_init(cond, pAttr) \
|
|
||||||
({ \
|
|
||||||
pthread_condattr_t *_pAttr = (pAttr); \
|
|
||||||
*(cond) = (pthread_cond_t){ \
|
|
||||||
_pAttr ? *_pAttr : PTHREAD_PROCESS_DEFAULT, \
|
|
||||||
}; \
|
|
||||||
0; \
|
|
||||||
})
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define pthread_barrierattr_init(pAttr) (*(pAttr) = PTHREAD_PROCESS_DEFAULT, 0)
|
|
||||||
#define pthread_barrierattr_destroy(pAttr) (*(pAttr) = 0)
|
|
||||||
#define pthread_barrierattr_getpshared(pAttr, pPshared) \
|
|
||||||
(*(pPshared) = *(pAttr), 0)
|
|
||||||
|
|
||||||
#define pthread_rwlockattr_init(pAttr) (*(pAttr) = PTHREAD_PROCESS_DEFAULT, 0)
|
|
||||||
#define pthread_rwlockattr_destroy(pAttr) (*(pAttr) = 0)
|
|
||||||
#define pthread_rwlockattr_getpshared(pAttr, pPshared) \
|
|
||||||
(*(pPshared) = *(pAttr), 0)
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#define pthread_rwlock_init(rwlock, pAttr) \
|
|
||||||
({ \
|
|
||||||
pthread_rwlockattr_t *_pAttr = (pAttr); \
|
|
||||||
*(rwlock) = (pthread_rwlock_t){ \
|
|
||||||
_pAttr ? *_pAttr : PTHREAD_PROCESS_DEFAULT, \
|
|
||||||
}; \
|
|
||||||
0; \
|
|
||||||
})
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int _pthread_mutex_wake(pthread_mutex_t *) hidden;
|
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
#endif /* COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_ */
|
#endif /* COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_ */
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "libc/calls/struct/sched_param.h"
|
#include "libc/calls/struct/sched_param.h"
|
||||||
#include "libc/calls/struct/timespec.h"
|
#include "libc/calls/struct/timespec.h"
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/runtime/stack.h"
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,6 @@
|
||||||
* Destroys pthread attributes.
|
* Destroys pthread attributes.
|
||||||
*/
|
*/
|
||||||
int pthread_attr_destroy(pthread_attr_t *attr) {
|
int pthread_attr_destroy(pthread_attr_t *attr) {
|
||||||
bzero(attr, sizeof(*attr));
|
memset(attr, -1, sizeof(*attr));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,15 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
|
||||||
int pthread_attr_getdetachstate(const pthread_attr_t *a, int *x) {
|
/**
|
||||||
*x = a->detachstate;
|
* Gets thread detachable attribute.
|
||||||
|
*
|
||||||
|
* @param detachstate is set to one of the following
|
||||||
|
* - `PTHREAD_CREATE_JOINABLE` (default)
|
||||||
|
* - `PTHREAD_CREATE_DETACHED`
|
||||||
|
* @return 0 on success, or error on failure
|
||||||
|
*/
|
||||||
|
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) {
|
||||||
|
*detachstate = attr->detachstate;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,13 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
|
||||||
int pthread_attr_getguardsize(const pthread_attr_t *a, size_t *x) {
|
/**
|
||||||
*x = a->guardsize;
|
* Returns size of unmapped pages at bottom of stack.
|
||||||
|
*
|
||||||
|
* @param guardsize will be set to guard size in bytes
|
||||||
|
* @return 0 on success, or errno on error
|
||||||
|
*/
|
||||||
|
int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize) {
|
||||||
|
*guardsize = attr->guardsize;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,22 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/runtime/stack.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns size of thread stack.
|
||||||
|
*
|
||||||
|
* This defaults to GetStackSize().
|
||||||
|
*
|
||||||
|
* @param x will be set to stack size in bytes
|
||||||
|
* @return 0 on success, or errno on error
|
||||||
|
* @see pthread_attr_setstacksize()
|
||||||
|
*/
|
||||||
int pthread_attr_getstacksize(const pthread_attr_t *a, size_t *x) {
|
int pthread_attr_getstacksize(const pthread_attr_t *a, size_t *x) {
|
||||||
*x = a->stacksize;
|
if (a->stacksize) {
|
||||||
|
*x = a->stacksize;
|
||||||
|
} else {
|
||||||
|
*x = GetStackSize();
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,18 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/runtime/stack.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes pthread attributes.
|
* Initializes pthread attributes.
|
||||||
|
*
|
||||||
|
* @return 0 on success, or errno on error
|
||||||
*/
|
*/
|
||||||
int pthread_attr_init(pthread_attr_t *attr) {
|
int pthread_attr_init(pthread_attr_t *attr) {
|
||||||
bzero(attr, sizeof(*attr));
|
*attr = (pthread_attr_t){
|
||||||
|
.detachstate = PTHREAD_CREATE_JOINABLE,
|
||||||
|
.stacksize = GetStackSize(),
|
||||||
|
.guardsize = PAGESIZE,
|
||||||
|
};
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,31 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
|
||||||
int pthread_attr_setdetachstate(pthread_attr_t *a, int x) {
|
/**
|
||||||
a->detachstate = x;
|
* Sets thread detachable attribute, e.g.
|
||||||
return 0;
|
*
|
||||||
|
* pthread_attr_t attr;
|
||||||
|
* pthread_attr_init(&attr);
|
||||||
|
* pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
|
* pthread_create(0, &attr, func, 0);
|
||||||
|
* pthread_attr_destroy(&attr);
|
||||||
|
*
|
||||||
|
* @param detachstate can be one of
|
||||||
|
* - `PTHREAD_CREATE_JOINABLE` (default)
|
||||||
|
* - `PTHREAD_CREATE_DETACHED`
|
||||||
|
* @return 0 on success, or error on failure
|
||||||
|
* @raises EINVAL if `detachstate` is invalid
|
||||||
|
*/
|
||||||
|
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) {
|
||||||
|
switch (detachstate) {
|
||||||
|
case PTHREAD_CREATE_JOINABLE:
|
||||||
|
case PTHREAD_CREATE_DETACHED:
|
||||||
|
attr->detachstate = detachstate;
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,21 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
int pthread_attr_setguardsize(pthread_attr_t *a, size_t x) {
|
/**
|
||||||
a->guardsize = x;
|
* Sets size of unmapped pages at bottom of stack.
|
||||||
|
*
|
||||||
|
* Cosmopolitan Libc stack guards always default to 4096 bytes. Setting
|
||||||
|
* `guardsize` to zero will disable automatic creation of guard pages.
|
||||||
|
* Your `guardsize` will be rounded up to `PAGESIZE`.
|
||||||
|
*
|
||||||
|
* @param guardsize contains guard size in bytes
|
||||||
|
* @return 0 on success, or errno on error
|
||||||
|
*/
|
||||||
|
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) {
|
||||||
|
attr->guardsize = ROUNDUP(guardsize, PAGESIZE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,38 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets size of thread stack.
|
||||||
|
*
|
||||||
|
* Your stack must have at least `PTHREAD_STACK_MIN` bytes, which
|
||||||
|
* Cosmpolitan Libc defines as `GetStackSize()`. It's a link-time
|
||||||
|
* constant used by Actually Portable Executable that's 128 kb by
|
||||||
|
* default. See libc/runtime/stack.h for docs on your stack limit
|
||||||
|
* since the APE ELF phdrs are the one true source of truth here.
|
||||||
|
*
|
||||||
|
* Cosmpolitan Libc runtime magic (e.g. ftrace) and memory safety
|
||||||
|
* (e.g. kprintf) assumes that stack sizes are two-powers and are
|
||||||
|
* aligned to that two-power. Conformance isn't required since we
|
||||||
|
* say caveat emptor to those who don't maintain these invariants
|
||||||
|
*
|
||||||
|
* Unlike pthread_attr_setstack() this function should be used if
|
||||||
|
* you want the Cosmopolitan Libc runtime to allocate a stack for
|
||||||
|
* you. Since the runtime uses mmap(MAP_STACK) to do that, you'll
|
||||||
|
* need to choose a multiple of FRAMESIZE, due to Windows.
|
||||||
|
*
|
||||||
|
* If this function isn't called it'll default to GetStackSize().
|
||||||
|
*
|
||||||
|
* @param x contains stack size in bytes
|
||||||
|
* @return 0 on success, or errno on error
|
||||||
|
* @raise EINVAL if `x` is less than `PTHREAD_STACK_MIN`
|
||||||
|
*/
|
||||||
int pthread_attr_setstacksize(pthread_attr_t *a, size_t x) {
|
int pthread_attr_setstacksize(pthread_attr_t *a, size_t x) {
|
||||||
|
if (x < PTHREAD_STACK_MIN) return EINVAL;
|
||||||
|
if (x & (FRAMESIZE - 1)) return EINVAL;
|
||||||
|
if (x < FRAMESIZE) return EINVAL;
|
||||||
a->stacksize = x;
|
a->stacksize = x;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys barrier.
|
* Destroys barrier.
|
||||||
|
@ -31,6 +32,6 @@ int pthread_barrier_destroy(pthread_barrier_t *barrier) {
|
||||||
assert(!"deadlock");
|
assert(!"deadlock");
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
*barrier = (pthread_barrier_t)PTHREAD_BARRIER_INITIALIZER;
|
memset(barrier, -1, sizeof(*barrier));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,11 +42,7 @@ int pthread_barrier_wait(pthread_barrier_t *barrier) {
|
||||||
atomic_store(&barrier->popped, 1);
|
atomic_store(&barrier->popped, 1);
|
||||||
do {
|
do {
|
||||||
if (IsLinux() || IsOpenbsd()) {
|
if (IsLinux() || IsOpenbsd()) {
|
||||||
if (barrier->attr == PTHREAD_PROCESS_SHARED) {
|
_futex_wake(&barrier->popped, INT_MAX, barrier->pshared);
|
||||||
_futex_wake_public(&barrier->popped, INT_MAX);
|
|
||||||
} else {
|
|
||||||
_futex_wake_private(&barrier->popped, INT_MAX);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
pthread_yield();
|
pthread_yield();
|
||||||
}
|
}
|
||||||
|
@ -59,11 +55,7 @@ int pthread_barrier_wait(pthread_barrier_t *barrier) {
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
if (IsLinux() || IsOpenbsd()) {
|
if (IsLinux() || IsOpenbsd()) {
|
||||||
if (barrier->attr == PTHREAD_PROCESS_SHARED) {
|
_futex_wait(&barrier->popped, 0, barrier->pshared, 0);
|
||||||
_futex_wait_public(&barrier->popped, 0, 0);
|
|
||||||
} else {
|
|
||||||
_futex_wait_private(&barrier->popped, 0, 0);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
pthread_yield();
|
pthread_yield();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,14 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys barrier attributes.
|
* Destroys barrier attributes.
|
||||||
*
|
*
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_barrierattr_destroy)(pthread_barrierattr_t *attr) {
|
int pthread_barrierattr_destroy(pthread_barrierattr_t *attr) {
|
||||||
return pthread_barrierattr_destroy(attr);
|
memset(attr, -1, sizeof(*attr));
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
* - `PTHREAD_PROCESS_PRIVATE`
|
* - `PTHREAD_PROCESS_PRIVATE`
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_barrierattr_getpshared)(const pthread_barrierattr_t *attr,
|
int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr,
|
||||||
int *pshared) {
|
int *pshared) {
|
||||||
return pthread_barrierattr_getpshared(attr, pshared);
|
*pshared = *attr;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
*
|
*
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_barrierattr_init)(pthread_barrierattr_t *attr) {
|
int pthread_barrierattr_init(pthread_barrierattr_t *attr) {
|
||||||
return pthread_barrierattr_init(attr);
|
*attr = PTHREAD_PROCESS_DEFAULT;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,11 +26,7 @@ static dontinline int pthread_cond_signal_impl(pthread_cond_t *cond, int n) {
|
||||||
if (atomic_load_explicit(&cond->waits, memory_order_relaxed)) {
|
if (atomic_load_explicit(&cond->waits, memory_order_relaxed)) {
|
||||||
atomic_fetch_add(&cond->seq, 1);
|
atomic_fetch_add(&cond->seq, 1);
|
||||||
if (IsLinux() || IsOpenbsd()) {
|
if (IsLinux() || IsOpenbsd()) {
|
||||||
if (cond->attr == PTHREAD_PROCESS_SHARED) {
|
_futex_wake(&cond->seq, n, cond->pshared);
|
||||||
_futex_wake_public(&cond->seq, n);
|
|
||||||
} else {
|
|
||||||
_futex_wake_private(&cond->seq, n);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys condition.
|
* Destroys condition.
|
||||||
|
@ -31,6 +32,6 @@ int pthread_cond_destroy(pthread_cond_t *cond) {
|
||||||
assert(!"deadlock");
|
assert(!"deadlock");
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
*cond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
|
memset(cond, -1, sizeof(*cond));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
* @param attr may be null
|
* @param attr may be null
|
||||||
* @return 0 on success, or error number on failure
|
* @return 0 on success, or error number on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_cond_init)(pthread_cond_t *cond, const pthread_condattr_t *attr) {
|
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) {
|
||||||
return pthread_cond_init(cond, attr);
|
*cond = (pthread_cond_t){attr ? *attr : PTHREAD_PROCESS_DEFAULT};
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,14 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys condition attributes.
|
* Destroys condition attributes.
|
||||||
*
|
*
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_condattr_destroy)(pthread_condattr_t *attr) {
|
int pthread_condattr_destroy(pthread_condattr_t *attr) {
|
||||||
return pthread_condattr_destroy(attr);
|
memset(attr, -1, sizeof(*attr));
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
* - `PTHREAD_PROCESS_PRIVATE`
|
* - `PTHREAD_PROCESS_PRIVATE`
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_condattr_getpshared)(const pthread_condattr_t *attr, int *pshared) {
|
int pthread_condattr_getpshared(const pthread_condattr_t *attr, int *pshared) {
|
||||||
return pthread_condattr_getpshared(attr, pshared);
|
*pshared = *attr;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
*
|
*
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_condattr_init)(pthread_condattr_t *attr) {
|
int pthread_condattr_init(pthread_condattr_t *attr) {
|
||||||
return pthread_condattr_init(attr);
|
*attr = PTHREAD_PROCESS_DEFAULT;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys mutex.
|
* Destroys mutex.
|
||||||
|
@ -31,6 +32,6 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex) {
|
||||||
assert(!"deadlock");
|
assert(!"deadlock");
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
*mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
|
memset(mutex, -1, sizeof(*mutex));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
#include "libc/str/str.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes mutex.
|
* Initializes mutex.
|
||||||
|
@ -25,7 +24,11 @@
|
||||||
* @param attr may be null
|
* @param attr may be null
|
||||||
* @return 0 on success, or error number on failure
|
* @return 0 on success, or error number on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_mutex_init)(pthread_mutex_t *mutex,
|
int pthread_mutex_init(pthread_mutex_t *mutex,
|
||||||
const pthread_mutexattr_t *attr) {
|
const pthread_mutexattr_t *attr) {
|
||||||
return pthread_mutex_init(mutex, attr);
|
*mutex = (pthread_mutex_t){
|
||||||
|
attr ? attr->type : PTHREAD_MUTEX_DEFAULT,
|
||||||
|
attr ? attr->pshared : PTHREAD_PROCESS_DEFAULT,
|
||||||
|
};
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ static int pthread_mutex_lock_spin(pthread_mutex_t *mutex, int expect,
|
||||||
tries++;
|
tries++;
|
||||||
} else if (IsLinux() || IsOpenbsd()) {
|
} else if (IsLinux() || IsOpenbsd()) {
|
||||||
atomic_fetch_add(&mutex->waits, 1);
|
atomic_fetch_add(&mutex->waits, 1);
|
||||||
_futex_wait_private(&mutex->lock, expect, &(struct timespec){1});
|
_futex_wait(&mutex->lock, expect, mutex->pshared, &(struct timespec){1});
|
||||||
atomic_fetch_sub(&mutex->waits, 1);
|
atomic_fetch_sub(&mutex->waits, 1);
|
||||||
} else {
|
} else {
|
||||||
pthread_yield();
|
pthread_yield();
|
||||||
|
@ -87,24 +87,24 @@ static int pthread_mutex_lock_spin(pthread_mutex_t *mutex, int expect,
|
||||||
*
|
*
|
||||||
* Microbenchmarks for single-threaded lock + unlock:
|
* Microbenchmarks for single-threaded lock + unlock:
|
||||||
*
|
*
|
||||||
* pthread_spinlock_t : 12c ( 4ns)
|
* pthread_spinlock_t : 12c ( 4ns)
|
||||||
* PTHREAD_MUTEX_NORMAL : 37c ( 12ns)
|
* PTHREAD_MUTEX_NORMAL : 37c ( 12ns)
|
||||||
* PTHREAD_MUTEX_RECURSIVE : 22c ( 7ns)
|
* PTHREAD_MUTEX_RECURSIVE : 22c ( 7ns)
|
||||||
* PTHREAD_MUTEX_ERRORCHECK : 27c ( 9ns)
|
* PTHREAD_MUTEX_ERRORCHECK : 27c ( 9ns)
|
||||||
*
|
*
|
||||||
* Microbenchmarks for multi-threaded lock + unlock:
|
* Microbenchmarks for multi-threaded lock + unlock:
|
||||||
*
|
*
|
||||||
* pthread_spinlock_t : 6,162c (1,990ns)
|
* pthread_spinlock_t : 2,396c (774ns)
|
||||||
* PTHREAD_MUTEX_NORMAL : 780c ( 252ns)
|
* PTHREAD_MUTEX_NORMAL : 535c (173ns)
|
||||||
* PTHREAD_MUTEX_RECURSIVE : 1,047c ( 338ns)
|
* PTHREAD_MUTEX_RECURSIVE : 1,045c (338ns)
|
||||||
* PTHREAD_MUTEX_ERRORCHECK : 1,044c ( 337ns)
|
* PTHREAD_MUTEX_ERRORCHECK : 917c (296ns)
|
||||||
*
|
*
|
||||||
* @return 0 on success, or error number on failure
|
* @return 0 on success, or error number on failure
|
||||||
* @see pthread_spin_lock
|
* @see pthread_spin_lock
|
||||||
*/
|
*/
|
||||||
int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||||
int c, me, owner, tries;
|
int c, me, owner, tries;
|
||||||
switch (mutex->attr) {
|
switch (mutex->type) {
|
||||||
case PTHREAD_MUTEX_NORMAL:
|
case PTHREAD_MUTEX_NORMAL:
|
||||||
// From Futexes Are Tricky Version 1.1 § Mutex, Take 3;
|
// From Futexes Are Tricky Version 1.1 § Mutex, Take 3;
|
||||||
// Ulrich Drepper, Red Hat Incorporated, June 27, 2004.
|
// Ulrich Drepper, Red Hat Incorporated, June 27, 2004.
|
||||||
|
@ -116,7 +116,7 @@ int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||||
c = atomic_exchange_explicit(&mutex->lock, 2, memory_order_acquire);
|
c = atomic_exchange_explicit(&mutex->lock, 2, memory_order_acquire);
|
||||||
}
|
}
|
||||||
while (c) {
|
while (c) {
|
||||||
_futex_wait_private(&mutex->lock, 2, 0);
|
_futex_wait(&mutex->lock, 2, mutex->pshared, 0);
|
||||||
c = atomic_exchange_explicit(&mutex->lock, 2, memory_order_acquire);
|
c = atomic_exchange_explicit(&mutex->lock, 2, memory_order_acquire);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||||
memory_order_relaxed)) {
|
memory_order_relaxed)) {
|
||||||
break;
|
break;
|
||||||
} else if (owner == me) {
|
} else if (owner == me) {
|
||||||
if (mutex->attr != PTHREAD_MUTEX_ERRORCHECK) {
|
if (mutex->type != PTHREAD_MUTEX_ERRORCHECK) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
assert(!"deadlock");
|
assert(!"deadlock");
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
*/
|
*/
|
||||||
int pthread_mutex_trylock(pthread_mutex_t *mutex) {
|
int pthread_mutex_trylock(pthread_mutex_t *mutex) {
|
||||||
int c, me, owner;
|
int c, me, owner;
|
||||||
switch (mutex->attr) {
|
switch (mutex->type) {
|
||||||
case PTHREAD_MUTEX_NORMAL:
|
case PTHREAD_MUTEX_NORMAL:
|
||||||
c = 0;
|
c = 0;
|
||||||
if (atomic_compare_exchange_strong_explicit(&mutex->lock, &c, 1,
|
if (atomic_compare_exchange_strong_explicit(&mutex->lock, &c, 1,
|
||||||
|
@ -52,7 +52,7 @@ int pthread_mutex_trylock(pthread_mutex_t *mutex) {
|
||||||
memory_order_acquire,
|
memory_order_acquire,
|
||||||
memory_order_relaxed)) {
|
memory_order_relaxed)) {
|
||||||
if (owner == me) {
|
if (owner == me) {
|
||||||
if (mutex->attr == PTHREAD_MUTEX_ERRORCHECK) {
|
if (mutex->type == PTHREAD_MUTEX_ERRORCHECK) {
|
||||||
return EBUSY;
|
return EBUSY;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/futex.internal.h"
|
#include "libc/intrin/futex.internal.h"
|
||||||
|
@ -31,14 +32,14 @@
|
||||||
*/
|
*/
|
||||||
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
||||||
int c, me, owner;
|
int c, me, owner;
|
||||||
switch (mutex->attr) {
|
switch (mutex->type) {
|
||||||
case PTHREAD_MUTEX_NORMAL:
|
case PTHREAD_MUTEX_NORMAL:
|
||||||
// From Futexes Are Tricky Version 1.1 § Mutex, Take 3;
|
// From Futexes Are Tricky Version 1.1 § Mutex, Take 3;
|
||||||
// Ulrich Drepper, Red Hat Incorporated, June 27, 2004.
|
// Ulrich Drepper, Red Hat Incorporated, June 27, 2004.
|
||||||
if ((c = atomic_fetch_sub_explicit(&mutex->lock, 1,
|
if ((c = atomic_fetch_sub_explicit(&mutex->lock, 1,
|
||||||
memory_order_release)) != 1) {
|
memory_order_release)) != 1) {
|
||||||
atomic_store_explicit(&mutex->lock, 0, memory_order_release);
|
atomic_store_explicit(&mutex->lock, 0, memory_order_release);
|
||||||
_futex_wake_private(&mutex->lock, 1);
|
_futex_wake(&mutex->lock, 1, mutex->pshared);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
case PTHREAD_MUTEX_ERRORCHECK:
|
case PTHREAD_MUTEX_ERRORCHECK:
|
||||||
|
@ -52,8 +53,9 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
||||||
case PTHREAD_MUTEX_RECURSIVE:
|
case PTHREAD_MUTEX_RECURSIVE:
|
||||||
if (--mutex->reent) return 0;
|
if (--mutex->reent) return 0;
|
||||||
atomic_store_explicit(&mutex->lock, 0, memory_order_relaxed);
|
atomic_store_explicit(&mutex->lock, 0, memory_order_relaxed);
|
||||||
if (atomic_load_explicit(&mutex->waits, memory_order_relaxed) > 0) {
|
if ((IsLinux() || IsOpenbsd()) &&
|
||||||
_pthread_mutex_wake(mutex);
|
atomic_load_explicit(&mutex->waits, memory_order_relaxed) > 0) {
|
||||||
|
return _futex_wake(&mutex->lock, 1, mutex->pshared);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -17,11 +17,13 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys mutex attr.
|
* Destroys mutex attr.
|
||||||
* @return 0 on success, or error number on failure
|
* @return 0 on success, or error number on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_mutexattr_destroy)(pthread_mutexattr_t *attr) {
|
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) {
|
||||||
return pthread_mutexattr_destroy(attr);
|
memset(attr, -1, sizeof(*attr));
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
* - `PTHREAD_PROCESS_PRIVATE`
|
* - `PTHREAD_PROCESS_PRIVATE`
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_mutexattr_getpshared)(const pthread_mutexattr_t *attr,
|
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr,
|
||||||
int *pshared) {
|
int *pshared) {
|
||||||
return pthread_mutexattr_getpshared(attr, pshared);
|
*pshared = attr->pshared;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
* - `PTHREAD_MUTEX_ERRORCHECK`
|
* - `PTHREAD_MUTEX_ERRORCHECK`
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_mutexattr_gettype)(const pthread_mutexattr_t *attr, int *type) {
|
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) {
|
||||||
return pthread_mutexattr_gettype(attr, type);
|
*type = attr->type;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,10 @@
|
||||||
* Initializes mutex attr.
|
* Initializes mutex attr.
|
||||||
* @return 0 on success, or error number on failure
|
* @return 0 on success, or error number on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_mutexattr_init)(pthread_mutexattr_t *attr) {
|
int pthread_mutexattr_init(pthread_mutexattr_t *attr) {
|
||||||
return pthread_mutexattr_init(attr);
|
*attr = (pthread_mutexattr_t){
|
||||||
|
PTHREAD_MUTEX_DEFAULT,
|
||||||
|
PTHREAD_PROCESS_DEFAULT,
|
||||||
|
};
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) {
|
||||||
switch (pshared) {
|
switch (pshared) {
|
||||||
case PTHREAD_PROCESS_SHARED:
|
case PTHREAD_PROCESS_SHARED:
|
||||||
case PTHREAD_PROCESS_PRIVATE:
|
case PTHREAD_PROCESS_PRIVATE:
|
||||||
*attr = pshared;
|
attr->pshared = pshared;
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
|
|
|
@ -35,7 +35,7 @@ int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) {
|
||||||
case PTHREAD_MUTEX_NORMAL:
|
case PTHREAD_MUTEX_NORMAL:
|
||||||
case PTHREAD_MUTEX_RECURSIVE:
|
case PTHREAD_MUTEX_RECURSIVE:
|
||||||
case PTHREAD_MUTEX_ERRORCHECK:
|
case PTHREAD_MUTEX_ERRORCHECK:
|
||||||
*attr = type;
|
attr->type = type;
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys read-write lock.
|
* Destroys read-write lock.
|
||||||
|
@ -31,6 +32,6 @@ int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) {
|
||||||
assert(!"deadlock");
|
assert(!"deadlock");
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
*rwlock = (pthread_rwlock_t)PTHREAD_RWLOCK_INITIALIZER;
|
memset(rwlock, -1, sizeof(*rwlock));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,8 @@
|
||||||
* @param attr may be null
|
* @param attr may be null
|
||||||
* @return 0 on success, or error number on failure
|
* @return 0 on success, or error number on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_rwlock_init)(pthread_rwlock_t *rwlock,
|
int pthread_rwlock_init(pthread_rwlock_t *rwlock,
|
||||||
const pthread_rwlockattr_t *attr) {
|
const pthread_rwlockattr_t *attr) {
|
||||||
return pthread_rwlock_init(rwlock, attr);
|
*rwlock = (pthread_rwlock_t){attr ? *attr : PTHREAD_PROCESS_DEFAULT};
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ static int pthread_rwlock_rdlock_spin(pthread_rwlock_t *rwlock, int expect,
|
||||||
tries++;
|
tries++;
|
||||||
} else if (IsLinux() || IsOpenbsd()) {
|
} else if (IsLinux() || IsOpenbsd()) {
|
||||||
atomic_fetch_add(&rwlock->waits, 1);
|
atomic_fetch_add(&rwlock->waits, 1);
|
||||||
_futex_wait_private(&rwlock->lock, expect, &(struct timespec){1});
|
_futex_wait(&rwlock->lock, expect, rwlock->pshared, &(struct timespec){1});
|
||||||
atomic_fetch_sub(&rwlock->waits, 1);
|
atomic_fetch_sub(&rwlock->waits, 1);
|
||||||
} else {
|
} else {
|
||||||
pthread_yield();
|
pthread_yield();
|
||||||
|
|
|
@ -43,11 +43,7 @@ int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) {
|
||||||
memory_order_acquire,
|
memory_order_acquire,
|
||||||
memory_order_relaxed)) {
|
memory_order_relaxed)) {
|
||||||
if (waits && (IsLinux() || IsOpenbsd())) {
|
if (waits && (IsLinux() || IsOpenbsd())) {
|
||||||
if (rwlock->attr == PTHREAD_PROCESS_SHARED) {
|
_futex_wake(&rwlock->lock, 1, rwlock->pshared);
|
||||||
_futex_wake_public(&rwlock->lock, 1);
|
|
||||||
} else {
|
|
||||||
_futex_wake_private(&rwlock->lock, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ static int pthread_rwlock_wrlock_spin(pthread_rwlock_t *rwlock, int expect,
|
||||||
tries++;
|
tries++;
|
||||||
} else if (IsLinux() || IsOpenbsd()) {
|
} else if (IsLinux() || IsOpenbsd()) {
|
||||||
atomic_fetch_add(&rwlock->waits, 1);
|
atomic_fetch_add(&rwlock->waits, 1);
|
||||||
_futex_wait_private(&rwlock->lock, expect, &(struct timespec){1});
|
_futex_wait(&rwlock->lock, expect, rwlock->pshared, &(struct timespec){1});
|
||||||
atomic_fetch_sub(&rwlock->waits, 1);
|
atomic_fetch_sub(&rwlock->waits, 1);
|
||||||
} else {
|
} else {
|
||||||
pthread_yield();
|
pthread_yield();
|
||||||
|
|
|
@ -17,12 +17,14 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys read-write lock attributes.
|
* Destroys read-write lock attributes.
|
||||||
*
|
*
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_rwlockattr_destroy)(pthread_rwlockattr_t *attr) {
|
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) {
|
||||||
return pthread_rwlockattr_destroy(attr);
|
memset(attr, -1, sizeof(*attr));
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
* - `PTHREAD_PROCESS_PRIVATE`
|
* - `PTHREAD_PROCESS_PRIVATE`
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_rwlockattr_getpshared)(const pthread_rwlockattr_t *attr,
|
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr,
|
||||||
int *pshared) {
|
int *pshared) {
|
||||||
return pthread_rwlockattr_getpshared(attr, pshared);
|
*pshared = *attr;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
*
|
*
|
||||||
* @return 0 on success, or error on failure
|
* @return 0 on success, or error on failure
|
||||||
*/
|
*/
|
||||||
int(pthread_rwlockattr_init)(pthread_rwlockattr_t *attr) {
|
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) {
|
||||||
return pthread_rwlockattr_init(attr);
|
*attr = PTHREAD_PROCESS_DEFAULT;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ void _wait0(const int *ctid) {
|
||||||
if (!(x = atomic_load_explicit(ctid, memory_order_relaxed))) {
|
if (!(x = atomic_load_explicit(ctid, memory_order_relaxed))) {
|
||||||
break;
|
break;
|
||||||
} else if (IsLinux() || IsOpenbsd()) {
|
} else if (IsLinux() || IsOpenbsd()) {
|
||||||
_futex_wait_public(ctid, x, &(struct timespec){2});
|
_futex_wait(ctid, x, PTHREAD_PROCESS_SHARED, &(struct timespec){2});
|
||||||
} else {
|
} else {
|
||||||
pthread_yield();
|
pthread_yield();
|
||||||
}
|
}
|
||||||
|
|
110
libc/stdio/ecvt.c
Normal file
110
libc/stdio/ecvt.c
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ $OpenBSD: ecvt.c,v 1.11 2019/01/25 00:19:25 millert Exp $ │
|
||||||
|
│ │
|
||||||
|
│ Copyright (c) 2002, 2006 Todd C. Miller <millert@openbsd.org> │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and distribute this software for any │
|
||||||
|
│ purpose with or without fee is hereby granted, provided that the above │
|
||||||
|
│ copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES │
|
||||||
|
│ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF │
|
||||||
|
│ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR │
|
||||||
|
│ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES │
|
||||||
|
│ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN │
|
||||||
|
│ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF │
|
||||||
|
│ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
│ │
|
||||||
|
│ Sponsored in part by the Defense Advanced Research Projects │
|
||||||
|
│ Agency (DARPA) and Air Force Research Laboratory, Air Force │
|
||||||
|
│ Materiel Command, USAF, under agreement number F39502-99-1-0512. │
|
||||||
|
│ SUCH DAMAGE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/fmt/fmt.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
#include "third_party/gdtoa/gdtoa.h"
|
||||||
|
|
||||||
|
asm(".ident\t\"\\n\\n\
|
||||||
|
OpenBSD ecvt/gcvt (MIT)\\n\
|
||||||
|
Copyright (c) 2002, 2006, 2010 Todd C. Miller <millert@openbsd.org>\"");
|
||||||
|
asm(".include \"libc/disclaimer.inc\"");
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
static char *
|
||||||
|
__cvt(double value, int ndigit, int *decpt, int *sign, int fmode, int pad)
|
||||||
|
{
|
||||||
|
static char *s;
|
||||||
|
char *p, *rve, c;
|
||||||
|
size_t siz;
|
||||||
|
|
||||||
|
if (ndigit == 0) {
|
||||||
|
*sign = value < 0.0;
|
||||||
|
*decpt = 0;
|
||||||
|
return ("");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(s);
|
||||||
|
s = NULL;
|
||||||
|
|
||||||
|
if (ndigit < 0)
|
||||||
|
siz = -ndigit + 1;
|
||||||
|
else
|
||||||
|
siz = ndigit + 1;
|
||||||
|
|
||||||
|
|
||||||
|
/* __dtoa() doesn't allocate space for 0 so we do it by hand */
|
||||||
|
if (value == 0.0) {
|
||||||
|
*decpt = 1 - fmode; /* 1 for 'e', 0 for 'f' */
|
||||||
|
*sign = 0;
|
||||||
|
if ((rve = s = malloc(siz)) == NULL)
|
||||||
|
return(NULL);
|
||||||
|
*rve++ = '0';
|
||||||
|
*rve = '\0';
|
||||||
|
} else {
|
||||||
|
p = dtoa(value, fmode + 2, ndigit, decpt, sign, &rve);
|
||||||
|
if (p == NULL)
|
||||||
|
return (NULL);
|
||||||
|
if (*decpt == 9999) {
|
||||||
|
/* Infinity or Nan, convert to inf or nan like printf */
|
||||||
|
*decpt = 0;
|
||||||
|
c = *p;
|
||||||
|
freedtoa(p);
|
||||||
|
return(c == 'I' ? "inf" : "nan");
|
||||||
|
}
|
||||||
|
/* Make a local copy and adjust rve to be in terms of s */
|
||||||
|
if (pad && fmode)
|
||||||
|
siz += *decpt;
|
||||||
|
if ((s = malloc(siz)) == NULL) {
|
||||||
|
freedtoa(p);
|
||||||
|
return(NULL);
|
||||||
|
}
|
||||||
|
(void) strlcpy(s, p, siz);
|
||||||
|
rve = s + (rve - p);
|
||||||
|
freedtoa(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add trailing zeros */
|
||||||
|
if (pad) {
|
||||||
|
siz -= rve - s;
|
||||||
|
while (--siz)
|
||||||
|
*rve++ = '0';
|
||||||
|
*rve = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
ecvt(double value, int ndigit, int *decpt, int *sign)
|
||||||
|
{
|
||||||
|
return(__cvt(value, ndigit, decpt, sign, 0, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
fcvt(double value, int ndigit, int *decpt, int *sign)
|
||||||
|
{
|
||||||
|
return(__cvt(value, ndigit, decpt, sign, 1, 1));
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/intrin/pthread.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/stdio/internal.h"
|
#include "libc/stdio/internal.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
|
@ -37,6 +38,7 @@ FILE *fdopen(int fd, const char *mode) {
|
||||||
f->fd = fd;
|
f->fd = fd;
|
||||||
f->bufmode = ischardev(fd) ? _IOLBF : _IOFBF;
|
f->bufmode = ischardev(fd) ? _IOLBF : _IOFBF;
|
||||||
f->iomode = fopenflags(mode);
|
f->iomode = fopenflags(mode);
|
||||||
|
f->lock.type = PTHREAD_MUTEX_RECURSIVE;
|
||||||
f->size = BUFSIZ;
|
f->size = BUFSIZ;
|
||||||
if ((f->buf = malloc(f->size))) {
|
if ((f->buf = malloc(f->size))) {
|
||||||
if ((f->iomode & O_ACCMODE) != O_RDONLY) {
|
if ((f->iomode & O_ACCMODE) != O_RDONLY) {
|
||||||
|
|
|
@ -16,14 +16,14 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/mem/arraylist.internal.h"
|
|
||||||
#include "libc/intrin/bits.h"
|
|
||||||
#include "libc/intrin/pushpop.h"
|
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/bits.h"
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/intrin/pushpop.h"
|
||||||
#include "libc/intrin/spinlock.h"
|
#include "libc/intrin/spinlock.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
#include "libc/mem/arraylist.internal.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/stdio/fflush.internal.h"
|
#include "libc/stdio/fflush.internal.h"
|
||||||
|
@ -34,12 +34,10 @@
|
||||||
static pthread_mutex_t __fflush_lock_obj;
|
static pthread_mutex_t __fflush_lock_obj;
|
||||||
|
|
||||||
void(__fflush_lock)(void) {
|
void(__fflush_lock)(void) {
|
||||||
__fflush_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
|
|
||||||
pthread_mutex_lock(&__fflush_lock_obj);
|
pthread_mutex_lock(&__fflush_lock_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void(__fflush_unlock)(void) {
|
void(__fflush_unlock)(void) {
|
||||||
__fflush_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
|
|
||||||
pthread_mutex_unlock(&__fflush_lock_obj);
|
pthread_mutex_unlock(&__fflush_lock_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,5 @@
|
||||||
* Acquires reentrant lock on stdio object, blocking if needed.
|
* Acquires reentrant lock on stdio object, blocking if needed.
|
||||||
*/
|
*/
|
||||||
void(flockfile)(FILE *f) {
|
void(flockfile)(FILE *f) {
|
||||||
f->lock.attr = PTHREAD_MUTEX_RECURSIVE;
|
|
||||||
pthread_mutex_lock(&f->lock);
|
pthread_mutex_lock(&f->lock);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ FILE *fmemopen(void *buf, size_t size, const char *mode) {
|
||||||
f->end = size;
|
f->end = size;
|
||||||
f->size = size;
|
f->size = size;
|
||||||
f->iomode = fopenflags(mode);
|
f->iomode = fopenflags(mode);
|
||||||
|
f->lock.type = PTHREAD_MUTEX_RECURSIVE;
|
||||||
if (f->iomode & O_APPEND) {
|
if (f->iomode & O_APPEND) {
|
||||||
if ((p = memchr(buf, '\0', size))) {
|
if ((p = memchr(buf, '\0', size))) {
|
||||||
f->beg = p - (char *)buf;
|
f->beg = p - (char *)buf;
|
||||||
|
|
|
@ -22,6 +22,5 @@
|
||||||
* Releases lock on stdio object.
|
* Releases lock on stdio object.
|
||||||
*/
|
*/
|
||||||
void(funlockfile)(FILE *f) {
|
void(funlockfile)(FILE *f) {
|
||||||
f->lock.attr = PTHREAD_MUTEX_RECURSIVE;
|
|
||||||
pthread_mutex_unlock(&f->lock);
|
pthread_mutex_unlock(&f->lock);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,130 @@
|
||||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
|
||||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│
|
||||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
│ $OpenBSD: ecvt.c,v 1.11 2019/01/25 00:19:25 millert Exp $ │
|
||||||
│ │
|
│ │
|
||||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
│ Copyright (c) 2002, 2006, 2010 Todd C. Miller <millert@openbsd.org> │
|
||||||
│ any purpose with or without fee is hereby granted, provided that the │
|
|
||||||
│ above copyright notice and this permission notice appear in all copies. │
|
|
||||||
│ │
|
│ │
|
||||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
│ Permission to use, copy, modify, and distribute this software for any │
|
||||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
│ purpose with or without fee is hereby granted, provided that the above │
|
||||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
│ copyright notice and this permission notice appear in all copies. │
|
||||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
│ │
|
||||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES │
|
||||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
│ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF │
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES │
|
||||||
|
│ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN │
|
||||||
|
│ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF │
|
||||||
|
│ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
│ │
|
||||||
|
│ Sponsored in part by the Defense Advanced Research Projects │
|
||||||
|
│ Agency (DARPA) and Air Force Research Laboratory, Air Force │
|
||||||
|
│ Materiel Command, USAF, under agreement number F39502-99-1-0512. │
|
||||||
|
│ SUCH DAMAGE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/fmt/fmt.h"
|
#include "libc/fmt/fmt.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/mem/mem.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/str/unicode.h"
|
||||||
|
#include "third_party/gdtoa/gdtoa.h"
|
||||||
|
|
||||||
char *gcvt(double x, int n, char *b) {
|
asm(".ident\t\"\\n\\n\
|
||||||
sprintf(b, "%.*g", n, x);
|
OpenBSD ecvt/gcvt (MIT)\\n\
|
||||||
return b;
|
Copyright (c) 2002, 2006, 2010 Todd C. Miller <millert@openbsd.org>\"");
|
||||||
|
asm(".include \"libc/disclaimer.inc\"");
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
#define DEFPREC 6
|
||||||
|
|
||||||
|
char *
|
||||||
|
gcvt(double value, int ndigit, char *buf)
|
||||||
|
{
|
||||||
|
char *digits, *dst, *src;
|
||||||
|
int i, decpt, sign;
|
||||||
|
struct lconv *lconv;
|
||||||
|
|
||||||
|
lconv = localeconv();
|
||||||
|
if (ndigit <= 0) {
|
||||||
|
/* Match printf(3) behavior. */
|
||||||
|
ndigit = ndigit ? DEFPREC : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
digits = dtoa(value, 2, ndigit, &decpt, &sign, NULL);
|
||||||
|
if (digits == NULL)
|
||||||
|
return (NULL);
|
||||||
|
if (decpt == 9999) {
|
||||||
|
/*
|
||||||
|
* Infinity or NaN, convert to inf or nan with sign.
|
||||||
|
* We can't infer buffer size based on ndigit.
|
||||||
|
* We have to assume it is at least 5 chars.
|
||||||
|
*/
|
||||||
|
snprintf(buf, 5, "%s%s", sign ? "-" : "",
|
||||||
|
*digits == 'I' ? "inf" : "nan");
|
||||||
|
freedtoa(digits);
|
||||||
|
return (buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
dst = buf;
|
||||||
|
if (sign)
|
||||||
|
*dst++ = '-';
|
||||||
|
|
||||||
|
/* Match printf(3) behavior for exponential vs. regular fomatting. */
|
||||||
|
if (decpt <= -4 || decpt > ndigit) {
|
||||||
|
/* exponential format (e.g. 1.2345e+13) */
|
||||||
|
if (--decpt < 0) {
|
||||||
|
sign = 1;
|
||||||
|
decpt = -decpt;
|
||||||
|
} else
|
||||||
|
sign = 0;
|
||||||
|
src = digits;
|
||||||
|
*dst++ = *src++;
|
||||||
|
if (*src != '\0') {
|
||||||
|
*dst++ = *lconv->decimal_point;
|
||||||
|
do {
|
||||||
|
*dst++ = *src++;
|
||||||
|
} while (*src != '\0');
|
||||||
|
}
|
||||||
|
*dst++ = 'e';
|
||||||
|
if (sign)
|
||||||
|
*dst++ = '-';
|
||||||
|
else
|
||||||
|
*dst++ = '+';
|
||||||
|
if (decpt < 10) {
|
||||||
|
*dst++ = '0';
|
||||||
|
*dst++ = '0' + decpt;
|
||||||
|
*dst = '\0';
|
||||||
|
} else {
|
||||||
|
/* XXX - optimize */
|
||||||
|
for (sign = decpt, i = 0; (sign /= 10) != 0; i++)
|
||||||
|
continue;
|
||||||
|
dst[i + 1] = '\0';
|
||||||
|
while (decpt != 0) {
|
||||||
|
dst[i--] = '0' + decpt % 10;
|
||||||
|
decpt /= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* standard format */
|
||||||
|
for (i = 0, src = digits; i < decpt; i++) {
|
||||||
|
if (*src != '\0')
|
||||||
|
*dst++ = *src++;
|
||||||
|
else
|
||||||
|
*dst++ = '0';
|
||||||
|
}
|
||||||
|
if (*src != '\0') {
|
||||||
|
if (src == digits)
|
||||||
|
*dst++ = '0'; /* zero before decimal point */
|
||||||
|
*dst++ = *lconv->decimal_point;
|
||||||
|
while (decpt < 0) {
|
||||||
|
*dst++ = '0';
|
||||||
|
decpt++;
|
||||||
|
}
|
||||||
|
for (i = decpt; digits[i] != '\0'; i++) {
|
||||||
|
*dst++ = digits[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*dst = '\0';
|
||||||
|
}
|
||||||
|
freedtoa(digits);
|
||||||
|
return (buf);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/sysv/consts/fileno.h"
|
#include "libc/sysv/consts/fileno.h"
|
||||||
|
#include "libc/intrin/pthread.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
.init.start 400,_init_stderr
|
.init.start 400,_init_stderr
|
||||||
|
@ -33,5 +34,6 @@
|
||||||
ezlea __stderr_buf,cx
|
ezlea __stderr_buf,cx
|
||||||
mov %rcx,0x18(%rax) #→ f.buf
|
mov %rcx,0x18(%rax) #→ f.buf
|
||||||
movl $BUFSIZ,0x20(%rax) #→ f.size
|
movl $BUFSIZ,0x20(%rax) #→ f.size
|
||||||
|
movb $PTHREAD_MUTEX_RECURSIVE,0x38(%rax) #→ f.lock.attr
|
||||||
mov %rax,stderr(%rip)
|
mov %rax,stderr(%rip)
|
||||||
.init.end 400,_init_stderr,globl,hidden
|
.init.end 400,_init_stderr,globl,hidden
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/sysv/consts/fileno.h"
|
#include "libc/sysv/consts/fileno.h"
|
||||||
|
#include "libc/intrin/pthread.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
.init.start 400,_init_stdin
|
.init.start 400,_init_stdin
|
||||||
|
@ -29,5 +30,6 @@
|
||||||
ezlea __stdin_buf,cx
|
ezlea __stdin_buf,cx
|
||||||
mov %rcx,0x18(%rax) #→ f.buf
|
mov %rcx,0x18(%rax) #→ f.buf
|
||||||
movl $BUFSIZ,0x20(%rax) #→ f.size
|
movl $BUFSIZ,0x20(%rax) #→ f.size
|
||||||
|
movb $PTHREAD_MUTEX_RECURSIVE,0x38(%rax) #→ f.lock.attr
|
||||||
mov %rax,stdin(%rip)
|
mov %rax,stdin(%rip)
|
||||||
.init.end 400,_init_stdin,globl,hidden
|
.init.end 400,_init_stdin,globl,hidden
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/sysv/consts/fileno.h"
|
#include "libc/sysv/consts/fileno.h"
|
||||||
|
#include "libc/intrin/pthread.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
.init.start 400,_init_stdout
|
.init.start 400,_init_stdout
|
||||||
|
@ -31,5 +32,6 @@
|
||||||
ezlea __stdout_buf,cx
|
ezlea __stdout_buf,cx
|
||||||
mov %rcx,0x18(%rax) #→ f.buf
|
mov %rcx,0x18(%rax) #→ f.buf
|
||||||
movl $BUFSIZ,0x20(%rax) #→ f.size
|
movl $BUFSIZ,0x20(%rax) #→ f.size
|
||||||
|
movb $PTHREAD_MUTEX_RECURSIVE,0x38(%rax) #→ f.lock.attr
|
||||||
mov %rax,stdout(%rip)
|
mov %rax,stdout(%rip)
|
||||||
.init.end 400,_init_stdout,globl,hidden
|
.init.end 400,_init_stdout,globl,hidden
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
|
#ifndef COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
|
||||||
#define COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
|
#define COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
|
||||||
|
#include "libc/intrin/pthread.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/thread/spawn.h"
|
#include "libc/thread/spawn.h"
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
@ -10,7 +11,7 @@ COSMOPOLITAN_C_START_
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum PosixThreadStatus {
|
enum PosixThreadStatus {
|
||||||
kPosixThreadStarted,
|
kPosixThreadJoinable,
|
||||||
kPosixThreadDetached,
|
kPosixThreadDetached,
|
||||||
kPosixThreadTerminated,
|
kPosixThreadTerminated,
|
||||||
kPosixThreadZombie,
|
kPosixThreadZombie,
|
||||||
|
@ -19,16 +20,19 @@ enum PosixThreadStatus {
|
||||||
struct PosixThread {
|
struct PosixThread {
|
||||||
struct spawn spawn;
|
struct spawn spawn;
|
||||||
void *(*start_routine)(void *);
|
void *(*start_routine)(void *);
|
||||||
void *arg;
|
void *arg; // start_routine's parameter
|
||||||
void *rc;
|
void *rc; // start_routine's return value
|
||||||
int tid;
|
|
||||||
_Atomic(enum PosixThreadStatus) status;
|
_Atomic(enum PosixThreadStatus) status;
|
||||||
jmp_buf exiter;
|
jmp_buf exiter;
|
||||||
|
size_t stacksize;
|
||||||
|
pthread_attr_t attr;
|
||||||
};
|
};
|
||||||
|
|
||||||
void pthread_zombies_add(struct PosixThread *);
|
void pthread_free(struct PosixThread *) hidden;
|
||||||
void pthread_zombies_decimate(void);
|
void pthread_wait(struct PosixThread *) hidden;
|
||||||
void pthread_zombies_harvest(void);
|
void pthread_zombies_add(struct PosixThread *) hidden;
|
||||||
|
void pthread_zombies_decimate(void) hidden;
|
||||||
|
void pthread_zombies_harvest(void) hidden;
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
38
libc/thread/pthread_attr_getstack.c
Normal file
38
libc/thread/pthread_attr_getstack.c
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/runtime/stack.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns configuration for thread stack.
|
||||||
|
*
|
||||||
|
* This is a getter for a configuration attribute. By default, zeros are
|
||||||
|
* returned. If pthread_attr_setstack() was called earlier, then this'll
|
||||||
|
* return those earlier supplied values.
|
||||||
|
*
|
||||||
|
* @param stackaddr will be set to stack address in bytes
|
||||||
|
* @return 0 on success, or errno on error
|
||||||
|
* @see pthread_attr_setstacksize()
|
||||||
|
*/
|
||||||
|
int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr,
|
||||||
|
size_t *stacksize) {
|
||||||
|
*stackaddr = attr->stackaddr;
|
||||||
|
*stacksize = attr->stacksize;
|
||||||
|
return 0;
|
||||||
|
}
|
106
libc/thread/pthread_attr_setstack.c
Normal file
106
libc/thread/pthread_attr_setstack.c
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/asan.internal.h"
|
||||||
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
|
#include "libc/runtime/stack.h"
|
||||||
|
#include "libc/sysv/consts/map.h"
|
||||||
|
#include "libc/sysv/consts/prot.h"
|
||||||
|
|
||||||
|
#define MAP_ANON_OPENBSD 0x1000
|
||||||
|
#define MAP_STACK_OPENBSD 0x4000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures custom allocated stack for thread, e.g.
|
||||||
|
*
|
||||||
|
* pthread_t id;
|
||||||
|
* pthread_attr_t attr;
|
||||||
|
* pthread_attr_init(&attr);
|
||||||
|
* pthread_attr_setstack(&attr, gc(malloc(GetStackSize())),
|
||||||
|
* GetStackSize());
|
||||||
|
* pthread_create(&id, &attr, func, 0);
|
||||||
|
* pthread_attr_destroy(&attr);
|
||||||
|
* pthread_join(id, 0);
|
||||||
|
*
|
||||||
|
* Your stack must have at least `PTHREAD_STACK_MIN` bytes, which
|
||||||
|
* Cosmpolitan Libc defines as `GetStackSize()`. It's a link-time
|
||||||
|
* constant used by Actually Portable Executable that's 128 kb by
|
||||||
|
* default. See libc/runtime/stack.h for docs on your stack limit
|
||||||
|
* since the APE ELF phdrs are the one true source of truth here.
|
||||||
|
*
|
||||||
|
* Cosmpolitan Libc runtime magic (e.g. ftrace) and memory safety
|
||||||
|
* (e.g. kprintf) assumes that stack sizes are two-powers and are
|
||||||
|
* aligned to that two-power. Conformance isn't required since we
|
||||||
|
* say caveat emptor to those who don't maintain these invariants
|
||||||
|
*
|
||||||
|
* Unlike pthread_attr_setstacksize(), this function permits just
|
||||||
|
* about any parameters and will change the values and allocation
|
||||||
|
* as needed to conform to the mandatory requirements of the host
|
||||||
|
* operating system.
|
||||||
|
*
|
||||||
|
* @param stackaddr is address of stack allocated by caller, and
|
||||||
|
* may be NULL in which case default behavior is restored
|
||||||
|
* @param stacksize is size of caller allocated stack
|
||||||
|
* @return 0 on success, or errno on error
|
||||||
|
* @raise EINVAL if parameters were unacceptable
|
||||||
|
* @see pthread_attr_setstacksize()
|
||||||
|
*/
|
||||||
|
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr,
|
||||||
|
size_t stacksize) {
|
||||||
|
if (!stackaddr) {
|
||||||
|
attr->stackaddr = 0;
|
||||||
|
attr->stacksize = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (stacksize < PTHREAD_STACK_MIN ||
|
||||||
|
(IsAsan() && !__asan_is_valid(stackaddr, stacksize))) {
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
if (IsOpenbsd()) {
|
||||||
|
// OpenBSD: Only permits RSP to occupy memory that's been explicitly
|
||||||
|
// defined as stack memory. We need to squeeze the provided interval
|
||||||
|
// in order to successfully call mmap(), which will return EINVAL if
|
||||||
|
// these calculations should overflow.
|
||||||
|
size_t n;
|
||||||
|
int e, rc;
|
||||||
|
uintptr_t x, y;
|
||||||
|
n = stacksize;
|
||||||
|
x = (uintptr_t)stackaddr;
|
||||||
|
y = ROUNDUP(x, PAGESIZE);
|
||||||
|
n -= y - x;
|
||||||
|
n = ROUNDDOWN(n, PAGESIZE);
|
||||||
|
stackaddr = (void *)y;
|
||||||
|
stacksize = n;
|
||||||
|
e = errno;
|
||||||
|
if (__sys_mmap(stackaddr, stacksize, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_PRIVATE | MAP_ANON_OPENBSD | MAP_STACK_OPENBSD, -1, 0,
|
||||||
|
0) == MAP_FAILED) {
|
||||||
|
rc = errno;
|
||||||
|
errno = e;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attr->stackaddr = stackaddr;
|
||||||
|
attr->stacksize = stacksize;
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -78,11 +78,7 @@ int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||||
tsp = &rel;
|
tsp = &rel;
|
||||||
}
|
}
|
||||||
if (IsLinux() || IsOpenbsd()) {
|
if (IsLinux() || IsOpenbsd()) {
|
||||||
if (cond->attr == PTHREAD_PROCESS_SHARED) {
|
_futex_wait(&cond->seq, seq, cond->pshared, tsp);
|
||||||
_futex_wait_public(&cond->seq, seq, tsp);
|
|
||||||
} else {
|
|
||||||
_futex_wait_private(&cond->seq, seq, tsp);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
sched_yield();
|
sched_yield();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,19 +16,37 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/asan.internal.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/intrin/wait0.internal.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/nexgen32e/gettls.h"
|
#include "libc/nexgen32e/gettls.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/sysv/consts/clone.h"
|
||||||
|
#include "libc/sysv/consts/map.h"
|
||||||
|
#include "libc/sysv/consts/prot.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
|
#include "libc/thread/spawn.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
|
void pthread_wait(struct PosixThread *pt) {
|
||||||
|
_wait0(pt->spawn.ctid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pthread_free(struct PosixThread *pt) {
|
||||||
|
free(pt->spawn.tls);
|
||||||
|
if (pt->stacksize) {
|
||||||
|
munmap(&pt->spawn.stk, pt->stacksize);
|
||||||
|
}
|
||||||
|
free(pt);
|
||||||
|
}
|
||||||
|
|
||||||
static int PosixThread(void *arg, int tid) {
|
static int PosixThread(void *arg, int tid) {
|
||||||
struct PosixThread *pt = arg;
|
struct PosixThread *pt = arg;
|
||||||
enum PosixThreadStatus status;
|
enum PosixThreadStatus status;
|
||||||
pt->tid = tid;
|
|
||||||
if (!setjmp(pt->exiter)) {
|
if (!setjmp(pt->exiter)) {
|
||||||
((cthread_t)__get_tls())->pthread = pt;
|
((cthread_t)__get_tls())->pthread = pt;
|
||||||
pt->rc = pt->start_routine(pt->arg);
|
pt->rc = pt->start_routine(pt->arg);
|
||||||
|
@ -66,27 +84,128 @@ static int PosixThread(void *arg, int tid) {
|
||||||
* │ bsdthread_create │ │ thr_new │ └──────────────┘
|
* │ bsdthread_create │ │ thr_new │ └──────────────┘
|
||||||
* └──────────────────┘ └─────────┘
|
* └──────────────────┘ └─────────┘
|
||||||
*
|
*
|
||||||
|
* @param thread if non-null is used to output the thread id
|
||||||
|
* upon successful completion
|
||||||
|
* @param attr points to launch configuration, or may be null
|
||||||
|
* to use sensible defaults; it must be initialized using
|
||||||
|
* pthread_attr_init()
|
||||||
|
* @param start_routine is your thread's callback function
|
||||||
|
* @param arg is an arbitrary value passed to `start_routine`
|
||||||
* @return 0 on success, or errno on error
|
* @return 0 on success, or errno on error
|
||||||
|
* @raise EAGAIN if resources to create thread weren't available
|
||||||
|
* @raise EINVAL if `attr` was supplied and had unnaceptable data
|
||||||
|
* @raise EPERM if scheduling policy was requested and user account
|
||||||
|
* isn't authorized to use it
|
||||||
*/
|
*/
|
||||||
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||||
void *(*start_routine)(void *), void *arg) {
|
void *(*start_routine)(void *), void *arg) {
|
||||||
int e, rc;
|
int rc, e = errno;
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
e = errno;
|
pthread_attr_t default_attr;
|
||||||
pthread_zombies_decimate();
|
pthread_zombies_decimate();
|
||||||
if ((pt = calloc(1, sizeof(struct PosixThread)))) {
|
|
||||||
pt->start_routine = start_routine;
|
// default attributes
|
||||||
pt->arg = arg;
|
if (!attr) {
|
||||||
if (!_spawn(PosixThread, pt, &pt->spawn)) {
|
pthread_attr_init(&default_attr);
|
||||||
*thread = pt;
|
attr = &default_attr;
|
||||||
rc = 0;
|
|
||||||
} else {
|
|
||||||
free(pt);
|
|
||||||
rc = errno;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rc = errno;
|
|
||||||
}
|
}
|
||||||
errno = e;
|
|
||||||
return rc;
|
// create posix thread object
|
||||||
|
if (!(pt = calloc(1, sizeof(struct PosixThread)))) {
|
||||||
|
errno = e;
|
||||||
|
return EAGAIN;
|
||||||
|
}
|
||||||
|
pt->start_routine = start_routine;
|
||||||
|
pt->arg = arg;
|
||||||
|
|
||||||
|
// create thread local storage memory
|
||||||
|
if (!(pt->spawn.tls = _mktls(&pt->spawn.tib))) {
|
||||||
|
free(pt);
|
||||||
|
errno = e;
|
||||||
|
return EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// child thread id is also a condition variable
|
||||||
|
pt->spawn.ctid = (int *)(pt->spawn.tib + 0x38);
|
||||||
|
|
||||||
|
// create stack
|
||||||
|
if (attr && attr->stackaddr) {
|
||||||
|
// caller is responsible for creating stacks
|
||||||
|
pt->spawn.stk = attr->stackaddr;
|
||||||
|
} else {
|
||||||
|
// cosmo posix threads is managing the stack
|
||||||
|
pt->spawn.stk = mmap(0, attr->stacksize, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_STACK | MAP_ANONYMOUS, -1, 0);
|
||||||
|
if (pt->spawn.stk != MAP_FAILED) {
|
||||||
|
pt->stacksize = attr->stacksize;
|
||||||
|
} else {
|
||||||
|
rc = errno;
|
||||||
|
pthread_free(pt);
|
||||||
|
errno = e;
|
||||||
|
if (rc == EINVAL || rc == EOVERFLOW) {
|
||||||
|
return EINVAL;
|
||||||
|
} else {
|
||||||
|
return EAGAIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// mmap(MAP_STACK) creates a 4096 guard by default
|
||||||
|
if (attr->guardsize != PAGESIZE) {
|
||||||
|
// user requested special guard size
|
||||||
|
if (attr->guardsize) {
|
||||||
|
rc = mprotect(pt->spawn.stk, attr->guardsize, PROT_NONE);
|
||||||
|
} else {
|
||||||
|
rc = mprotect(pt->spawn.stk, PAGESIZE, PROT_READ | PROT_WRITE);
|
||||||
|
}
|
||||||
|
if (rc) {
|
||||||
|
notpossible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (IsAsan()) {
|
||||||
|
if (attr->guardsize) {
|
||||||
|
__asan_poison(pt->spawn.stk, attr->guardsize, kAsanStackOverflow);
|
||||||
|
}
|
||||||
|
__asan_poison(
|
||||||
|
pt->spawn.stk + attr->stacksize - 16 /* openbsd:stackbound */, 16,
|
||||||
|
kAsanStackOverflow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the attributes for descriptive purposes
|
||||||
|
pt->attr = *attr;
|
||||||
|
|
||||||
|
// set initial status
|
||||||
|
switch (attr->detachstate) {
|
||||||
|
case PTHREAD_CREATE_JOINABLE:
|
||||||
|
pt->status = kPosixThreadJoinable;
|
||||||
|
break;
|
||||||
|
case PTHREAD_CREATE_DETACHED:
|
||||||
|
pt->status = kPosixThreadDetached;
|
||||||
|
pthread_zombies_add(pt);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pthread_free(pt);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// launch PosixThread(pt) in new thread
|
||||||
|
if (clone(PosixThread, pt->spawn.stk,
|
||||||
|
attr->stacksize - 16 /* openbsd:stackbound */,
|
||||||
|
CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
|
||||||
|
CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID |
|
||||||
|
CLONE_CHILD_CLEARTID,
|
||||||
|
pt, &pt->spawn.ptid, pt->spawn.tib, pt->spawn.ctid) == -1) {
|
||||||
|
rc = errno;
|
||||||
|
pthread_free(pt);
|
||||||
|
errno = e;
|
||||||
|
if (rc == EINVAL) {
|
||||||
|
return EINVAL;
|
||||||
|
} else {
|
||||||
|
return EAGAIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread) {
|
||||||
|
*thread = pt;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,10 +36,10 @@ int pthread_detach(pthread_t thread) {
|
||||||
if (status == kPosixThreadDetached || status == kPosixThreadZombie) {
|
if (status == kPosixThreadDetached || status == kPosixThreadZombie) {
|
||||||
break;
|
break;
|
||||||
} else if (status == kPosixThreadTerminated) {
|
} else if (status == kPosixThreadTerminated) {
|
||||||
_join(&pt->spawn);
|
pthread_wait(pt);
|
||||||
free(pt);
|
pthread_free(pt);
|
||||||
break;
|
break;
|
||||||
} else if (status == kPosixThreadStarted &&
|
} else if (status == kPosixThreadJoinable &&
|
||||||
atomic_compare_exchange_weak_explicit(
|
atomic_compare_exchange_weak_explicit(
|
||||||
&pt->status, &status, kPosixThreadDetached,
|
&pt->status, &status, kPosixThreadDetached,
|
||||||
memory_order_acquire, memory_order_relaxed)) {
|
memory_order_acquire, memory_order_relaxed)) {
|
||||||
|
|
|
@ -27,5 +27,5 @@
|
||||||
int pthread_equal(pthread_t t1, pthread_t t2) {
|
int pthread_equal(pthread_t t1, pthread_t t2) {
|
||||||
struct PosixThread *a = t1;
|
struct PosixThread *a = t1;
|
||||||
struct PosixThread *b = t2;
|
struct PosixThread *b = t2;
|
||||||
return a->tid == b->tid;
|
return a->spawn.ptid == b->spawn.ptid;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,16 +16,13 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/dce.h"
|
|
||||||
#include "libc/intrin/atomic.h"
|
|
||||||
#include "libc/intrin/futex.internal.h"
|
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/thread/posixthread.internal.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
int _pthread_mutex_wake(pthread_mutex_t *mutex) {
|
int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr) {
|
||||||
if ((IsLinux() || IsOpenbsd()) &&
|
struct PosixThread *pt = thread;
|
||||||
atomic_load_explicit(&mutex->waits, memory_order_relaxed)) {
|
memcpy(attr, &pt->attr, sizeof(pt->attr));
|
||||||
return _futex_wake_private(&mutex->lock, 1);
|
return 0;
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -24,5 +24,5 @@
|
||||||
*/
|
*/
|
||||||
int64_t pthread_getunique_np(pthread_t thread) {
|
int64_t pthread_getunique_np(pthread_t thread) {
|
||||||
struct PosixThread *pt = thread;
|
struct PosixThread *pt = thread;
|
||||||
return pt->tid;
|
return pt->spawn.ptid;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,10 +36,10 @@ int pthread_join(pthread_t thread, void **value_ptr) {
|
||||||
assert(!"badjoin");
|
assert(!"badjoin");
|
||||||
return EDEADLK;
|
return EDEADLK;
|
||||||
}
|
}
|
||||||
_join(&pt->spawn);
|
pthread_wait(pt);
|
||||||
if (value_ptr) {
|
if (value_ptr) {
|
||||||
*value_ptr = pt->rc;
|
*value_ptr = pt->rc;
|
||||||
}
|
}
|
||||||
free(pt);
|
pthread_free(pt);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,12 +22,13 @@
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/futex.internal.h"
|
#include "libc/intrin/futex.internal.h"
|
||||||
|
#include "libc/intrin/pthread.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
int cthread_memory_wait32(int* addr, int val, const struct timespec* timeout) {
|
int cthread_memory_wait32(int* addr, int val, const struct timespec* timeout) {
|
||||||
size_t size;
|
size_t size;
|
||||||
if (IsLinux() || IsOpenbsd()) {
|
if (IsLinux() || IsOpenbsd()) {
|
||||||
return _futex_wait_public(addr, val, timeout);
|
return _futex_wait(addr, val, PTHREAD_PROCESS_SHARED, timeout);
|
||||||
} else {
|
} else {
|
||||||
return sched_yield();
|
return sched_yield();
|
||||||
}
|
}
|
||||||
|
@ -35,7 +36,7 @@ int cthread_memory_wait32(int* addr, int val, const struct timespec* timeout) {
|
||||||
|
|
||||||
int cthread_memory_wake32(int* addr, int n) {
|
int cthread_memory_wake32(int* addr, int n) {
|
||||||
if (IsLinux() || IsOpenbsd()) {
|
if (IsLinux() || IsOpenbsd()) {
|
||||||
return _futex_wake_public(addr, n);
|
return _futex_wake(addr, n, PTHREAD_PROCESS_SHARED);
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,9 +44,9 @@ void pthread_zombies_add(struct PosixThread *pt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pthread_zombies_destroy(struct Zombie *z) {
|
static void pthread_zombies_collect(struct Zombie *z) {
|
||||||
_join(&z->pt->spawn);
|
pthread_wait(z->pt);
|
||||||
free(z->pt);
|
pthread_free(z->pt);
|
||||||
free(z);
|
free(z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ void pthread_zombies_decimate(void) {
|
||||||
while ((z = atomic_load(&pthread_zombies)) &&
|
while ((z = atomic_load(&pthread_zombies)) &&
|
||||||
atomic_load(&z->pt->status) == kPosixThreadZombie) {
|
atomic_load(&z->pt->status) == kPosixThreadZombie) {
|
||||||
if (atomic_compare_exchange_strong(&pthread_zombies, &z, z->next)) {
|
if (atomic_compare_exchange_strong(&pthread_zombies, &z, z->next)) {
|
||||||
pthread_zombies_destroy(z);
|
pthread_zombies_collect(z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ void pthread_zombies_harvest(void) {
|
||||||
struct Zombie *z;
|
struct Zombie *z;
|
||||||
while ((z = atomic_load(&pthread_zombies))) {
|
while ((z = atomic_load(&pthread_zombies))) {
|
||||||
if (atomic_compare_exchange_weak(&pthread_zombies, &z, z->next)) {
|
if (atomic_compare_exchange_weak(&pthread_zombies, &z, z->next)) {
|
||||||
pthread_zombies_destroy(z);
|
pthread_zombies_collect(z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ TEST(qsort, test) {
|
||||||
free(M);
|
free(M);
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCH(qsort, equivalence_random) {
|
TEST(qsort, equivalence_random) {
|
||||||
size_t i;
|
size_t i;
|
||||||
size_t n = 1000;
|
size_t n = 1000;
|
||||||
long *a = gc(malloc(n * sizeof(long)));
|
long *a = gc(malloc(n * sizeof(long)));
|
||||||
|
@ -68,7 +68,7 @@ BENCH(qsort, equivalence_random) {
|
||||||
ASSERT_EQ(0, memcmp(b, c, n * sizeof(long)));
|
ASSERT_EQ(0, memcmp(b, c, n * sizeof(long)));
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCH(qsort, equivalence_reverse) {
|
TEST(qsort, equivalence_reverse) {
|
||||||
size_t i;
|
size_t i;
|
||||||
size_t n = 1000;
|
size_t n = 1000;
|
||||||
long *a = gc(malloc(n * sizeof(long)));
|
long *a = gc(malloc(n * sizeof(long)));
|
||||||
|
|
|
@ -16,24 +16,96 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/errno.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/testlib/ezbench.h"
|
#include "libc/testlib/ezbench.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
|
|
||||||
TEST(l64a, test) {
|
TEST(l64a, test) {
|
||||||
EXPECT_STREQ("", l64a(0));
|
EXPECT_STREQ("", l64a(0));
|
||||||
EXPECT_STREQ("zzzzz1", l64a(-1));
|
|
||||||
EXPECT_STREQ("zzzzz/", l64a(0x7fffffff));
|
EXPECT_STREQ("zzzzz/", l64a(0x7fffffff));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(a64l, test) {
|
TEST(a64l, test) {
|
||||||
EXPECT_EQ(0, a64l(""));
|
EXPECT_EQ(0, a64l(""));
|
||||||
EXPECT_EQ(-1, a64l("zzzzz1"));
|
|
||||||
EXPECT_EQ(0x7fffffff, a64l("zzzzz/"));
|
EXPECT_EQ(0x7fffffff, a64l("zzzzz/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dontinline long openbsd_a64l(const char *s) {
|
||||||
|
long value, digit, shift;
|
||||||
|
int i;
|
||||||
|
if (s == NULL) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return (-1L);
|
||||||
|
}
|
||||||
|
value = 0;
|
||||||
|
shift = 0;
|
||||||
|
for (i = 0; *s && i < 6; i++, s++) {
|
||||||
|
if (*s >= '.' && *s <= '/')
|
||||||
|
digit = *s - '.';
|
||||||
|
else if (*s >= '0' && *s <= '9')
|
||||||
|
digit = *s - '0' + 2;
|
||||||
|
else if (*s >= 'A' && *s <= 'Z')
|
||||||
|
digit = *s - 'A' + 12;
|
||||||
|
else if (*s >= 'a' && *s <= 'z')
|
||||||
|
digit = *s - 'a' + 38;
|
||||||
|
else {
|
||||||
|
errno = EINVAL;
|
||||||
|
return (-1L);
|
||||||
|
}
|
||||||
|
value |= digit << shift;
|
||||||
|
shift += 6;
|
||||||
|
}
|
||||||
|
return (value);
|
||||||
|
}
|
||||||
|
|
||||||
|
dontinline char *openbsd_l64a(long value) {
|
||||||
|
static char buf[8];
|
||||||
|
char *s = buf;
|
||||||
|
int digit;
|
||||||
|
int i;
|
||||||
|
if (value < 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
for (i = 0; value != 0 && i < 6; i++) {
|
||||||
|
digit = value & 0x3f;
|
||||||
|
if (digit < 2)
|
||||||
|
*s = digit + '.';
|
||||||
|
else if (digit < 12)
|
||||||
|
*s = digit + '0' - 2;
|
||||||
|
else if (digit < 38)
|
||||||
|
*s = digit + 'A' - 12;
|
||||||
|
else
|
||||||
|
*s = digit + 'a' - 38;
|
||||||
|
value >>= 6;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
*s = '\0';
|
||||||
|
return (buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(openbsd, consistency) {
|
||||||
|
long i;
|
||||||
|
for (i = 0; i < 512; ++i) {
|
||||||
|
ASSERT_STREQ(openbsd_l64a(i), l64a(i));
|
||||||
|
ASSERT_EQ(openbsd_a64l(openbsd_l64a(i)), a64l(l64a(i)));
|
||||||
|
}
|
||||||
|
for (i = INT_MAX; i > INT_MAX - 100; --i) {
|
||||||
|
ASSERT_STREQ(openbsd_l64a(i), l64a(i));
|
||||||
|
ASSERT_EQ(openbsd_a64l(openbsd_l64a(i)), a64l(l64a(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BENCH(a64l, bench) {
|
BENCH(a64l, bench) {
|
||||||
|
EZBENCH2("l64a", donothing, l64a(INT_MAX));
|
||||||
EZBENCH2(
|
EZBENCH2(
|
||||||
"a64l", donothing,
|
"a64l", donothing,
|
||||||
a64l("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"));
|
a64l("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"));
|
||||||
|
EZBENCH2("openbsd_l64a", donothing, openbsd_l64a(INT_MAX));
|
||||||
|
EZBENCH2(
|
||||||
|
"openbsd_a64l", donothing,
|
||||||
|
openbsd_a64l(
|
||||||
|
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,24 +16,85 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/intrin/pthread.h"
|
#include "libc/intrin/pthread.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
|
#include "libc/runtime/stack.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
pthread_t thread;
|
static void *Increment(void *arg) {
|
||||||
|
ASSERT_EQ(gettid(), pthread_getthreadid_np());
|
||||||
static void *ReturnArg(void *arg) {
|
return (void *)((uintptr_t)arg + 1);
|
||||||
return arg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(pthread_create, testCreateReturnJoin) {
|
TEST(pthread_create, testCreateReturnJoin) {
|
||||||
void *exitcode;
|
void *rc;
|
||||||
ASSERT_EQ(0, pthread_create(&thread, 0, ReturnArg, ReturnArg));
|
pthread_t id;
|
||||||
ASSERT_EQ(0, pthread_join(thread, &exitcode));
|
ASSERT_EQ(0, pthread_create(&id, 0, Increment, (void *)1));
|
||||||
ASSERT_EQ(ReturnArg, exitcode);
|
ASSERT_EQ(0, pthread_join(id, &rc));
|
||||||
|
ASSERT_EQ((void *)2, rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *IncExit(void *arg) {
|
||||||
|
pthread_exit((void *)((uintptr_t)arg + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pthread_create, testCreateExitJoin) {
|
||||||
|
void *rc;
|
||||||
|
pthread_t id;
|
||||||
|
ASSERT_EQ(0, pthread_create(&id, 0, IncExit, (void *)2));
|
||||||
|
ASSERT_EQ(0, pthread_join(id, &rc));
|
||||||
|
ASSERT_EQ((void *)3, rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(pthread_detach, testCreateReturn) {
|
TEST(pthread_detach, testCreateReturn) {
|
||||||
ASSERT_EQ(0, pthread_create(&thread, 0, ReturnArg, ReturnArg));
|
pthread_t id;
|
||||||
ASSERT_EQ(0, pthread_detach(thread));
|
ASSERT_EQ(0, pthread_create(&id, 0, Increment, 0));
|
||||||
|
ASSERT_EQ(0, pthread_detach(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pthread_detach, testDetachUponCreation) {
|
||||||
|
pthread_attr_t attr;
|
||||||
|
ASSERT_EQ(0, pthread_attr_init(&attr));
|
||||||
|
ASSERT_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
|
||||||
|
ASSERT_EQ(0, pthread_create(0, &attr, Increment, 0));
|
||||||
|
ASSERT_EQ(0, pthread_attr_destroy(&attr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *CheckStack(void *arg) {
|
||||||
|
char buf[1024 * 1024];
|
||||||
|
CheckLargeStackAllocation(buf, 1024 * 1024);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pthread_detach, testBigStack) {
|
||||||
|
pthread_t id;
|
||||||
|
pthread_attr_t attr;
|
||||||
|
ASSERT_EQ(0, pthread_attr_init(&attr));
|
||||||
|
ASSERT_EQ(0, pthread_attr_setstacksize(&attr, 2 * 1024 * 1024));
|
||||||
|
ASSERT_EQ(0, pthread_create(&id, &attr, CheckStack, 0));
|
||||||
|
ASSERT_EQ(0, pthread_attr_destroy(&attr));
|
||||||
|
ASSERT_EQ(0, pthread_join(id, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pthread_detach, testCustomStack_withReallySmallSize) {
|
||||||
|
char *stk;
|
||||||
|
size_t siz;
|
||||||
|
pthread_t id;
|
||||||
|
pthread_attr_t attr;
|
||||||
|
siz = PTHREAD_STACK_MIN;
|
||||||
|
stk = malloc(siz);
|
||||||
|
ASSERT_EQ(0, pthread_attr_init(&attr));
|
||||||
|
ASSERT_EQ(0, pthread_attr_setstack(&attr, stk, siz));
|
||||||
|
ASSERT_EQ(0, pthread_create(&id, &attr, Increment, 0));
|
||||||
|
ASSERT_EQ(0, pthread_attr_destroy(&attr));
|
||||||
|
ASSERT_EQ(0, pthread_join(id, 0));
|
||||||
|
// we still own the stack memory
|
||||||
|
ASSERT_EQ(0, pthread_attr_init(&attr));
|
||||||
|
ASSERT_EQ(0, pthread_attr_setstack(&attr, stk, siz));
|
||||||
|
ASSERT_EQ(0, pthread_create(&id, &attr, Increment, 0));
|
||||||
|
ASSERT_EQ(0, pthread_attr_destroy(&attr));
|
||||||
|
ASSERT_EQ(0, pthread_join(id, 0));
|
||||||
|
free(stk);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,10 @@ o/$(MODE)/test/libc/thread/%.com.dbg: \
|
||||||
$(APE_NO_MODIFY_SELF)
|
$(APE_NO_MODIFY_SELF)
|
||||||
@$(APELINK)
|
@$(APELINK)
|
||||||
|
|
||||||
|
o/$(MODE)/test/libc/thread/pthread_create_test.o: \
|
||||||
|
private OVERRIDE_CPPFLAGS += \
|
||||||
|
-DSTACK_FRAME_UNLIMITED
|
||||||
|
|
||||||
.PHONY: o/$(MODE)/test/libc/thread
|
.PHONY: o/$(MODE)/test/libc/thread
|
||||||
o/$(MODE)/test/libc/thread: \
|
o/$(MODE)/test/libc/thread: \
|
||||||
$(TEST_LIBC_THREAD_BINS) \
|
$(TEST_LIBC_THREAD_BINS) \
|
||||||
|
|
Loading…
Add table
Reference in a new issue