2022-06-13 02:33:42 +00:00
|
|
|
/*-*- 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│
|
2022-06-09 03:01:28 +00:00
|
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
|
|
│ 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. │
|
|
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
2022-09-08 09:33:01 +00:00
|
|
|
#include "libc/assert.h"
|
2022-09-07 12:23:44 +00:00
|
|
|
#include "libc/calls/calls.h"
|
2022-09-09 18:30:33 +00:00
|
|
|
#include "libc/calls/sched-sysv.internal.h"
|
2022-09-11 18:02:07 +00:00
|
|
|
#include "libc/calls/struct/sigaltstack.h"
|
2022-09-09 11:07:08 +00:00
|
|
|
#include "libc/calls/syscall-sysv.internal.h"
|
|
|
|
#include "libc/dce.h"
|
2022-06-13 02:33:42 +00:00
|
|
|
#include "libc/errno.h"
|
2022-09-07 12:23:44 +00:00
|
|
|
#include "libc/intrin/asan.internal.h"
|
2022-09-05 15:26:03 +00:00
|
|
|
#include "libc/intrin/atomic.h"
|
2022-09-09 11:07:08 +00:00
|
|
|
#include "libc/intrin/bits.h"
|
2022-09-11 18:02:07 +00:00
|
|
|
#include "libc/intrin/kprintf.h"
|
2022-09-08 04:13:50 +00:00
|
|
|
#include "libc/intrin/weaken.h"
|
2022-09-11 18:02:07 +00:00
|
|
|
#include "libc/log/internal.h"
|
2022-09-09 11:07:08 +00:00
|
|
|
#include "libc/macros.internal.h"
|
2022-09-05 15:26:03 +00:00
|
|
|
#include "libc/mem/mem.h"
|
2022-09-08 09:33:01 +00:00
|
|
|
#include "libc/nexgen32e/gc.internal.h"
|
2022-09-13 06:10:38 +00:00
|
|
|
#include "libc/runtime/clone.internal.h"
|
2022-09-05 15:26:03 +00:00
|
|
|
#include "libc/runtime/runtime.h"
|
2022-09-09 11:07:08 +00:00
|
|
|
#include "libc/runtime/stack.h"
|
2022-09-07 12:23:44 +00:00
|
|
|
#include "libc/sysv/consts/clone.h"
|
|
|
|
#include "libc/sysv/consts/map.h"
|
|
|
|
#include "libc/sysv/consts/prot.h"
|
2022-09-11 18:02:07 +00:00
|
|
|
#include "libc/sysv/consts/ss.h"
|
2022-09-09 18:30:33 +00:00
|
|
|
#include "libc/sysv/errfuns.h"
|
2022-09-05 15:26:03 +00:00
|
|
|
#include "libc/thread/posixthread.internal.h"
|
2022-09-07 12:23:44 +00:00
|
|
|
#include "libc/thread/spawn.h"
|
2022-09-05 15:26:03 +00:00
|
|
|
#include "libc/thread/thread.h"
|
2022-09-10 09:56:25 +00:00
|
|
|
#include "libc/thread/tls.h"
|
2022-09-11 18:02:07 +00:00
|
|
|
#include "libc/thread/wait0.internal.h"
|
|
|
|
|
|
|
|
STATIC_YOINK("nsync_mu_lock");
|
|
|
|
STATIC_YOINK("nsync_mu_unlock");
|
2022-09-05 15:26:03 +00:00
|
|
|
|
2022-09-09 11:07:08 +00:00
|
|
|
#define MAP_ANON_OPENBSD 0x1000
|
|
|
|
#define MAP_STACK_OPENBSD 0x4000
|
|
|
|
|
2022-09-08 04:13:50 +00:00
|
|
|
void _pthread_wait(struct PosixThread *pt) {
|
2022-09-13 21:57:38 +00:00
|
|
|
_wait0(&pt->tib->tib_tid);
|
2022-09-07 12:23:44 +00:00
|
|
|
}
|
|
|
|
|
2022-09-08 04:13:50 +00:00
|
|
|
void _pthread_free(struct PosixThread *pt) {
|
2022-09-09 11:07:08 +00:00
|
|
|
free(pt->tls);
|
|
|
|
if (pt->ownstack && //
|
|
|
|
pt->attr.stackaddr && //
|
|
|
|
pt->attr.stackaddr != MAP_FAILED) {
|
2022-09-11 18:02:07 +00:00
|
|
|
if (munmap(pt->attr.stackaddr, pt->attr.stacksize)) {
|
|
|
|
notpossible;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pt->altstack) {
|
|
|
|
free(pt->altstack);
|
2022-09-07 12:23:44 +00:00
|
|
|
}
|
|
|
|
free(pt);
|
|
|
|
}
|
|
|
|
|
2022-09-05 15:26:03 +00:00
|
|
|
static int PosixThread(void *arg, int tid) {
|
|
|
|
struct PosixThread *pt = arg;
|
|
|
|
enum PosixThreadStatus status;
|
2022-09-11 18:02:07 +00:00
|
|
|
struct sigaltstack ss;
|
|
|
|
if (pt->altstack) {
|
|
|
|
ss.ss_flags = 0;
|
|
|
|
ss.ss_size = SIGSTKSZ;
|
|
|
|
ss.ss_sp = pt->altstack;
|
|
|
|
if (sigaltstack(&ss, 0)) {
|
|
|
|
notpossible;
|
|
|
|
}
|
|
|
|
}
|
2022-09-09 18:30:33 +00:00
|
|
|
if (pt->attr.inheritsched == PTHREAD_EXPLICIT_SCHED) {
|
|
|
|
_pthread_reschedule(pt);
|
|
|
|
}
|
2022-09-05 15:26:03 +00:00
|
|
|
if (!setjmp(pt->exiter)) {
|
2022-09-10 09:56:25 +00:00
|
|
|
__get_tls()->tib_pthread = (pthread_t)pt;
|
2022-09-05 15:26:03 +00:00
|
|
|
pt->rc = pt->start_routine(pt->arg);
|
|
|
|
}
|
2022-09-13 06:10:38 +00:00
|
|
|
if (_weaken(_pthread_key_destruct)) {
|
|
|
|
_weaken(_pthread_key_destruct)(0);
|
2022-09-08 04:13:50 +00:00
|
|
|
}
|
2022-09-10 09:56:25 +00:00
|
|
|
_pthread_ungarbage();
|
2022-09-06 04:43:49 +00:00
|
|
|
if (atomic_load_explicit(&pt->status, memory_order_acquire) ==
|
2022-09-05 15:26:03 +00:00
|
|
|
kPosixThreadDetached) {
|
|
|
|
atomic_store_explicit(&pt->status, kPosixThreadZombie,
|
2022-09-06 04:43:49 +00:00
|
|
|
memory_order_release);
|
2022-09-05 15:26:03 +00:00
|
|
|
} else {
|
|
|
|
atomic_store_explicit(&pt->status, kPosixThreadTerminated,
|
2022-09-06 04:43:49 +00:00
|
|
|
memory_order_release);
|
2022-09-05 15:26:03 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2022-06-09 03:01:28 +00:00
|
|
|
|
2022-09-09 11:07:08 +00:00
|
|
|
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,
|
2022-09-09 13:19:05 +00:00
|
|
|
MAP_PRIVATE | MAP_FIXED | MAP_ANON_OPENBSD | MAP_STACK_OPENBSD,
|
|
|
|
-1, 0, 0) == (void *)y) {
|
2022-09-09 11:07:08 +00:00
|
|
|
attr->stackaddr = (void *)y;
|
|
|
|
attr->stacksize = n;
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
rc = errno;
|
|
|
|
errno = e;
|
|
|
|
if (rc == EOVERFLOW) {
|
|
|
|
rc = EINVAL;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-13 02:33:42 +00:00
|
|
|
/**
|
2022-09-08 18:54:56 +00:00
|
|
|
* Creates thread, e.g.
|
|
|
|
*
|
|
|
|
* void *worker(void *arg) {
|
|
|
|
* fputs(arg, stdout);
|
|
|
|
* return "there\n";
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* int main() {
|
|
|
|
* void *result;
|
|
|
|
* pthread_t id;
|
|
|
|
* pthread_create(&id, 0, worker, "hi ");
|
|
|
|
* pthread_join(id, &result);
|
|
|
|
* fputs(result, stdout);
|
|
|
|
* }
|
2022-09-05 15:26:03 +00:00
|
|
|
*
|
2022-09-06 04:43:49 +00:00
|
|
|
* Here's the OSI model of threads in Cosmopolitan:
|
|
|
|
*
|
|
|
|
* ┌──────────────────┐
|
|
|
|
* │ pthread_create() │ - Standard
|
|
|
|
* └─────────┬────────┘ Abstraction
|
|
|
|
* ┌─────────┴────────┐
|
|
|
|
* │ clone() │ - Polyfill
|
|
|
|
* └─────────┬────────┘
|
2022-09-08 04:13:50 +00:00
|
|
|
* ┌────────┬──┴┬─┬─┬─────────┐ - Kernel
|
|
|
|
* ┌─────┴─────┐ │ │ │┌┴──────┐ │ Interfaces
|
|
|
|
* │ sys_clone │ │ │ ││ tfork │ ┌┴─────────────┐
|
|
|
|
* └───────────┘ │ │ │└───────┘ │ CreateThread │
|
|
|
|
* ┌───────────────┴──┐│┌┴────────┐ └──────────────┘
|
|
|
|
* │ bsdthread_create │││ thr_new │
|
|
|
|
* └──────────────────┘│└─────────┘
|
|
|
|
* ┌───────┴──────┐
|
|
|
|
* │ _lwp_create │
|
|
|
|
* └──────────────┘
|
2022-09-06 04:43:49 +00:00
|
|
|
*
|
2022-09-07 12:23:44 +00:00
|
|
|
* @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`
|
2022-09-05 15:26:03 +00:00
|
|
|
* @return 0 on success, or errno on error
|
2022-09-07 12:23:44 +00:00
|
|
|
* @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
|
2022-09-08 04:13:50 +00:00
|
|
|
* @threadsafe
|
2022-06-13 02:33:42 +00:00
|
|
|
*/
|
2022-09-19 22:01:48 +00:00
|
|
|
errno_t pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
|
|
|
void *(*start_routine)(void *), void *arg) {
|
2022-09-07 12:23:44 +00:00
|
|
|
int rc, e = errno;
|
2022-09-05 15:26:03 +00:00
|
|
|
struct PosixThread *pt;
|
2022-09-10 09:56:25 +00:00
|
|
|
__require_tls();
|
2022-09-08 04:13:50 +00:00
|
|
|
_pthread_zombies_decimate();
|
2022-09-07 12:23:44 +00:00
|
|
|
|
|
|
|
// 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
|
2022-09-09 11:07:08 +00:00
|
|
|
if (!(pt->tls = _mktls(&pt->tib))) {
|
2022-09-07 12:23:44 +00:00
|
|
|
free(pt);
|
|
|
|
errno = e;
|
|
|
|
return EAGAIN;
|
|
|
|
}
|
|
|
|
|
2022-09-09 11:07:08 +00:00
|
|
|
// setup attributes
|
|
|
|
if (attr) {
|
|
|
|
pt->attr = *attr;
|
|
|
|
attr = 0;
|
2022-09-07 12:23:44 +00:00
|
|
|
} else {
|
2022-09-09 11:07:08 +00:00
|
|
|
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());
|
2022-09-13 06:10:38 +00:00
|
|
|
pt->attr.stacksize = _roundup2pow(pt->attr.stacksize);
|
2022-09-09 11:07:08 +00:00
|
|
|
pt->attr.guardsize = ROUNDUP(pt->attr.guardsize, PAGESIZE);
|
|
|
|
if (pt->attr.guardsize + PAGESIZE >= pt->attr.stacksize) {
|
|
|
|
_pthread_free(pt);
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2022-09-09 13:19:05 +00:00
|
|
|
if (pt->attr.guardsize == PAGESIZE) {
|
|
|
|
// user is wisely using smaller stacks with default guard size
|
|
|
|
pt->attr.stackaddr = mmap(0, pt->attr.stacksize, PROT_READ | PROT_WRITE,
|
|
|
|
MAP_STACK | MAP_ANONYMOUS, -1, 0);
|
|
|
|
} else {
|
|
|
|
// user is tuning things, performance may suffer
|
|
|
|
pt->attr.stackaddr = mmap(0, pt->attr.stacksize, PROT_READ | PROT_WRITE,
|
|
|
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
|
|
if (pt->attr.stackaddr != MAP_FAILED) {
|
|
|
|
if (IsOpenbsd() &&
|
|
|
|
__sys_mmap(
|
|
|
|
pt->attr.stackaddr, pt->attr.stacksize, PROT_READ | PROT_WRITE,
|
|
|
|
MAP_PRIVATE | MAP_FIXED | MAP_ANON_OPENBSD | MAP_STACK_OPENBSD,
|
|
|
|
-1, 0, 0) != pt->attr.stackaddr) {
|
|
|
|
notpossible;
|
|
|
|
}
|
|
|
|
if (pt->attr.guardsize && !IsWindows() &&
|
|
|
|
mprotect(pt->attr.stackaddr, pt->attr.guardsize, PROT_NONE)) {
|
|
|
|
notpossible;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-09 11:07:08 +00:00
|
|
|
if (pt->attr.stackaddr == MAP_FAILED) {
|
2022-09-05 15:26:03 +00:00
|
|
|
rc = errno;
|
2022-09-08 04:13:50 +00:00
|
|
|
_pthread_free(pt);
|
2022-09-07 12:23:44 +00:00
|
|
|
errno = e;
|
|
|
|
if (rc == EINVAL || rc == EOVERFLOW) {
|
|
|
|
return EINVAL;
|
|
|
|
} else {
|
|
|
|
return EAGAIN;
|
|
|
|
}
|
2022-09-05 15:26:03 +00:00
|
|
|
}
|
2022-09-09 13:19:05 +00:00
|
|
|
if (IsAsan() && pt->attr.guardsize) {
|
|
|
|
__asan_poison(pt->attr.stackaddr, pt->attr.guardsize, kAsanStackOverflow);
|
2022-09-07 12:23:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-11 18:02:07 +00:00
|
|
|
// setup signal handler stack
|
|
|
|
if (_wantcrashreports && !IsWindows()) {
|
|
|
|
pt->altstack = malloc(SIGSTKSZ);
|
|
|
|
}
|
|
|
|
|
2022-09-07 12:23:44 +00:00
|
|
|
// set initial status
|
2022-09-09 11:07:08 +00:00
|
|
|
switch (pt->attr.detachstate) {
|
2022-09-07 12:23:44 +00:00
|
|
|
case PTHREAD_CREATE_JOINABLE:
|
|
|
|
pt->status = kPosixThreadJoinable;
|
|
|
|
break;
|
|
|
|
case PTHREAD_CREATE_DETACHED:
|
|
|
|
pt->status = kPosixThreadDetached;
|
2022-09-08 04:13:50 +00:00
|
|
|
_pthread_zombies_add(pt);
|
2022-09-07 12:23:44 +00:00
|
|
|
break;
|
|
|
|
default:
|
2022-09-08 04:13:50 +00:00
|
|
|
_pthread_free(pt);
|
2022-09-07 12:23:44 +00:00
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// launch PosixThread(pt) in new thread
|
2022-09-09 11:07:08 +00:00
|
|
|
if (clone(PosixThread, pt->attr.stackaddr,
|
2022-09-09 13:19:05 +00:00
|
|
|
pt->attr.stacksize - (IsOpenbsd() ? 16 : 0),
|
2022-09-07 12:23:44 +00:00
|
|
|
CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
|
|
|
|
CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID |
|
|
|
|
CLONE_CHILD_CLEARTID,
|
2022-09-13 21:57:38 +00:00
|
|
|
pt, &pt->tid, pt->tib, &pt->tib->tib_tid) == -1) {
|
2022-09-05 15:26:03 +00:00
|
|
|
rc = errno;
|
2022-09-08 04:13:50 +00:00
|
|
|
_pthread_free(pt);
|
2022-09-07 12:23:44 +00:00
|
|
|
errno = e;
|
2022-09-09 11:07:08 +00:00
|
|
|
return rc;
|
2022-06-13 02:33:42 +00:00
|
|
|
}
|
2022-09-07 12:23:44 +00:00
|
|
|
|
|
|
|
if (thread) {
|
2022-09-08 18:54:56 +00:00
|
|
|
*thread = (pthread_t)pt;
|
2022-09-07 12:23:44 +00:00
|
|
|
}
|
|
|
|
return 0;
|
2022-06-13 02:33:42 +00:00
|
|
|
}
|