mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-28 13:30:29 +00:00
Implement pthread_atfork()
If threads are being used, then fork() will now acquire and release and runtime locks so that fork() may be safely used from threads. This also makes vfork() thread safe, because pthread mutexes will do nothing when the process is a child of vfork(). More torture tests have been written to confirm this all works like a charm. Additionally: - Invent hexpcpy() api - Rename nsync_malloc_() to kmalloc() - Complete posix named semaphore implementation - Make pthread_create() asynchronous signal safe - Add rm, rmdir, and touch to command interpreter builtins - Invent sigisprecious() and modify sigset functions to use it - Add unit tests for posix_spawn() attributes and fix its bugs One unresolved problem is the reclaiming of *NSYNC waiter memory in the forked child processes, within apps which have threads waiting on locks
This commit is contained in:
parent
64c284003d
commit
60cb435cb4
124 changed files with 2169 additions and 718 deletions
|
@ -28,3 +28,11 @@ void(__cxa_lock)(void) {
|
|||
void(__cxa_unlock)(void) {
|
||||
pthread_mutex_unlock(&__cxa_lock_obj);
|
||||
}
|
||||
|
||||
void(__cxa_funlock)(void) {
|
||||
pthread_mutex_init(&__cxa_lock_obj, 0);
|
||||
}
|
||||
|
||||
__attribute__((__constructor__)) static void __cxa_init(void) {
|
||||
pthread_atfork(__cxa_lock, __cxa_unlock, __cxa_funlock);
|
||||
}
|
||||
|
|
40
libc/intrin/describebacktrace.c
Normal file
40
libc/intrin/describebacktrace.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*-*- 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/describebacktrace.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/nexgen32e/stackframe.h"
|
||||
|
||||
#define N 64
|
||||
|
||||
#define append(...) o += ksnprintf(buf + o, N - o, __VA_ARGS__)
|
||||
|
||||
const char *(DescribeBacktrace)(char buf[N], struct StackFrame *fr) {
|
||||
int o = 0;
|
||||
bool gotsome = false;
|
||||
while (fr) {
|
||||
if (gotsome) {
|
||||
append(" ");
|
||||
} else {
|
||||
gotsome = true;
|
||||
}
|
||||
append("%x", fr->addr);
|
||||
fr = fr->next;
|
||||
}
|
||||
return buf;
|
||||
}
|
13
libc/intrin/describebacktrace.internal.h
Normal file
13
libc/intrin/describebacktrace.internal.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_INTRIN_DESCRIBEBACKTRACE_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_INTRIN_DESCRIBEBACKTRACE_INTERNAL_H_
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/nexgen32e/stackframe.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
const char *DescribeBacktrace(char[64], struct StackFrame *);
|
||||
#define DescribeBacktrace(x) DescribeBacktrace(alloca(64), x)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_INTRIN_DESCRIBEBACKTRACE_INTERNAL_H_ */
|
|
@ -62,6 +62,7 @@ const char *DescribeSocketFamily(char[12], int);
|
|||
const char *DescribeSocketProtocol(char[12], int);
|
||||
const char *DescribeSocketType(char[64], int);
|
||||
const char *DescribeStdioState(char[12], int);
|
||||
const char *DescribeStringList(char[300], char *const[]);
|
||||
const char *DescribeWhence(char[12], int);
|
||||
const char *DescribeWhichPrio(char[12], int);
|
||||
|
||||
|
@ -69,6 +70,7 @@ const char *DescribeWhichPrio(char[12], int);
|
|||
#define DescribeCapability(x) DescribeCapability(alloca(20), x)
|
||||
#define DescribeClockName(x) DescribeClockName(alloca(32), x)
|
||||
#define DescribeDirfd(x) DescribeDirfd(alloca(12), x)
|
||||
#define DescribeDnotifyFlags(x) DescribeDnotifyFlags(alloca(80), x)
|
||||
#define DescribeErrno(x) DescribeErrno(alloca(12), x)
|
||||
#define DescribeErrnoResult(x) DescribeErrnoResult(alloca(12), x)
|
||||
#define DescribeFcntlCmd(x) DescribeFcntlCmd(alloca(20), x)
|
||||
|
@ -111,9 +113,9 @@ const char *DescribeWhichPrio(char[12], int);
|
|||
#define DescribeSocketProtocol(x) DescribeSocketProtocol(alloca(12), x)
|
||||
#define DescribeSocketType(x) DescribeSocketType(alloca(64), x)
|
||||
#define DescribeStdioState(x) DescribeStdioState(alloca(12), x)
|
||||
#define DescribeStringList(x) DescribeStringList(alloca(300), x)
|
||||
#define DescribeWhence(x) DescribeWhence(alloca(12), x)
|
||||
#define DescribeWhichPrio(x) DescribeWhichPrio(alloca(12), x)
|
||||
#define DescribeDnotifyFlags(x) DescribeDnotifyFlags(alloca(80), x)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -54,8 +54,8 @@ static const char *GetFrameName(int x) {
|
|||
return "g_fds";
|
||||
} else if (IsZiposFrame(x)) {
|
||||
return "zipos";
|
||||
} else if (IsNsyncFrame(x)) {
|
||||
return "nsync";
|
||||
} else if (IsKmallocFrame(x)) {
|
||||
return "kmalloc";
|
||||
} else if (IsMemtrackFrame(x)) {
|
||||
return "memtrack";
|
||||
} else if (IsOldStackFrame(x)) {
|
||||
|
|
|
@ -23,14 +23,15 @@
|
|||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/popcnt.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
#define N 128
|
||||
|
||||
#define append(...) i += ksnprintf(buf + i, N - i, __VA_ARGS__)
|
||||
#define append(...) o += ksnprintf(buf + o, N - o, __VA_ARGS__)
|
||||
|
||||
const char *(DescribeSigset)(char buf[N], int rc, const sigset_t *ss) {
|
||||
int i, sig;
|
||||
bool gotsome;
|
||||
int sig, o = 0;
|
||||
sigset_t sigset;
|
||||
|
||||
if (rc == -1) return "n/a";
|
||||
|
@ -41,17 +42,22 @@ const char *(DescribeSigset)(char buf[N], int rc, const sigset_t *ss) {
|
|||
return buf;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
sigset = *ss;
|
||||
gotsome = false;
|
||||
if (popcnt(sigset.__bits[0] & 0xffffffff) > 16) {
|
||||
if (sigcountset(ss) > 16) {
|
||||
append("~");
|
||||
sigset.__bits[0] = ~sigset.__bits[0];
|
||||
sigset.__bits[1] = ~sigset.__bits[1];
|
||||
sigemptyset(&sigset);
|
||||
for (sig = 1; sig <= NSIG; ++sig) {
|
||||
if (!sigismember(ss, sig)) {
|
||||
sigaddset(&sigset, sig);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sigset = *ss;
|
||||
}
|
||||
|
||||
append("{");
|
||||
for (sig = 1; sig < 32; ++sig) {
|
||||
if (sigismember(&sigset, sig)) {
|
||||
gotsome = false;
|
||||
for (sig = 1; sig <= NSIG; ++sig) {
|
||||
if (sigismember(&sigset, sig) > 0) {
|
||||
if (gotsome) {
|
||||
append(",");
|
||||
} else {
|
||||
|
|
|
@ -24,10 +24,10 @@
|
|||
|
||||
#define N 300
|
||||
|
||||
#define append(...) i += ksnprintf(buf + i, N - i, __VA_ARGS__)
|
||||
#define append(...) o += ksnprintf(buf + o, N - o, __VA_ARGS__)
|
||||
|
||||
const char *(DescribeStat)(char buf[N], int rc, const struct stat *st) {
|
||||
int i = 0;
|
||||
int o = 0;
|
||||
|
||||
if (rc == -1) return "n/a";
|
||||
if (!st) return "NULL";
|
||||
|
@ -59,6 +59,10 @@ const char *(DescribeStat)(char buf[N], int rc, const struct stat *st) {
|
|||
append(", .st_%s=%lu", "gid", st->st_gid);
|
||||
}
|
||||
|
||||
if (st->st_dev) {
|
||||
append(", .st_%s=%lu", "dev", st->st_dev);
|
||||
}
|
||||
|
||||
if (st->st_ino) {
|
||||
append(", .st_%s=%lu", "ino", st->st_ino);
|
||||
}
|
||||
|
|
46
libc/intrin/describestringlist.c
Normal file
46
libc/intrin/describestringlist.c
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*-*- 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/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
|
||||
#define N 300
|
||||
|
||||
#define append(...) o += ksnprintf(buf + o, N - o, __VA_ARGS__)
|
||||
|
||||
const char *(DescribeStringList)(char buf[N], char *const list[]) {
|
||||
int i, o = 0;
|
||||
|
||||
if (!list) return "NULL";
|
||||
if (IsAsan() && !__asan_is_valid_strlist(list)) {
|
||||
ksnprintf(buf, N, "%p", list);
|
||||
return buf;
|
||||
}
|
||||
|
||||
append("{");
|
||||
i = 0;
|
||||
do {
|
||||
if (i++) append(", ");
|
||||
append("%#s", *list);
|
||||
} while (*list++);
|
||||
append("}");
|
||||
|
||||
return buf;
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
void(__fds_lock)(void) {
|
||||
|
@ -26,3 +27,8 @@ void(__fds_lock)(void) {
|
|||
void(__fds_unlock)(void) {
|
||||
pthread_mutex_unlock(&__fds_lock_obj);
|
||||
}
|
||||
|
||||
void __fds_funlock(void) {
|
||||
bzero(&__fds_lock_obj, sizeof(__fds_lock_obj));
|
||||
__fds_lock_obj._type = PTHREAD_MUTEX_RECURSIVE;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
@ -43,6 +44,8 @@ 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;
|
||||
pthread_atfork(_weaken(__fds_lock), _weaken(__fds_unlock),
|
||||
_weaken(__fds_funlock));
|
||||
fds = VEIL("r", &g_fds);
|
||||
fds->p = fds->e = (void *)kMemtrackFdsStart;
|
||||
fds->n = 4;
|
||||
|
|
75
libc/intrin/kmalloc.c
Normal file
75
libc/intrin/kmalloc.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*-*- 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/assert.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/extend.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#define KMALLOC_ALIGN __BIGGEST_ALIGNMENT__
|
||||
|
||||
static struct {
|
||||
char *endptr;
|
||||
size_t total;
|
||||
pthread_spinlock_t lock;
|
||||
} g_kmalloc;
|
||||
|
||||
static void kmalloc_lock(void) {
|
||||
if (__threaded) pthread_spin_lock(&g_kmalloc.lock);
|
||||
}
|
||||
|
||||
static void kmalloc_unlock(void) {
|
||||
pthread_spin_unlock(&g_kmalloc.lock);
|
||||
}
|
||||
|
||||
__attribute__((__constructor__)) static void kmalloc_init(void) {
|
||||
pthread_atfork(kmalloc_lock, kmalloc_unlock, kmalloc_unlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates permanent memory.
|
||||
*
|
||||
* The code malloc() depends upon uses this function to allocate memory.
|
||||
* The returned memory can't be freed, and leak detection is impossible.
|
||||
* This function panics when memory isn't available.
|
||||
*/
|
||||
void *kmalloc(size_t size) {
|
||||
char *start;
|
||||
size_t i, n;
|
||||
n = ROUNDUP(size + (IsAsan() * 8), KMALLOC_ALIGN);
|
||||
kmalloc_lock();
|
||||
i = g_kmalloc.total;
|
||||
g_kmalloc.total += n;
|
||||
start = (char *)kMemtrackKmallocStart;
|
||||
if (!g_kmalloc.endptr) g_kmalloc.endptr = start;
|
||||
g_kmalloc.endptr =
|
||||
_extend(start, g_kmalloc.total, g_kmalloc.endptr, MAP_PRIVATE,
|
||||
kMemtrackKmallocStart + kMemtrackKmallocSize);
|
||||
kmalloc_unlock();
|
||||
_unassert(!((intptr_t)(start + i) & (KMALLOC_ALIGN - 1)));
|
||||
if (IsAsan()) __asan_poison(start + i + size, n - size, kAsanHeapOverrun);
|
||||
return start + i;
|
||||
}
|
10
libc/intrin/kmalloc.h
Normal file
10
libc/intrin/kmalloc.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_INTRIN_KMALLOC_H_
|
||||
#define COSMOPOLITAN_LIBC_INTRIN_KMALLOC_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void *kmalloc(size_t) attributeallocsize((1)) mallocesque returnsnonnull;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_INTRIN_KMALLOC_H_ */
|
|
@ -206,6 +206,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt,
|
|||
const char *abet;
|
||||
signed char type;
|
||||
const char *s, *f;
|
||||
struct CosmoTib *tib;
|
||||
unsigned long long x;
|
||||
unsigned i, j, m, rem, sign, hash, cols, prec;
|
||||
char c, *p, *e, pdot, zero, flip, dang, base, quot, uppr, ansi, z[128];
|
||||
|
@ -323,12 +324,12 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt,
|
|||
goto FormatUnsigned;
|
||||
|
||||
case 'P':
|
||||
if (!__vforked) {
|
||||
if (!__tls_enabled) {
|
||||
x = __pid;
|
||||
tib = __tls_enabled ? __get_tls_privileged() : 0;
|
||||
if (!(tib && (tib->tib_flags & TIB_FLAG_VFORKED))) {
|
||||
if (tib) {
|
||||
x = atomic_load_explicit(&tib->tib_tid, memory_order_relaxed);
|
||||
} else {
|
||||
x = atomic_load_explicit(&__get_tls_privileged()->tib_tid,
|
||||
memory_order_relaxed);
|
||||
x = sys_gettid();
|
||||
}
|
||||
if (!__nocolor && p + 7 <= e) {
|
||||
*p++ = '\e';
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
// this lock currently needs to be (1) recursive and (2) not nsync
|
||||
|
@ -30,3 +31,12 @@ void(__mmi_lock)(void) {
|
|||
void(__mmi_unlock)(void) {
|
||||
pthread_mutex_unlock(&__mmi_lock_obj);
|
||||
}
|
||||
|
||||
void(__mmi_funlock)(void) {
|
||||
bzero(&__mmi_lock_obj, sizeof(__mmi_lock_obj));
|
||||
__mmi_lock_obj._type = PTHREAD_MUTEX_RECURSIVE;
|
||||
}
|
||||
|
||||
__attribute__((__constructor__)) static void init(void) {
|
||||
pthread_atfork(__mmi_lock, __mmi_unlock, __mmi_funlock);
|
||||
}
|
||||
|
|
84
libc/intrin/pthread_atfork.c
Normal file
84
libc/intrin/pthread_atfork.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*-*- 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/weaken.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
* Registers fork() handlers.
|
||||
*
|
||||
* Parent and child functions are called in the same order they're
|
||||
* registered. Prepare functions are called in reverse order.
|
||||
*
|
||||
* Here's an example of how pthread_atfork() can be used:
|
||||
*
|
||||
* static struct {
|
||||
* pthread_once_t once;
|
||||
* pthread_mutex_t lock;
|
||||
* // data structures...
|
||||
* } g_lib;
|
||||
*
|
||||
* static void lib_lock(void) {
|
||||
* pthread_mutex_lock(&g_lib.lock);
|
||||
* }
|
||||
*
|
||||
* static void lib_unlock(void) {
|
||||
* pthread_mutex_unlock(&g_lib.lock);
|
||||
* }
|
||||
*
|
||||
* static void lib_funlock(void) {
|
||||
* pthread_mutex_init(&g_lib.lock, 0);
|
||||
* }
|
||||
*
|
||||
* static void lib_setup(void) {
|
||||
* lib_funlock();
|
||||
* pthread_atfork(lib_lock, lib_unlock, lib_funlock);
|
||||
* }
|
||||
*
|
||||
* static void lib_init(void) {
|
||||
* pthread_once(&g_lib.once, lib_setup);
|
||||
* }
|
||||
*
|
||||
* void lib(void) {
|
||||
* lib_init();
|
||||
* lib_lock();
|
||||
* // do stuff...
|
||||
* lib_unlock();
|
||||
* }
|
||||
*
|
||||
* This won't actually aspect fork() until pthread_create() is called,
|
||||
* since we don't want normal non-threaded programs to have to acquire
|
||||
* exclusive locks on every resource in the entire app just to fork().
|
||||
*
|
||||
* The vfork() function is *never* aspected. What happens instead is a
|
||||
* global variable named `__vforked` is set to true in the child which
|
||||
* causes lock functions to do nothing. So far, it works like a charm.
|
||||
*
|
||||
* @param prepare is run by fork() before forking happens
|
||||
* @param parent is run by fork() after forking happens in parent process
|
||||
* @param child is run by fork() after forking happens in childe process
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int pthread_atfork(atfork_f prepare, atfork_f parent, atfork_f child) {
|
||||
if (_weaken(_pthread_atfork)) {
|
||||
return _weaken(_pthread_atfork)(prepare, parent, child);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@
|
|||
*/
|
||||
int pthread_key_create(pthread_key_t *key, pthread_key_dtor dtor) {
|
||||
int i, j, rc = EAGAIN;
|
||||
pthread_spin_lock(&_pthread_keys_lock);
|
||||
_pthread_key_lock();
|
||||
for (i = 0; i < (PTHREAD_KEYS_MAX + 63) / 64; ++i) {
|
||||
if (~_pthread_key_usage[i]) {
|
||||
j = _bsrl(~_pthread_key_usage[i]);
|
||||
|
@ -39,14 +39,29 @@ int pthread_key_create(pthread_key_t *key, pthread_key_dtor dtor) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
pthread_spin_unlock(&_pthread_keys_lock);
|
||||
_pthread_key_unlock();
|
||||
return rc;
|
||||
}
|
||||
|
||||
void _pthread_key_lock(void) {
|
||||
pthread_spin_lock(&_pthread_keys_lock);
|
||||
}
|
||||
|
||||
void _pthread_key_unlock(void) {
|
||||
pthread_spin_unlock(&_pthread_keys_lock);
|
||||
}
|
||||
|
||||
static void _pthread_key_funlock(void) {
|
||||
pthread_spin_init(&_pthread_keys_lock, 0);
|
||||
}
|
||||
|
||||
static textexit void _pthread_key_atexit(void) {
|
||||
_pthread_key_destruct(0);
|
||||
}
|
||||
|
||||
__attribute__((__constructor__)) static textstartup void _pthread_key_init() {
|
||||
atexit(_pthread_key_atexit);
|
||||
pthread_atfork(_pthread_key_lock, //
|
||||
_pthread_key_unlock, //
|
||||
_pthread_key_funlock);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
*/
|
||||
int pthread_key_delete(pthread_key_t key) {
|
||||
int rc;
|
||||
pthread_spin_lock(&_pthread_keys_lock);
|
||||
_pthread_key_lock();
|
||||
if (key < PTHREAD_KEYS_MAX) {
|
||||
_pthread_key_usage[key / 64] &= ~(1ul << (key % 64));
|
||||
_pthread_key_dtor[key] = 0;
|
||||
|
@ -33,6 +33,6 @@ int pthread_key_delete(pthread_key_t key) {
|
|||
} else {
|
||||
rc = EINVAL;
|
||||
}
|
||||
pthread_spin_unlock(&_pthread_keys_lock);
|
||||
_pthread_key_unlock();
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -21,13 +21,14 @@
|
|||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
// TODO(jart): why does this even need a lock?
|
||||
void _pthread_key_destruct(void *key[PTHREAD_KEYS_MAX]) {
|
||||
int i, j;
|
||||
uint64_t x;
|
||||
void *value;
|
||||
pthread_key_dtor dtor;
|
||||
if (!__tls_enabled) return;
|
||||
pthread_spin_lock(&_pthread_keys_lock);
|
||||
_pthread_key_lock();
|
||||
if (!key) key = __get_tls()->tib_keys;
|
||||
StartOver:
|
||||
for (i = 0; i < (PTHREAD_KEYS_MAX + 63) / 64; ++i) {
|
||||
|
@ -36,13 +37,13 @@ StartOver:
|
|||
j = _bsrl(x);
|
||||
if ((value = key[i * 64 + j]) && (dtor = _pthread_key_dtor[i * 64 + j])) {
|
||||
key[i * 64 + j] = 0;
|
||||
pthread_spin_unlock(&_pthread_keys_lock);
|
||||
_pthread_key_unlock();
|
||||
dtor(value);
|
||||
pthread_spin_lock(&_pthread_keys_lock);
|
||||
_pthread_key_lock();
|
||||
goto StartOver;
|
||||
}
|
||||
x &= ~(1ul << j);
|
||||
}
|
||||
}
|
||||
pthread_spin_unlock(&_pthread_keys_lock);
|
||||
_pthread_key_unlock();
|
||||
}
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "third_party/nsync/mu.h"
|
||||
|
@ -55,12 +58,19 @@
|
|||
* pthread_mutex_unlock(&lock);
|
||||
* pthread_mutex_destroy(&lock);
|
||||
*
|
||||
* This function does nothing in vfork() children.
|
||||
*
|
||||
* @return 0 on success, or error number on failure
|
||||
* @see pthread_spin_lock()
|
||||
* @vforksafe
|
||||
*/
|
||||
int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||
int c, d, t;
|
||||
|
||||
if (__vforked) return 0;
|
||||
|
||||
LOCKTRACE("pthread_mutex_lock(%t)", mutex);
|
||||
|
||||
if (__tls_enabled && //
|
||||
mutex->_type == PTHREAD_MUTEX_NORMAL && //
|
||||
mutex->_pshared == PTHREAD_PROCESS_PRIVATE && //
|
||||
|
@ -96,6 +106,7 @@ int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
|||
|
||||
mutex->_depth = 0;
|
||||
mutex->_owner = t;
|
||||
mutex->_pid = __pid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "third_party/nsync/mu.h"
|
||||
|
@ -27,12 +30,19 @@
|
|||
/**
|
||||
* Releases mutex.
|
||||
*
|
||||
* This function does nothing in vfork() children.
|
||||
*
|
||||
* @return 0 on success or error number on failure
|
||||
* @raises EPERM if in error check mode and not owned by caller
|
||||
* @vforksafe
|
||||
*/
|
||||
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
||||
int c, t;
|
||||
|
||||
if (__vforked) return 0;
|
||||
|
||||
LOCKTRACE("pthread_mutex_unlock(%t)", mutex);
|
||||
|
||||
if (__tls_enabled && //
|
||||
mutex->_type == PTHREAD_MUTEX_NORMAL && //
|
||||
mutex->_pshared == PTHREAD_PROCESS_PRIVATE && //
|
||||
|
@ -47,7 +57,11 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
|||
}
|
||||
|
||||
t = gettid();
|
||||
if (mutex->_owner != t) {
|
||||
|
||||
// we allow unlocking an initialized lock that wasn't locked, but we
|
||||
// don't allow unlocking a lock held by another thread, on unlocking
|
||||
// recursive locks from a forked child, since it should be re-init'd
|
||||
if (mutex->_owner && (mutex->_owner != t || mutex->_pid != __pid)) {
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
|
@ -29,7 +30,9 @@
|
|||
int sigaddset(sigset_t *set, int sig) {
|
||||
_Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, "");
|
||||
if (1 <= sig && sig <= NSIG) {
|
||||
set->__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63);
|
||||
if (!sigisprecious(sig)) {
|
||||
set->__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63);
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
return einval();
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
/**
|
||||
* Adds all signals to set.
|
||||
|
@ -28,5 +29,7 @@
|
|||
*/
|
||||
int sigfillset(sigset_t *set) {
|
||||
memset(set->__bits, -1, sizeof(set->__bits));
|
||||
sigdelset(set, SIGKILL);
|
||||
sigdelset(set, SIGSTOP);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*-*- 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 2021 Justine Alexandra Roberts Tunney │
|
||||
│ 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 │
|
||||
|
@ -16,5 +16,13 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
int __vforked;
|
||||
/**
|
||||
* Returns true if you're not authorized to block this signal.
|
||||
*/
|
||||
int sigisprecious(int sig) {
|
||||
return sig == SIGKILL || //
|
||||
sig == SIGSTOP;
|
||||
}
|
|
@ -7,7 +7,8 @@
|
|||
#define _POLLTRACE 0 /* not configurable w/ flag yet */
|
||||
#define _DATATRACE 1 /* not configurable w/ flag yet */
|
||||
#define _STDIOTRACE 0 /* not configurable w/ flag yet */
|
||||
#define _NTTRACE 1 /* not configurable w/ flag yet */
|
||||
#define _LOCKTRACE 0 /* not configurable w/ flag yet */
|
||||
#define _NTTRACE 0 /* not configurable w/ flag yet */
|
||||
|
||||
#define STRACE_PROLOGUE "%rSYS %6P %'18T "
|
||||
|
||||
|
@ -55,6 +56,12 @@ COSMOPOLITAN_C_START_
|
|||
#define NTTRACE(FMT, ...) (void)0
|
||||
#endif
|
||||
|
||||
#if defined(SYSDEBUG) && _LOCKTRACE
|
||||
#define LOCKTRACE(FMT, ...) STRACE(FMT, ##__VA_ARGS__)
|
||||
#else
|
||||
#define LOCKTRACE(FMT, ...) (void)0
|
||||
#endif
|
||||
|
||||
void __stracef(const char *, ...);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
void __require_tls(void) {
|
||||
|
|
|
@ -228,8 +228,8 @@ static void __ubsan_warning(const struct UbsanSourceLocation *loc,
|
|||
|
||||
dontdiscard __ubsan_die_f *__ubsan_abort(const struct UbsanSourceLocation *loc,
|
||||
const char *description) {
|
||||
kprintf("\n%s:%d: %subsan error%s: %s\n", loc->file, loc->line, RED2, RESET,
|
||||
description);
|
||||
kprintf("\n%s:%d: %subsan error%s: %s (tid %d)\n", loc->file, loc->line, RED2,
|
||||
RESET, description, gettid());
|
||||
return __ubsan_die();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue