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