Perform more low-level code cleanup

This commit is contained in:
Justine Tunney 2022-09-09 04:07:08 -07:00
parent c32e2d4486
commit 2d17ab016c
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
42 changed files with 977 additions and 265 deletions

View file

@ -2,7 +2,6 @@
#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)
COSMOPOLITAN_C_START_
@ -54,13 +53,16 @@ enum PosixThreadStatus {
};
struct PosixThread {
struct spawn spawn;
void *(*start_routine)(void *);
void *arg; // start_routine's parameter
void *rc; // start_routine's return value
bool ownstack;
int tid;
int *ctid;
char *tls;
char *tib;
_Atomic(enum PosixThreadStatus) status;
jmp_buf exiter;
size_t stacksize;
pthread_attr_t attr;
};

View file

@ -1,38 +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 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pthread.h"
#include "libc/runtime/stack.h"
/**
* Returns configuration for thread stack.
*
* This is a getter for a configuration attribute. By default, zeros are
* returned. If pthread_attr_setstack() was called earlier, then this'll
* return those earlier supplied values.
*
* @param stackaddr will be set to stack address in bytes
* @return 0 on success, or errno on error
* @see pthread_attr_setstacksize()
*/
int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr,
size_t *stacksize) {
*stackaddr = attr->stackaddr;
*stacksize = attr->stacksize;
return 0;
}

View file

@ -1,112 +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 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;
* char *stk = _mapstack();
* pthread_attr_init(&attr);
* pthread_attr_setstack(&attr, stk, GetStackSize());
* pthread_create(&id, &attr, func, 0);
* pthread_attr_destroy(&attr);
* pthread_join(id, 0);
* _freestack(stk);
*
* 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
* please consider using _mapstack() which always does it perfect
* or use `mmap(0, GetStackSize() << 1, ...)` for a bigger stack.
*
* 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 even if it doesn't meet the stricter needs of
* Cosmopolitan Libc userspace libraries. For example with malloc
* allocations, things like page size alignment, shall be handled
* automatically for compatibility with existing codebases.
*
* @param stackaddr is address of stack allocated by caller, and
* may be NULL in which case default behavior is restored
* @param stacksize is size of caller allocated stack
* @return 0 on success, or errno on error
* @raise EINVAL if parameters were unacceptable
* @see pthread_attr_setstacksize()
*/
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr,
size_t stacksize) {
if (!stackaddr) {
attr->stackaddr = 0;
attr->stacksize = 0;
return 0;
}
if (stacksize < PTHREAD_STACK_MIN ||
(IsAsan() && !__asan_is_valid(stackaddr, stacksize))) {
return EINVAL;
}
if (IsOpenbsd()) {
// OpenBSD: Only permits RSP to occupy memory that's been explicitly
// defined as stack memory. We need to squeeze the provided interval
// in order to successfully call mmap(), which will return EINVAL if
// these calculations should overflow.
size_t n;
int e, rc;
uintptr_t x, y;
n = stacksize;
x = (uintptr_t)stackaddr;
y = ROUNDUP(x, PAGESIZE);
n -= y - x;
n = ROUNDDOWN(n, PAGESIZE);
stackaddr = (void *)y;
stacksize = n;
e = errno;
if (__sys_mmap(stackaddr, stacksize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON_OPENBSD | MAP_STACK_OPENBSD, -1, 0,
0) == MAP_FAILED) {
rc = errno;
errno = e;
return rc;
}
}
attr->stackaddr = stackaddr;
attr->stacksize = stacksize;
return 0;
}

View file

@ -18,17 +18,22 @@
*/
#include "libc/assert.h"
#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/atomic.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/pthread.h"
#include "libc/intrin/wait0.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/gc.internal.h"
#include "libc/nexgen32e/gettls.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/sysv/consts/clone.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
@ -37,14 +42,19 @@
#include "libc/thread/spawn.h"
#include "libc/thread/thread.h"
#define MAP_ANON_OPENBSD 0x1000
#define MAP_STACK_OPENBSD 0x4000
void _pthread_wait(struct PosixThread *pt) {
_wait0(pt->spawn.ctid);
_wait0(pt->ctid);
}
void _pthread_free(struct PosixThread *pt) {
free(pt->spawn.tls);
if (pt->stacksize) {
munmap(&pt->spawn.stk, pt->stacksize);
free(pt->tls);
if (pt->ownstack && //
pt->attr.stackaddr && //
pt->attr.stackaddr != MAP_FAILED) {
munmap(&pt->attr.stackaddr, pt->attr.stacksize);
}
free(pt);
}
@ -71,6 +81,36 @@ static int PosixThread(void *arg, int tid) {
return 0;
}
static int FixupCustomStackOnOpenbsd(pthread_attr_t *attr) {
// 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 = attr->stacksize;
x = (uintptr_t)attr->stackaddr;
y = ROUNDUP(x, PAGESIZE);
n -= y - x;
n = ROUNDDOWN(n, PAGESIZE);
e = errno;
if (__sys_mmap((void *)y, n, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON_OPENBSD | MAP_STACK_OPENBSD, -1, 0,
0) != MAP_FAILED) {
attr->stackaddr = (void *)y;
attr->stacksize = n;
return 0;
} else {
rc = errno;
errno = e;
if (rc == EOVERFLOW) {
rc = EINVAL;
}
return rc;
}
}
/**
* Creates thread, e.g.
*
@ -124,16 +164,9 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg) {
int rc, e = errno;
struct PosixThread *pt;
pthread_attr_t default_attr;
TlsIsRequired();
_pthread_zombies_decimate();
// default attributes
if (!attr) {
pthread_attr_init(&default_attr);
attr = &default_attr;
}
// create posix thread object
if (!(pt = calloc(1, sizeof(struct PosixThread)))) {
errno = e;
@ -143,26 +176,48 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
pt->arg = arg;
// create thread local storage memory
if (!(pt->spawn.tls = _mktls(&pt->spawn.tib))) {
if (!(pt->tls = _mktls(&pt->tib))) {
free(pt);
errno = e;
return EAGAIN;
}
// child thread id is also a condition variable
pt->spawn.ctid = (int *)(pt->spawn.tib + 0x38);
pt->ctid = (int *)(pt->tib + 0x38);
// create stack
if (attr && attr->stackaddr) {
// caller is responsible for creating stacks
pt->spawn.stk = attr->stackaddr;
// setup attributes
if (attr) {
pt->attr = *attr;
attr = 0;
} 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 {
pthread_attr_init(&pt->attr);
}
// setup stack
if (pt->attr.stackaddr) {
// caller supplied their own stack
// assume they know what they're doing as much as possible
if (IsOpenbsd()) {
if ((rc = FixupCustomStackOnOpenbsd(&pt->attr))) {
_pthread_free(pt);
return rc;
}
}
} else {
// cosmo is managing the stack
// 1. in mono repo optimize for tiniest stack possible
// 2. in public world optimize to *work* regardless of memory
pt->ownstack = true;
pt->attr.stacksize = MAX(pt->attr.stacksize, GetStackSize());
pt->attr.stacksize = roundup2pow(pt->attr.stacksize);
pt->attr.guardsize = ROUNDUP(pt->attr.guardsize, PAGESIZE);
if (pt->attr.guardsize + PAGESIZE >= pt->attr.stacksize) {
_pthread_free(pt);
return EINVAL;
}
pt->attr.stackaddr = mmap(0, pt->attr.stacksize, PROT_READ | PROT_WRITE,
MAP_STACK | MAP_ANONYMOUS, -1, 0);
if (pt->attr.stackaddr == MAP_FAILED) {
rc = errno;
_pthread_free(pt);
errno = e;
@ -173,41 +228,31 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
}
}
// 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);
if (pt->attr.guardsize != PAGESIZE) {
if (pt->attr.guardsize) {
// user requested special guard size
rc = mprotect(pt->attr.stackaddr, pt->attr.guardsize, PROT_NONE);
} else {
rc = mprotect(pt->spawn.stk, PAGESIZE, PROT_READ | PROT_WRITE);
// user explicitly disabled guard page
rc = mprotect(pt->attr.stackaddr, PAGESIZE, PROT_READ | PROT_WRITE);
}
if (rc) {
notpossible;
}
}
if (IsAsan()) {
if (attr->guardsize) {
__asan_poison(pt->spawn.stk, attr->guardsize, kAsanStackOverflow);
if (pt->attr.guardsize) {
__asan_poison(pt->attr.stackaddr, pt->attr.guardsize,
kAsanStackOverflow);
}
__asan_poison(
pt->spawn.stk + attr->stacksize - 16 /* openbsd:stackbound */, 16,
kAsanStackOverflow);
__asan_poison((char *)pt->attr.stackaddr + pt->attr.stacksize -
16 /* openbsd:stackbound */,
16, kAsanStackOverflow);
}
}
// we only need to save this to support pthread_getattr_np()
pt->attr = *attr;
// if attr->stackaddr == 0,
// the stack is managed by cosmo,
// pt->spawn.stk is from a successful mmap,
// and so pt->attr.stackaddr = pt->spawn.stk
pt->attr.stackaddr = pt->spawn.stk;
// if attr->stackaddr != 0,
// then stack is not managed by cosmo
// pt->attr.stackaddr = pt->spawn.stk = attr->stackaddr
// so the above line is a no-op.
// set initial status
switch (attr->detachstate) {
switch (pt->attr.detachstate) {
case PTHREAD_CREATE_JOINABLE:
pt->status = kPosixThreadJoinable;
break;
@ -221,20 +266,16 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
}
// launch PosixThread(pt) in new thread
if (clone(PosixThread, pt->spawn.stk,
attr->stacksize - 16 /* openbsd:stackbound */,
if (clone(PosixThread, pt->attr.stackaddr,
pt->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) {
pt, &pt->tid, pt->tib, pt->ctid) == -1) {
rc = errno;
_pthread_free(pt);
errno = e;
if (rc == EINVAL) {
return EINVAL;
} else {
return EAGAIN;
}
return rc;
}
if (thread) {

View file

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

View file

@ -50,7 +50,7 @@ int pthread_getname_np(pthread_t thread, char *name, size_t size) {
if (!size) return 0;
bzero(name, size);
tid = ((struct PosixThread *)thread)->spawn.ptid;
tid = ((struct PosixThread *)thread)->tid;
if (IsLinux()) {
// TASK_COMM_LEN is 16 on Linux so we're just being paranoid.
@ -99,7 +99,7 @@ int pthread_getname_np(pthread_t thread, char *name, size_t size) {
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
: "1"(324 /* _lwp_getname */), "D"(tid), "S"(name),
"d"(size - 1)
: "rcx", "r11", "memory");
: "rcx", "r8", "r9", "r10", "r11", "memory");
if (!cf) {
// if size + our nul + kernel's nul is the buffer size, then we
// can't say with absolute confidence truncation didn't happen.

View file

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

View file

@ -56,7 +56,7 @@ int pthread_setname_np(pthread_t thread, const char *name) {
char path[128], *p;
int e, fd, rc, tid, len;
tid = ((struct PosixThread *)thread)->spawn.ptid;
tid = ((struct PosixThread *)thread)->tid;
len = strlen(name);
if (IsLinux()) {
@ -100,7 +100,7 @@ int pthread_setname_np(pthread_t thread, const char *name) {
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
: "1"(323 /* thr_set_name */), "D"(tid), "S"(name)
: "rcx", "r11", "memory");
: "rcx", "r8", "r9", "r10", "r11", "memory");
return !cf ? 0 : ax;
} else if (IsNetbsd()) {
@ -109,7 +109,7 @@ int pthread_setname_np(pthread_t thread, const char *name) {
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
: "1"(323 /* _lwp_setname */), "D"(tid), "S"(name)
: "rcx", "r11", "memory");
: "rcx", "r8", "r9", "r10", "r11", "memory");
return !cf ? 0 : ax;
} else {

View file

@ -6,7 +6,7 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct FtraceTls { /* 16 */
struct Ftrace { /* 16 */
bool once; /* 0 */
bool noreentry; /* 1 */
int skew; /* 4 */
@ -15,7 +15,7 @@ struct FtraceTls { /* 16 */
struct cthread_descriptor_t {
struct cthread_descriptor_t *self; /* 0x00 */
struct FtraceTls ftrace; /* 0x08 */
struct Ftrace ftrace; /* 0x08 */
void *garbages; /* 0x18 */
locale_t locale; /* 0x20 */
pthread_t pthread; /* 0x28 */