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:
Justine Tunney 2022-10-16 12:05:08 -07:00
parent 64c284003d
commit 60cb435cb4
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
124 changed files with 2169 additions and 718 deletions

View file

@ -92,6 +92,7 @@ o/$(MODE): \
.UNVEIL = \
libc/integral \
libc/disclaimer.inc \
rwc:/dev/shm \
rx:build/bootstrap \
rx:o/third_party/gcc \
/proc/stat \

View file

@ -17,7 +17,7 @@ void __assert_fail(const char *, const char *, int) hidden relegated;
#endif
#ifdef __GNUC__
#ifndef TINY
#if !defined(TINY) && !defined(MODE_DBG)
/**
* Specifies expression can't possibly be false.
*
@ -36,7 +36,7 @@ void __assert_fail(const char *, const char *, int) hidden relegated;
* In `MODE=tiny` these assertions are redefined as undefined behavior.
*/
#define _npassert(x) \
({ \
__extension__({ \
if (__builtin_expect(!(x), 0)) { \
notpossible; \
} \
@ -63,7 +63,7 @@ void __assert_fail(const char *, const char *, int) hidden relegated;
* that's capable of debugging this macro.
*/
#define _unassert(x) \
({ \
__extension__({ \
if (__builtin_expect(!(x), 0)) { \
unreachable; \
} \

View file

@ -84,7 +84,7 @@ int clock_gettime(int clock, struct timespec *ts) {
rc = __clock_gettime(clock, ts);
}
#if SYSDEBUG
if (!(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
if (__tls_enabled && !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
STRACE("clock_gettime(%s, [%s]) → %d% m", DescribeClockName(clock),
DescribeTimespec(rc, ts), rc);
}

View file

@ -113,7 +113,7 @@ errno_t clock_nanosleep(int clock, int flags, const struct timespec *req,
}
#if SYSDEBUG
if (!(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
if (__tls_enabled && !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
STRACE("clock_nanosleep(%s, %s, %s, [%s]) → %s", DescribeClockName(clock),
DescribeSleepFlags(flags), DescribeTimespec(0, req),
DescribeTimespec(rc, rem), DescribeErrnoResult(rc));

View file

@ -23,6 +23,7 @@
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/likely.h"
#include "libc/intrin/promises.internal.h"
@ -58,21 +59,8 @@ int execve(const char *prog, char *const argv[], char *const envp[]) {
!__asan_is_valid_strlist(envp)))) {
rc = efault();
} else {
#ifdef SYSDEBUG
if (UNLIKELY(__strace > 0)) {
kprintf(STRACE_PROLOGUE "execve(%#s, {", prog);
for (i = 0; argv[i]; ++i) {
if (i) kprintf(", ");
kprintf("%#s", argv[i]);
}
kprintf("}, {");
for (i = 0; envp[i]; ++i) {
if (i) kprintf(", ");
kprintf("%#s", envp[i]);
}
kprintf("})\n");
}
#endif
STRACE("execve(%#s, %s, %s) → ...", prog, DescribeStringList(argv),
DescribeStringList(envp));
if (!IsWindows()) {
rc = 0;
if (IsLinux() && __execpromises && _weaken(sys_pledge_linux)) {

View file

@ -63,7 +63,7 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) {
rc = __gettimeofday(tv, tz, 0).ax;
}
#if SYSDEBUG
if (!(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
if (__tls_enabled && !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
STRACE("gettimeofday([%s], %p) → %d% m", DescribeTimeval(rc, tv), tz, rc);
}
#endif

View file

@ -56,7 +56,7 @@ static long double GetTimeSample(void) {
static long double MeasureNanosPerCycle(void) {
int i, n;
long double avg, samp;
__get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
if (__tls_enabled) __get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
if (IsWindows()) {
n = 10;
} else {
@ -66,7 +66,7 @@ static long double MeasureNanosPerCycle(void) {
samp = GetTimeSample();
avg += (samp - avg) / i;
}
__get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
if (__tls_enabled) __get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000));
return avg;
}

View file

@ -53,7 +53,7 @@ static long double GetTimeSample(void) {
static long double MeasureNanosPerCycle(void) {
int i, n;
long double avg, samp;
__get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
if (__tls_enabled) __get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
if (IsWindows()) {
n = 30;
} else {
@ -63,7 +63,7 @@ static long double MeasureNanosPerCycle(void) {
samp = GetTimeSample();
avg += (samp - avg) / i;
}
__get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
if (__tls_enabled) __get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000));
return avg;
}

View file

@ -19,9 +19,9 @@
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/stdio/rand.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "libc/stdio/lcg.internal.h"
/**
* Returns handles of windows pids being tracked.
@ -35,12 +35,8 @@
textwindows int __sample_pids(int pids[hasatleast 64],
int64_t handles[hasatleast 64],
bool exploratory) {
static uint64_t rando = 1;
static pthread_spinlock_t lock;
uint32_t i, j, base, count;
if (__threaded) pthread_spin_lock(&lock);
base = KnuthLinearCongruentialGenerator(&rando) >> 32;
pthread_spin_unlock(&lock);
base = _rand64() >> 32;
for (count = i = 0; i < g_fds.n; ++i) {
j = (base + i) % g_fds.n;
if (g_fds.p[j].kind == kFdProcess && (!exploratory || !g_fds.p[j].zombie)) {

View file

@ -17,10 +17,10 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/console.h"
#include "libc/sysv/errfuns.h"
@ -34,7 +34,7 @@ int setpgid(int pid, int pgid) {
rc = sys_setpgid(pid, pgid);
} else {
me = getpid();
if (pid == me && pgid == me) {
if ((!pid || pid == me) && (!pgid || pgid == me)) {
/*
* "When a process is created with CREATE_NEW_PROCESS_GROUP
* specified, an implicit call to SetConsoleCtrlHandler(NULL,TRUE)

View file

@ -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(__sig_lock)(void) {
@ -27,6 +28,11 @@ void(__sig_unlock)(void) {
pthread_mutex_unlock(&__sig_lock_obj);
}
__attribute__((__constructor__)) static void init(void) {
void __sig_funlock(void) {
bzero(&__sig_lock_obj, sizeof(__sig_lock_obj));
__sig_lock_obj._type = PTHREAD_MUTEX_RECURSIVE;
}
__attribute__((__constructor__)) static void __sig_init(void) {
pthread_atfork(__sig_lock, __sig_unlock, __sig_funlock);
}

View file

@ -16,8 +16,10 @@ hidden extern const struct NtSecurityAttributes kNtIsInheritable;
void __fds_lock(void);
void __fds_unlock(void);
void __fds_funlock(void);
void __sig_lock(void);
void __sig_unlock(void);
void __sig_funlock(void);
#ifdef _NOPL0
#define __fds_lock() _NOPL0("__threadcalls", __fds_lock)
@ -35,6 +37,8 @@ void __sig_unlock(void);
#define __sig_unlock() (__threaded ? __sig_unlock() : 0)
#endif
#define __vforked (__tls_enabled && (__get_tls()->tib_flags & TIB_FLAG_VFORKED))
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_STATE_INTERNAL_H_ */

View file

@ -16,6 +16,7 @@ int sigorset(sigset_t *, const sigset_t *, const sigset_t *) paramsnonnull();
int sigisemptyset(const sigset_t *) paramsnonnull() nosideeffect;
int sigismember(const sigset_t *, int) paramsnonnull() nosideeffect;
int sigcountset(const sigset_t *) paramsnonnull() nosideeffect;
int sigisprecious(int) nosideeffect;
int sigprocmask(int, const sigset_t *, sigset_t *);
int sigsuspend(const sigset_t *);
int sigpending(sigset_t *);

View file

@ -18,21 +18,46 @@
*/
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/struct/ucontext.internal.h"
#include "libc/calls/ucontext.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/enum/exceptionhandleractions.h"
#include "libc/nt/enum/signal.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/ntexceptionpointers.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/tls.h"
#include "libc/thread/tls2.h"
privileged unsigned __wincrash(struct NtExceptionPointers *ep) {
int64_t rip;
int sig, code;
ucontext_t ctx;
struct CosmoTib *tib;
static bool noreentry;
if ((tib = __tls_enabled ? __get_tls_privileged() : 0)) {
if (~tib->tib_flags & TIB_FLAG_WINCRASHING) {
tib->tib_flags |= TIB_FLAG_WINCRASHING;
} else {
WincrashPanic:
STRACE("panic: wincrash reentry: rip %x bt %s", ep->ContextRecord->Rip,
DescribeBacktrace((struct StackFrame *)ep->ContextRecord->Rbp));
ExitProcess(89);
}
} else {
if (!noreentry) {
noreentry = true;
} else {
goto WincrashPanic;
}
}
STRACE("__wincrash");
switch (ep->ExceptionRecord->ExceptionCode) {
@ -115,5 +140,10 @@ privileged unsigned __wincrash(struct NtExceptionPointers *ep) {
ep->ContextRecord->Rip++;
}
noreentry = false;
if (tib) {
tib->tib_flags &= ~TIB_FLAG_WINCRASHING;
}
return kNtExceptionContinueExecution;
}

View file

@ -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);
}

View file

@ -16,22 +16,25 @@
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/runtime/runtime.h"
#include "libc/str/path.h"
#include "libc/str/str.h"
#include "libc/thread/semaphore.internal.h"
#include "libc/thread/thread.h"
#include "libc/nexgen32e/stackframe.h"
const char *__sem_name(const char *name, char path[hasatleast PATH_MAX]) {
const char *res;
if (_isabspath(name)) {
res = name;
} else {
strlcpy(path, kTmpPath, PATH_MAX);
strlcat(path, ".sem-", PATH_MAX);
strlcat(path, name, PATH_MAX);
res = path;
#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 res;
return buf;
}

View 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_ */

View file

@ -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) */

View file

@ -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)) {

View file

@ -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 {

View file

@ -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);
}

View 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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -1,5 +1,5 @@
/*-*- 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
/*-*- 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
@ -16,37 +16,60 @@
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 "third_party/nsync/common.internal.h"
#include "third_party/nsync/malloc.internal.h"
// clang-format off
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
static char *nsync_malloc_endptr_;
static size_t nsync_malloc_total_;
static atomic_char nsync_malloc_lock_;
#define KMALLOC_ALIGN __BIGGEST_ALIGNMENT__
/* nsync_malloc_() is a malloc-like routine used by mutex and condition
variable code to allocate waiter structs. This allows *NSYNC mutexes
to be used by malloc(), by providing another, simpler allocator here.
The intent is that the implicit NULL value here can be overridden by
a client declaration that uses an initializer. */
void *nsync_malloc_ (size_t size) {
char *start;
size_t offset;
size = ROUNDUP (size, __BIGGEST_ALIGNMENT__);
while (atomic_exchange (&nsync_malloc_lock_, 1)) nsync_yield_ ();
offset = nsync_malloc_total_;
nsync_malloc_total_ += size;
start = (char *) kMemtrackNsyncStart;
if (!nsync_malloc_endptr_) nsync_malloc_endptr_ = start;
nsync_malloc_endptr_ =
_extend (start, nsync_malloc_total_, nsync_malloc_endptr_,
MAP_PRIVATE, kMemtrackNsyncStart + kMemtrackNsyncSize);
atomic_store_explicit (&nsync_malloc_lock_, 0, memory_order_relaxed);
return start + offset;
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
View 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_ */

View file

@ -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';

View file

@ -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);
}

View 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;
}
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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();

View file

@ -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;
}

View file

@ -16,17 +16,13 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/semaphore.h"
#include "libc/calls/struct/sigset.h"
#include "libc/sysv/consts/sig.h"
/**
* Closes named semaphore.
*
* @param sem was created with sem_open()
* @return 0 on success, or -1 w/ errno
* Returns true if you're not authorized to block this signal.
*/
int sem_close(sem_t *sem) {
_npassert(!munmap(sem, FRAMESIZE));
return 0;
int sigisprecious(int sig) {
return sig == SIGKILL || //
sig == SIGSTOP;
}

View file

@ -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_

View file

@ -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) {

View file

@ -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();
}

View file

@ -1,20 +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 2021 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.
*/
int __vforked;

4
libc/isystem/cpio.h Normal file
View file

@ -0,0 +1,4 @@
#ifndef COSMOPOLITAN_LIBC_ISYSTEM_CPIO_H_
#define COSMOPOLITAN_LIBC_ISYSTEM_CPIO_H_
#include "libc/sysv/consts/c.h"
#endif /* COSMOPOLITAN_LIBC_ISYSTEM_CPIO_H_ */

View file

@ -284,7 +284,6 @@ relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
int gdbpid, err;
static int sync;
static bool _notpossible;
__tls_enabled = false;
STRACE("__oncrash rip %x", ctx->uc_mcontext.rip);
--__ftrace;
--__strace;

View file

@ -51,7 +51,6 @@ struct mallinfo {
struct mallinfo mallinfo(void);
void malloc_stats(void);
size_t malloc_footprint(void);
size_t malloc_max_footprint(void);
size_t malloc_footprint_limit(void);

View file

@ -87,21 +87,26 @@ static unsigned char *brk_unlocked(unsigned char *p) {
}
}
int brk_lock(void) {
void brk_lock(void) {
pthread_mutex_lock(&__brk.m);
return 0;
}
void brk_unlock(void) {
pthread_mutex_unlock(&__brk.m);
}
void brk_funlock(void) {
pthread_mutex_init(&__brk.m, 0);
}
__attribute__((__constructor__)) static void brk_init(void) {
brk_funlock();
pthread_atfork(brk_lock, brk_unlock, brk_funlock);
}
#ifdef _NOPL0
#define brk_lock() _NOPL0("__threadcalls", brk_lock)
#define brk_unlock() _NOPL0("__threadcalls", brk_unlock)
#else
#define brk_lock() (__threaded ? brk_lock() : 0)
#define brk_unlock() (__threaded ? brk_unlock() : 0)
#endif
/**

View file

@ -301,7 +301,7 @@ static int Kill(void) {
int sig, rc = 0, i = 1;
if (i < n && args[i][0] == '-') {
sig = GetSignalByName(args[i++] + 1);
if (!sig) return 1;
if (!sig) return -1; // fallback to system kill command
} else {
sig = SIGTERM;
}
@ -340,15 +340,15 @@ static int Usleep(void) {
}
static int Test(void) {
int w;
int w, m = n;
struct stat st;
if (n && READ16LE(args[n - 1]) == READ16LE("]")) --n;
if (n == 4) {
if (m && READ16LE(args[m - 1]) == READ16LE("]")) --m;
if (m == 4) {
w = READ32LE(args[2]) & 0x00ffffff;
if ((w & 65535) == READ16LE("=")) return !!strcmp(args[1], args[3]);
if (w == READ24("==")) return !!strcmp(args[1], args[3]);
if (w == READ24("!=")) return !strcmp(args[1], args[3]);
} else if (n == 3) {
} else if (m == 3) {
w = READ32LE(args[1]) & 0x00ffffff;
if (w == READ24("-n")) return !(strlen(args[2]) > 0);
if (w == READ24("-z")) return !(strlen(args[2]) == 0);
@ -357,7 +357,52 @@ static int Test(void) {
if (w == READ24("-d")) return !(!stat(args[2], &st) && S_ISDIR(st.st_mode));
if (w == READ24("-h")) return !(!stat(args[2], &st) && S_ISLNK(st.st_mode));
}
return 1;
return -1; // fall back to system test command
}
static int Rm(void) {
int i;
if (n > 1 && args[1][0] != '-') {
for (i = 1; i < n; ++i) {
if (unlink(args[i])) {
Log("rm: ", args[i], ": ", _strerdoc(errno), 0);
return 1;
}
}
return 0;
} else {
return -1; // fall back to system rm command
}
}
static int Rmdir(void) {
int i;
if (n > 1 && args[1][0] != '-') {
for (i = 1; i < n; ++i) {
if (rmdir(args[i])) {
Log("rmdir: ", args[i], ": ", _strerdoc(errno), 0);
return 1;
}
}
return 0;
} else {
return -1; // fall back to system rmdir command
}
}
static int Touch(void) {
int i;
if (n > 1 && args[1][0] != '-') {
for (i = 1; i < n; ++i) {
if (touch(args[i], 0644)) {
Log("touch: ", args[i], ": ", _strerdoc(errno), 0);
return 1;
}
}
return 0;
} else {
return -1; // fall back to system rmdir command
}
}
static int Fake(int main(int, char **)) {
@ -377,6 +422,7 @@ static int TryBuiltin(void) {
if (!n) return 0;
if (!strcmp(args[0], "exit")) Exit();
if (!strcmp(args[0], "cd")) return Cd();
if (!strcmp(args[0], "rm")) return Rm();
if (!strcmp(args[0], "[")) return Test();
if (!strcmp(args[0], "wait")) return Wait();
if (!strcmp(args[0], "echo")) return Echo();
@ -384,6 +430,8 @@ static int TryBuiltin(void) {
if (!strcmp(args[0], "true")) return True();
if (!strcmp(args[0], "test")) return Test();
if (!strcmp(args[0], "kill")) return Kill();
if (!strcmp(args[0], "touch")) return Touch();
if (!strcmp(args[0], "rmdir")) return Rmdir();
if (!strcmp(args[0], "mkdir")) return Mkdir();
if (!strcmp(args[0], "false")) return False();
if (!strcmp(args[0], "usleep")) return Usleep();

View file

@ -19,6 +19,7 @@
#include "ape/sections.internal.h"
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/tls.h"
@ -28,7 +29,7 @@ extern int __threadcalls_start[];
#pragma weak __threadcalls_start
#pragma weak __threadcalls_end
static privileged dontinline void FixupLocks(void) {
static privileged dontinline void FixupLockNops(void) {
__morph_begin();
/*
* _NOPL("__threadcalls", func)
@ -59,6 +60,6 @@ static privileged dontinline void FixupLocks(void) {
void __enable_threads(void) {
if (__threaded) return;
STRACE("__enable_threads()");
FixupLocks();
__threaded = gettid();
FixupLockNops();
__threaded = sys_gettid();
}

View file

@ -273,7 +273,7 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
char *p, forkvar[6 + 21 + 1 + 21 + 1];
struct NtProcessInformation procinfo;
if (!setjmp(jb)) {
pid = untrackpid = __reservefd(-1);
pid = untrackpid = __reservefd_unlocked(-1);
reader = CreateNamedPipe(CreatePipeName(pipename),
kNtPipeAccessInbound | kNtFileFlagOverlapped,
kNtPipeTypeMessage | kNtPipeReadmodeMessage, 1,
@ -347,7 +347,7 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
rc = 0;
}
if (untrackpid != -1) {
__releasefd(untrackpid);
__releasefd_unlocked(untrackpid);
}
return rc;
}

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h"
@ -33,14 +34,15 @@
int _fork(uint32_t dwCreationFlags) {
axdx_t ad;
bool threaded;
sigset_t old, all;
int ax, dx, parent, parent_tid = 0;
if (_weaken(_pthread_atfork)) {
parent_tid = gettid();
int ax, dx, parent;
sigfillset(&all);
_unassert(!sigprocmask(SIG_BLOCK, &all, &old));
if (__threaded && _weaken(_pthread_onfork_prepare)) {
_weaken(_pthread_onfork_prepare)();
}
if (!IsWindows()) {
sigfillset(&all);
sys_sigprocmask(SIG_BLOCK, &all, &old);
ad = sys_fork();
ax = ad.ax;
dx = ad.dx;
@ -50,7 +52,11 @@ int _fork(uint32_t dwCreationFlags) {
ax &= dx - 1;
}
} else {
threaded = __threaded;
ax = sys_fork_nt(dwCreationFlags);
if (threaded && !__threaded && _weaken(__enable_threads)) {
_weaken(__enable_threads)();
}
}
if (!ax) {
if (!IsWindows()) {
@ -65,15 +71,17 @@ int _fork(uint32_t dwCreationFlags) {
IsLinux() ? dx : sys_gettid(),
memory_order_relaxed);
}
if (_weaken(_pthread_atfork)) {
_weaken(_pthread_atfork)(parent_tid);
if (__threaded && _weaken(_pthread_onfork_child)) {
_weaken(_pthread_onfork_child)();
}
if (!IsWindows()) sys_sigprocmask(SIG_SETMASK, &old, 0);
STRACE("fork() → 0 (child of %d)", parent);
} else {
if (!IsWindows()) sys_sigprocmask(SIG_SETMASK, &old, 0);
if (__threaded && _weaken(_pthread_onfork_parent)) {
_weaken(_pthread_onfork_parent)();
}
STRACE("fork() → %d% m", ax);
}
_unassert(!sigprocmask(SIG_SETMASK, &old, 0));
return ax;
}

View file

@ -11,19 +11,19 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define kAutomapStart 0x100080040000
#define kAutomapSize (kMemtrackStart - kAutomapStart)
#define kMemtrackStart 0x1fe7fffc0000
#define kMemtrackSize (0x1ffffffc0000 - kMemtrackStart)
#define kFixedmapStart 0x300000040000
#define kFixedmapSize (0x400000040000 - kFixedmapStart)
#define kMemtrackFdsStart 0x6fe000040000
#define kMemtrackFdsSize (0x6feffffc0000 - kMemtrackFdsStart)
#define kMemtrackZiposStart 0x6fd000040000
#define kMemtrackZiposSize (0x6fdffffc0000 - kMemtrackZiposStart)
#define kMemtrackNsyncStart 0x6fc000040000
#define kMemtrackNsyncSize (0x6fcffffc0000 - kMemtrackNsyncStart)
#define kMemtrackGran (!IsAsan() ? FRAMESIZE : FRAMESIZE * 8)
#define kAutomapStart 0x100080040000
#define kAutomapSize (kMemtrackStart - kAutomapStart)
#define kMemtrackStart 0x1fe7fffc0000
#define kMemtrackSize (0x1ffffffc0000 - kMemtrackStart)
#define kFixedmapStart 0x300000040000
#define kFixedmapSize (0x400000040000 - kFixedmapStart)
#define kMemtrackFdsStart 0x6fe000040000
#define kMemtrackFdsSize (0x6feffffc0000 - kMemtrackFdsStart)
#define kMemtrackZiposStart 0x6fd000040000
#define kMemtrackZiposSize (0x6fdffffc0000 - kMemtrackZiposStart)
#define kMemtrackKmallocStart 0x6fc000040000
#define kMemtrackKmallocSize (0x6fcffffc0000 - kMemtrackKmallocStart)
#define kMemtrackGran (!IsAsan() ? FRAMESIZE : FRAMESIZE * 8)
struct MemoryInterval {
int x;
@ -47,6 +47,7 @@ extern struct MemoryIntervals _mmi;
void __mmi_lock(void);
void __mmi_unlock(void);
void __mmi_funlock(void);
bool IsMemtracked(int, int);
void PrintSystemMappings(int);
unsigned FindMemoryInterval(const struct MemoryIntervals *, int) nosideeffect;
@ -95,9 +96,9 @@ forceinline pureconst bool IsZiposFrame(int x) {
x <= (int)((kMemtrackZiposStart + kMemtrackZiposSize - 1) >> 16);
}
forceinline pureconst bool IsNsyncFrame(int x) {
return (int)(kMemtrackNsyncStart >> 16) <= x &&
x <= (int)((kMemtrackNsyncStart + kMemtrackNsyncSize - 1) >> 16);
forceinline pureconst bool IsKmallocFrame(int x) {
return (int)(kMemtrackKmallocStart >> 16) <= x &&
x <= (int)((kMemtrackKmallocStart + kMemtrackKmallocSize - 1) >> 16);
}
forceinline pureconst bool IsShadowFrame(int x) {

View file

@ -1808,7 +1808,7 @@
6f900000-6f9fffff 64gb free
6fa00000-6fafffff 64gb free
6fb00000-6fbfffff 64gb free
6fc00004-6fcffffb 64gb nsync
6fc00004-6fcffffb 64gb kmalloc
6fd00004-6fdffffb 64gb zipos
6fe00004-6feffffb 64gb g_fds
6ff00004-70000003 64gb free

View file

@ -18,6 +18,7 @@
*/
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/thread/tls.h"
#include "libc/macros.internal.h"
.privileged
@ -26,7 +27,10 @@
// This is the same as fork() except it's optimized for the case
// where the caller invokes execve() immediately afterwards. You
// can also call functions like close(), dup2(), etc. Call _exit
// but don't call exit. Look for vforksafe function annotations.
// but don't call exit. Look for vforksafe function annotations,
// For example pthread mutexes are @vforksafe because they don't
// do anything in a vfork()'d child process. TLS memory must not
// be disabled (it's enabled by default) since vfork() needs it.
//
// Do not make the assumption that the parent is suspended until
// the child terminates since this impl calls fork() on Windows,
@ -34,8 +38,10 @@
//
// @return pid of child process or 0 if forked process
// @returnstwice
// @threadsafe
// @vforksafe
vfork: xor %edi,%edi # dwCreationFlags
vfork: call __require_tls
xor %edi,%edi # dwCreationFlags
#ifdef __SANITIZE_ADDRESS__
jmp fork # TODO: asan and vfork don't mix?
.endfn vfork,globl
@ -56,45 +62,31 @@ vfork: xor %edi,%edi # dwCreationFlags
ezlea .Llog,di
call __stracef
#endif /* SYSDEBUG */
mov __NR_vfork(%rip),%eax
mov __errno(%rip),%r8d # avoid question of @vforksafe errno
mov %fs:0,%r9 # get thread information block
mov 0x3c(%r9),%r8d # avoid question of @vforksafe errno
pop %rsi # saves return address in a register
mov __NR_vfork(%rip),%eax
#if SupportsBsd()
testb IsBsd()
jnz vfork.bsd
clc
#endif
syscall
#if SupportsBsd()
jnc 0f
neg %rax
0:
#endif
push %rsi # note it happens twice in same page
#if SupportsLinux()
cmp $-4095,%eax
jae systemfive_error
#endif
0: mov %r8d,__errno(%rip)
ezlea __vforked,di
mov %r8d,0x3c(%r9) # restore errno
test %eax,%eax
jz 1f
decl (%rdi)
jns 2f # openbsd doesn't actually share mem
1: incl (%rdi)
2: ret
jnz .Lprnt
.Lchld: orb $TIB_FLAG_VFORKED,0x40(%r9)
ret
.Lprnt: andb $~TIB_FLAG_VFORKED,0x40(%r9)
ret
.endfn vfork,globl
#if SupportsBsd()
vfork.bsd:
syscall
push %rsi
jc systemfive_errno
#if SupportsXnu()
testb IsXnu()
jz 0b
neg %edx # edx is 0 for parent and 1 for child
not %edx # eax always returned with childs pid
and %edx,%eax
#endif /* XNU */
jmp 0b
.endfn vfork.bsd
#endif /* BSD */
#ifdef SYSDEBUG
.rodata.str1.1
.Llog: .ascii STRACE_PROLOGUE

View file

@ -117,6 +117,8 @@ struct dirent_netbsd {
char d_name[512];
};
// TODO(jart): wipe these locks when forking
void _lockdir(DIR *dir) {
pthread_mutex_lock(&dir->lock);
}

View file

@ -38,6 +38,43 @@ void(__fflush_unlock)(void) {
pthread_mutex_unlock(&__fflush_lock_obj);
}
static void __stdio_fork_prepare(void) {
FILE *f;
__fflush_lock();
for (int i = 0; i < __fflush.handles.i; ++i) {
if ((f = __fflush.handles.p[i])) {
pthread_mutex_lock((pthread_mutex_t *)f->lock);
}
}
}
static void __stdio_fork_parent(void) {
FILE *f;
for (int i = __fflush.handles.i; i--;) {
if ((f = __fflush.handles.p[i])) {
pthread_mutex_unlock((pthread_mutex_t *)f->lock);
}
}
__fflush_unlock();
}
static void __stdio_fork_child(void) {
FILE *f;
pthread_mutex_t *m;
for (int i = __fflush.handles.i; i--;) {
if ((f = __fflush.handles.p[i])) {
m = (pthread_mutex_t *)f->lock;
bzero(m, sizeof(*m));
m->_type = PTHREAD_MUTEX_RECURSIVE;
}
}
pthread_mutex_init(&__fflush_lock_obj, 0);
}
__attribute__((__constructor__)) static void __stdio_init(void) {
pthread_atfork(__stdio_fork_prepare, __stdio_fork_parent, __stdio_fork_child);
}
/**
* Blocks until data from stream buffer is written out.
*

View file

@ -24,7 +24,7 @@
#include "libc/runtime/runtime.h"
#include "libc/stdio/posix_spawn.h"
#include "libc/stdio/posix_spawn.internal.h"
#include "libc/thread/thread.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/tls.h"
static int RunFileActions(struct _posix_faction *a) {
@ -54,7 +54,11 @@ static int RunFileActions(struct _posix_faction *a) {
}
/**
* Spawns process the POSIX way.
* Spawns process, the POSIX way.
*
* This function provides an API for vfork() that's intended to be less
* terrifying to the uninitiated, since it only lets you define actions
* that are @vforksafe. This function requires TLS not be disabled.
*
* @param pid if non-null shall be set to child pid on success
* @param path is resolved path of program which is not `$PATH` searched
@ -63,33 +67,40 @@ static int RunFileActions(struct _posix_faction *a) {
* @param envp is environment variables, or `environ` if null
* @return 0 on success or error number on failure
* @see posix_spawnp() for `$PATH` searching
* @tlsrequired
* @threadsafe
*/
int posix_spawn(int *pid, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp, char *const argv[],
char *const envp[]) {
int s, child;
sigset_t allsigs;
struct sigaction dfl;
if (!(child = _weaken(pthread_create) ? fork() : vfork())) {
short flags = 0;
sigset_t sigmask;
int s, child, policy;
struct sched_param param;
struct sigaction dfl = {0};
if (!(child = vfork())) {
if (attrp && *attrp) {
if ((*attrp)->flags & POSIX_SPAWN_SETPGROUP) {
if (setpgid(0, (*attrp)->pgroup)) _Exit(127);
posix_spawnattr_getflags(attrp, &flags);
if (flags & POSIX_SPAWN_SETPGROUP) {
if (setpgid(0, (*attrp)->pgroup)) {
_Exit(127);
}
}
if ((*attrp)->flags & POSIX_SPAWN_SETSIGMASK) {
sigprocmask(SIG_SETMASK, &(*attrp)->sigmask, 0);
if (flags & POSIX_SPAWN_SETSIGMASK) {
posix_spawnattr_getsigmask(attrp, &sigmask);
sigprocmask(SIG_SETMASK, &sigmask, 0);
}
if ((*attrp)->flags & POSIX_SPAWN_RESETIDS) {
if (flags & POSIX_SPAWN_RESETIDS) {
setuid(getuid());
setgid(getgid());
}
if ((*attrp)->flags & POSIX_SPAWN_SETSIGDEF) {
dfl.sa_handler = SIG_DFL;
dfl.sa_flags = 0;
sigfillset(&allsigs);
for (s = 0; sigismember(&allsigs, s); s++) {
if (flags & POSIX_SPAWN_SETSIGDEF) {
for (s = 1; s < 32; s++) {
if (sigismember(&(*attrp)->sigdefault, s)) {
if (sigaction(s, &dfl, 0) == -1) _Exit(127);
if (sigaction(s, &dfl, 0) == -1) {
_Exit(127);
}
}
}
}
@ -100,15 +111,17 @@ int posix_spawn(int *pid, const char *path,
}
}
if (attrp && *attrp) {
if ((*attrp)->flags & POSIX_SPAWN_SETSCHEDULER) {
if (sched_setscheduler(0, (*attrp)->schedpolicy,
&(*attrp)->schedparam) == -1) {
if (errno != ENOSYS) _Exit(127);
if (flags & POSIX_SPAWN_SETSCHEDULER) {
posix_spawnattr_getschedpolicy(attrp, &policy);
posix_spawnattr_getschedparam(attrp, &param);
if (sched_setscheduler(0, policy, &param) == -1) {
_Exit(127);
}
}
if ((*attrp)->flags & POSIX_SPAWN_SETSCHEDPARAM) {
if (sched_setparam(0, &(*attrp)->schedparam) == -1) {
if (errno != ENOSYS) _Exit(127);
if (flags & POSIX_SPAWN_SETSCHEDPARAM) {
posix_spawnattr_getschedparam(attrp, &param);
if (sched_setparam(0, &param) == -1) {
_Exit(127);
}
}
}

View file

@ -16,8 +16,10 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigset.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/mem/mem.h"
#include "libc/stdio/posix_spawn.h"
@ -32,19 +34,15 @@
* @raise ENOMEM if we require more vespene gas
*/
int posix_spawnattr_init(posix_spawnattr_t *attr) {
int e, rc;
int rc, e = errno;
struct _posix_spawna *a;
e = errno;
errno = 0;
if ((a = calloc(1, sizeof(*a)))) {
a->flags = 0;
a->pgroup = 0;
sigemptyset(&a->sigdefault);
a->schedpolicy = sched_getscheduler(0);
sched_getparam(0, &a->schedparam);
if ((a = calloc(1, sizeof(struct _posix_spawna)))) {
*attr = a;
rc = 0;
} else {
rc = errno;
errno = e;
}
rc = errno;
errno = e;
return rc;
}
@ -79,7 +77,9 @@ int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags) {
* Sets posix_spawn() flags.
*
* Setting these flags is needed in order for the other setters in this
* function to take effect.
* function to take effect. If a flag is known but unsupported by the
* host platform, it'll be silently removed from the flags. You can
* check for this by calling the getter afterwards.
*
* @param attr was initialized by posix_spawnattr_init()
* @param flags may have any of the following
@ -93,6 +93,9 @@ int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags) {
* @raise EINVAL if `flags` has invalid bits
*/
int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) {
if (!(IsLinux() || IsFreebsd() || IsNetbsd())) {
flags &= ~(POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER);
}
if (flags & ~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP |
POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK |
POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) {
@ -121,14 +124,23 @@ int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, int pgroup) {
* @param attr was initialized by posix_spawnattr_init()
* @param schedpolicy receives the result
* @return 0 on success, or errno on error
* @raise ENOSYS if platform support isn't available
*/
int posix_spawnattr_getschedpolicy(const posix_spawnattr_t *attr,
int *schedpolicy) {
if (!(*attr)->schedpolicy_isset) {
(*attr)->schedpolicy = sched_getscheduler(0);
(*attr)->schedpolicy_isset = true;
int rc, e = errno;
struct _posix_spawna *a = *(/*unconst*/ posix_spawnattr_t *)attr;
if (!a->schedpolicy_isset) {
rc = sched_getscheduler(0);
if (rc == -1) {
rc = errno;
errno = e;
return rc;
}
a->schedpolicy = rc;
a->schedpolicy_isset = true;
}
*schedpolicy = (*attr)->schedpolicy;
*schedpolicy = a->schedpolicy;
return 0;
}
@ -156,14 +168,22 @@ int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy) {
* @param attr was initialized by posix_spawnattr_init()
* @param schedparam receives the result
* @return 0 on success, or errno on error
* @raise ENOSYS if platform support isn't available
*/
int posix_spawnattr_getschedparam(const posix_spawnattr_t *attr,
struct sched_param *schedparam) {
if (!(*attr)->schedparam_isset) {
sched_getparam(0, &(*attr)->schedparam);
(*attr)->schedparam_isset = true;
int rc, e = errno;
struct _posix_spawna *a = *(/*unconst*/ posix_spawnattr_t *)attr;
if (!a->schedparam_isset) {
rc = sched_getparam(0, &a->schedparam);
if (rc == -1) {
rc = errno;
errno = e;
return rc;
}
a->schedparam_isset = true;
}
*schedparam = (*attr)->schedparam;
*schedparam = a->schedparam;
return 0;
}
@ -183,16 +203,32 @@ int posix_spawnattr_setschedparam(posix_spawnattr_t *attr,
return 0;
}
/**
* Gets signal mask for sigprocmask() in child process.
*
* If the setter wasn't called then this function will return the
* scheduling parameter of the current process.
*
* @return 0 on success, or errno on error
*/
int posix_spawnattr_getsigmask(const posix_spawnattr_t *attr,
sigset_t *sigmask) {
if (!(*attr)->sigmask_isset) {
sigprocmask(SIG_SETMASK, 0, &(*attr)->sigmask);
(*attr)->sigmask_isset = true;
struct _posix_spawna *a = *(/*unconst*/ posix_spawnattr_t *)attr;
if (!a->sigmask_isset) {
_npassert(!sigprocmask(SIG_SETMASK, 0, &a->sigmask));
a->sigmask_isset = true;
}
*sigmask = (*attr)->sigmask;
*sigmask = a->sigmask;
return 0;
}
/**
* Specifies signal mask for sigprocmask() in child process.
*
* Signal masks are inherited by default. Use this to change it.
*
* @return 0 on success, or errno on error
*/
int posix_spawnattr_setsigmask(posix_spawnattr_t *attr,
const sigset_t *sigmask) {
(*attr)->sigmask = *sigmask;
@ -200,12 +236,22 @@ int posix_spawnattr_setsigmask(posix_spawnattr_t *attr,
return 0;
}
/**
* Retrieves which signals will be restored to `SIG_DFL`.
*
* @return 0 on success, or errno on error
*/
int posix_spawnattr_getsigdefault(const posix_spawnattr_t *attr,
sigset_t *sigdefault) {
*sigdefault = (*attr)->sigdefault;
return 0;
}
/**
* Specifies which signals should be restored to `SIG_DFL`.
*
* @return 0 on success, or errno on error
*/
int posix_spawnattr_setsigdefault(posix_spawnattr_t *attr,
const sigset_t *sigdefault) {
(*attr)->sigdefault = *sigdefault;

View file

@ -166,6 +166,10 @@ int BLAKE2B256_Final(struct Blake2b *b2b,
* blake2b256 n=256 1 ns/byte 662 mb/s
* blake2b256 n=22851 1 ns/byte 683 mb/s
*
* @param data is binary memory to hash
* @param len is bytes in `data`
* @param out receives 32 byte binary digest
* @return 0 on success (always successful)
*/
int BLAKE2B256(const void *data, size_t len,
uint8_t out[BLAKE2B256_DIGEST_LENGTH]) {

View file

@ -16,20 +16,25 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/thread/semaphore.h"
#include "libc/thread/semaphore.internal.h"
#include "libc/str/str.h"
/**
* Removes named semaphore.
* Turns data into lowercase hex.
*
* @param name can be absolute path or should be component w/o slashes
* @return 0 on success, or -1 w/ errno
* @raise EPERM if pledge() is in play w/o `cpath` promise
* @raise ENOENT if named semaphore doesn't exist
* @raise EACCES if permission is denied
* This routine always writes a nul terminator, even if `n` is zero.
* There's no failure condition for this function.
*
* @param s must have `n*2+1` bytes
* @param p must have `n` bytes
* @param n is byte length of p
* @return pointer to nul byte in `s`
*/
int sem_unlink(const char *name) {
char path[PATH_MAX];
return unlink(__sem_name(name, path));
char *hexpcpy(char *restrict s, const void *restrict p, size_t n) {
const char *d, *e;
for (d = p, e = d + n; d < e; ++d) {
*s++ = "0123456789abcdef"[(*d >> 4) & 15];
*s++ = "0123456789abcdef"[(*d >> 0) & 15];
}
*s = 0;
return s;
}

View file

@ -46,6 +46,7 @@ void *memset(void *, int, size_t) memcpyesque;
void *memmove(void *, const void *, size_t) memcpyesque;
void *memcpy(void *restrict, const void *restrict, size_t) memcpyesque;
void *mempcpy(void *restrict, const void *restrict, size_t) memcpyesque;
char *hexpcpy(char *restrict, const void *restrict, size_t) memcpyesque;
void *memccpy(void *restrict, const void *restrict, int, size_t) memcpyesque;
void bcopy(const void *, void *, size_t) memcpyesque;
void explicit_bzero(void *, size_t);

View file

@ -1107,32 +1107,6 @@ syscon pf PF_VSOCK 40 0 0 0 0 0
syscon pf PF_WANPIPE 25 0 0 0 0 0
syscon pf PF_X25 9 0 0 0 0 0
# Eric Allman's exit() codes
#
# - Broadly supported style guideline;
# - Dating back to 1980 in 4.0BSD;
# - That won't be standardized.
#
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
syscon ex EX_OK 0 0 0 0 0 0 # consensus
syscon ex EX_USAGE 64 64 64 64 64 64 # unix consensus & force NT
syscon ex EX_DATAERR 65 65 65 65 65 65 # unix consensus & force NT
syscon ex EX_NOINPUT 66 66 66 66 66 66 # unix consensus & force NT
syscon ex EX_NOUSER 67 67 67 67 67 67 # unix consensus & force NT
syscon ex EX_NOHOST 68 68 68 68 68 68 # unix consensus & force NT
syscon ex EX_UNAVAILABLE 69 69 69 69 69 69 # unix consensus & force NT
syscon ex EX_SOFTWARE 70 70 70 70 70 70 # unix consensus & force NT
syscon ex EX_OSERR 71 71 71 71 71 71 # unix consensus & force NT
syscon ex EX_OSFILE 72 72 72 72 72 72 # unix consensus & force NT
syscon ex EX_CANTCREAT 73 73 73 73 73 73 # unix consensus & force NT
syscon ex EX_IOERR 74 74 74 74 74 74 # unix consensus & force NT
syscon ex EX_TEMPFAIL 75 75 75 75 75 75 # unix consensus & force NT
syscon ex EX_PROTOCOL 76 76 76 76 76 76 # unix consensus & force NT
syscon ex EX_NOPERM 77 77 77 77 77 77 # unix consensus & force NT
syscon ex EX_CONFIG 78 78 78 78 78 78 # unix consensus & force NT
syscon ex EX__BASE 64 64 64 64 64 64 # unix consensus & force NT
syscon ex EX__MAX 78 78 78 78 78 78 # unix consensus & force NT
# getdents() constants
#
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_CANTCREAT,73,73,73,73,73,73

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_CONFIG,78,78,78,78,78,78

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_NOHOST,68,68,68,68,68,68

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_NOINPUT,66,66,66,66,66,66

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_NOPERM,77,77,77,77,77,77

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_NOUSER,67,67,67,67,67,67

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_OK,0,0,0,0,0,0

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_OSERR,71,71,71,71,71,71

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_OSFILE,72,72,72,72,72,72

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_PROTOCOL,76,76,76,76,76,76

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_SOFTWARE,70,70,70,70,70,70

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_TEMPFAIL,75,75,75,75,75,75

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_UNAVAILABLE,69,69,69,69,69,69

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_USAGE,64,64,64,64,64,64

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX__BASE,64,64,64,64,64,64

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX__MAX,78,78,78,78,78,78

View file

@ -0,0 +1,2 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon limits,LINK_MAX,127,32767,32767,32767,32767,64

View file

@ -1,2 +1,2 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_DATAERR,65,65,65,65,65,65
.syscon limits,MAX_CANON,255,1024,255,255,255,255

View file

@ -1,2 +1,2 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon ex,EX_IOERR,74,74,74,74,74,74
.syscon limits,MAX_INPUT,255,1024,255,255,255,255

View file

@ -1,25 +1,29 @@
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_CPIO_H_
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_CPIO_H_
#define C_IXOTH 0000001
#define C_IWOTH 0000002
#define C_IROTH 0000004
#define C_IXGRP 0000010
#define C_IWGRP 0000020
#define C_IRGRP 0000040
#define C_IXUSR 0000100
#define C_IWUSR 0000200
#define C_IRUSR 0000400
#define C_ISVTX 0001000
#define C_ISGID 0002000
#define C_ISUID 0004000
#define C_ISFIFO 0010000
#define C_ISCHR 0020000
#define C_ISDIR 0040000
#define C_ISBLK 0060000
#define C_ISREG 0100000
#define C_ISCTG 0110000
#define C_ISLNK 0120000
#define MAGIC "070707"
#define C_IRUSR 000400
#define C_IWUSR 000200
#define C_IXUSR 000100
#define C_IRGRP 000040
#define C_IWGRP 000020
#define C_IXGRP 000010
#define C_IROTH 000004
#define C_IWOTH 000002
#define C_IXOTH 000001
#define C_ISUID 004000
#define C_ISGID 002000
#define C_ISVTX 001000
#define C_ISBLK 060000
#define C_ISCHR 020000
#define C_ISDIR 040000
#define C_ISFIFO 010000
#define C_ISSOCK 0140000
#define C_ISLNK 0120000
#define C_ISCTG 0110000
#define C_ISREG 0100000
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_CPIO_H_ */

View file

@ -1,48 +1,32 @@
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_EX_H_
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_EX_H_
#include "libc/runtime/symbolic.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const int EX_CANTCREAT;
extern const int EX_CONFIG;
extern const int EX_DATAERR;
extern const int EX_IOERR;
extern const int EX_NOHOST;
extern const int EX_NOINPUT;
extern const int EX_NOPERM;
extern const int EX_NOUSER;
extern const int EX_OK;
extern const int EX_OSERR;
extern const int EX_OSFILE;
extern const int EX_PROTOCOL;
extern const int EX_SOFTWARE;
extern const int EX_TEMPFAIL;
extern const int EX_UNAVAILABLE;
extern const int EX_USAGE;
extern const int EX__BASE;
extern const int EX__MAX;
/**
* @fileoverview Eric Allman's exit() codes
*
* - Broadly supported style guideline;
* - Dating back to 1980 in 4.0BSD;
* - That won't be standardized.
*
*/
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#define EX_CANTCREAT LITERALLY(73)
#define EX_CONFIG LITERALLY(78)
#define EX_DATAERR LITERALLY(65)
#define EX_IOERR LITERALLY(74)
#define EX_NOHOST LITERALLY(68)
#define EX_NOINPUT LITERALLY(66)
#define EX_NOPERM LITERALLY(77)
#define EX_NOUSER LITERALLY(67)
#define EX_OK LITERALLY(0)
#define EX_OSERR LITERALLY(71)
#define EX_OSFILE LITERALLY(72)
#define EX_PROTOCOL LITERALLY(76)
#define EX_SOFTWARE LITERALLY(70)
#define EX_TEMPFAIL LITERALLY(75)
#define EX_UNAVAILABLE LITERALLY(69)
#define EX_USAGE LITERALLY(64)
#define EX__BASE LITERALLY(64)
#define EX__MAX LITERALLY(78)
#define EX_CANTCREAT 73
#define EX_CONFIG 78
#define EX_DATAERR 65
#define EX_IOERR 74
#define EX_NOHOST 68
#define EX_NOINPUT 66
#define EX_NOPERM 77
#define EX_NOUSER 67
#define EX_OK 0
#define EX_OSERR 71
#define EX_OSFILE 72
#define EX_PROTOCOL 76
#define EX_SOFTWARE 70
#define EX_TEMPFAIL 75
#define EX_UNAVAILABLE 69
#define EX_USAGE 64
#define EX__BASE 64
#define EX__MAX 78
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_EX_H_ */

View file

@ -63,7 +63,7 @@ void testlib_finish(void) {
void testlib_error_enter(const char *file, const char *func) {
atomic_fetch_sub_explicit(&__ftrace, 1, memory_order_relaxed);
atomic_fetch_sub_explicit(&__strace, 1, memory_order_relaxed);
if (!__vforked) pthread_mutex_lock(&testlib_error_lock);
pthread_mutex_lock(&testlib_error_lock);
if (!IsWindows()) sys_getpid(); /* make strace easier to read */
if (!IsWindows()) sys_getpid();
if (g_testlib_shoulddebugbreak) {

View file

@ -1,6 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
#define COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
#include "libc/calls/struct/sched_param.h"
#include "libc/calls/struct/sigset.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
@ -15,6 +16,8 @@ COSMOPOLITAN_C_START_
* @fileoverview Cosmopolitan POSIX Thread Internals
*/
typedef void (*atfork_f)(void);
// LEGAL TRANSITIONS ┌──> TERMINATED
// pthread_create ─┬─> JOINABLE ─┴┬─> DETACHED ──> ZOMBIE
// └──────────────┘
@ -73,6 +76,7 @@ struct PosixThread {
struct CosmoTib *tib; // middle of tls allocation
jmp_buf exiter; // for pthread_exit
pthread_attr_t attr;
sigset_t sigmask;
struct _pthread_cleanup_buffer *cleanup;
};
@ -82,8 +86,7 @@ hidden extern uint64_t _pthread_key_usage[(PTHREAD_KEYS_MAX + 63) / 64];
hidden extern pthread_key_dtor _pthread_key_dtor[PTHREAD_KEYS_MAX];
hidden extern _Thread_local void *_pthread_keys[PTHREAD_KEYS_MAX];
void _pthread_atfork(int) hidden;
void _pthread_funlock(pthread_mutex_t *, int) hidden;
int _pthread_atfork(atfork_f, atfork_f, atfork_f) hidden;
int _pthread_reschedule(struct PosixThread *) hidden;
int _pthread_setschedparam_freebsd(int, int, const struct sched_param *) hidden;
void _pthread_free(struct PosixThread *) hidden;
@ -91,9 +94,15 @@ void _pthread_cleanup(struct PosixThread *) hidden;
void _pthread_ungarbage(void) hidden;
void _pthread_wait(struct PosixThread *) hidden;
void _pthread_zombies_add(struct PosixThread *) hidden;
void _pthread_zombies_purge(void) hidden;
void _pthread_zombies_decimate(void) hidden;
void _pthread_zombies_harvest(void) hidden;
void _pthread_key_destruct(void *[PTHREAD_KEYS_MAX]) hidden;
void _pthread_key_lock(void) hidden;
void _pthread_key_unlock(void) hidden;
void _pthread_onfork_prepare(void) hidden;
void _pthread_onfork_parent(void) hidden;
void _pthread_onfork_child(void) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -0,0 +1,79 @@
/*-*- 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/intrin/kmalloc.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
static struct AtForks {
pthread_mutex_t lock;
struct AtFork {
struct AtFork *p[2];
atfork_f f[3];
} * list;
} _atforks;
static void _pthread_onfork(int i) {
struct AtFork *a;
struct PosixThread *pt;
_unassert(0 <= i && i <= 2);
if (!i) pthread_mutex_lock(&_atforks.lock);
for (a = _atforks.list; a; a = a->p[!i]) {
if (a->f[i]) a->f[i]();
_atforks.list = a;
}
if (i) pthread_mutex_unlock(&_atforks.lock);
if (i == 2) {
_pthread_zombies_purge();
if (__tls_enabled) {
pt = (struct PosixThread *)__get_tls()->tib_pthread;
pt->flags |= PT_MAINTHREAD;
}
}
}
void _pthread_onfork_prepare(void) {
_pthread_onfork(0);
}
void _pthread_onfork_parent(void) {
_pthread_onfork(1);
}
void _pthread_onfork_child(void) {
_pthread_onfork(2);
}
int _pthread_atfork(atfork_f prepare, atfork_f parent, atfork_f child) {
int rc;
struct AtFork *a;
a = kmalloc(sizeof(struct AtFork));
a->f[0] = prepare;
a->f[1] = parent;
a->f[2] = child;
pthread_mutex_lock(&_atforks.lock);
a->p[0] = 0;
a->p[1] = _atforks.list;
if (_atforks.list) _atforks.list->p[0] = a;
_atforks.list = a;
pthread_mutex_unlock(&_atforks.lock);
rc = 0;
return rc;
}

View file

@ -21,6 +21,7 @@
#include "libc/calls/sched-sysv.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigaltstack.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
@ -41,6 +42,7 @@
#include "libc/sysv/consts/clone.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/ss.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
@ -52,6 +54,7 @@
STATIC_YOINK("nsync_mu_lock");
STATIC_YOINK("nsync_mu_unlock");
STATIC_YOINK("_pthread_atfork");
#define MAP_ANON_OPENBSD 0x1000
#define MAP_STACK_OPENBSD 0x4000
@ -76,30 +79,6 @@ void _pthread_free(struct PosixThread *pt) {
free(pt);
}
void _pthread_funlock(pthread_mutex_t *mu, int parent_tid) {
if (mu->_type == PTHREAD_MUTEX_NORMAL ||
(atomic_load_explicit(&mu->_lock, memory_order_relaxed) &&
mu->_owner != parent_tid)) {
atomic_store_explicit(&mu->_lock, 0, memory_order_relaxed);
mu->_nsync = 0;
mu->_depth = 0;
mu->_owner = 0;
}
}
void _pthread_atfork(int parent_tid) {
FILE *f;
if (_weaken(dlmalloc_atfork)) _weaken(dlmalloc_atfork)();
_pthread_funlock(&__fds_lock_obj, parent_tid);
_pthread_funlock(&__sig_lock_obj, parent_tid);
_pthread_funlock(&__fflush_lock_obj, parent_tid);
for (int i = 0; i < __fflush.handles.i; ++i) {
if ((f = __fflush.handles.p[i])) {
_pthread_funlock((pthread_mutex_t *)f->lock, parent_tid);
}
}
}
static int PosixThread(void *arg, int tid) {
struct PosixThread *pt = arg;
enum PosixThreadStatus status;
@ -118,6 +97,7 @@ static int PosixThread(void *arg, int tid) {
// set long jump handler so pthread_exit can bring control back here
if (!setjmp(pt->exiter)) {
__get_tls()->tib_pthread = (pthread_t)pt;
sigprocmask(SIG_SETMASK, &pt->sigmask, 0);
pt->rc = pt->start_routine(pt->arg);
// ensure pthread_cleanup_pop(), and pthread_exit() popped cleanup
_npassert(!pt->cleanup);
@ -158,62 +138,12 @@ static int FixupCustomStackOnOpenbsd(pthread_attr_t *attr) {
}
}
/**
* 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);
* }
*
* Here's the OSI model of threads in Cosmopolitan:
*
*
* pthread_create() - Standard
* Abstraction
*
* clone() - Polyfill
*
* - Kernel
* Interfaces
* sys_clone tfork
* CreateThread
*
* bsdthread_create thr_new
*
*
* _lwp_create
*
*
* @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
* @returnserrno
* @threadsafe
*/
errno_t pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg) {
static errno_t pthread_create_impl(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg,
sigset_t oldsigs) {
int rc, e = errno;
struct PosixThread *pt;
__require_tls();
_pthread_zombies_decimate();
// create posix thread object
if (!(pt = calloc(1, sizeof(struct PosixThread)))) {
@ -323,6 +253,7 @@ errno_t pthread_create(pthread_t *thread, const pthread_attr_t *attr,
}
// launch PosixThread(pt) in new thread
pt->sigmask = oldsigs;
if (clone(PosixThread, pt->attr.__stackaddr,
pt->attr.__stacksize - (IsOpenbsd() ? 16 : 0),
CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
@ -340,3 +271,66 @@ errno_t pthread_create(pthread_t *thread, const pthread_attr_t *attr,
}
return 0;
}
/**
* 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);
* }
*
* Here's the OSI model of threads in Cosmopolitan:
*
*
* pthread_create() - Standard
* Abstraction
*
* clone() - Polyfill
*
* - Kernel
* Interfaces
* sys_clone tfork
* CreateThread
*
* bsdthread_create thr_new
*
*
* _lwp_create
*
*
* @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
* @returnserrno
* @threadsafe
*/
errno_t pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg) {
errno_t rc;
sigset_t blocksigs, oldsigs;
__require_tls();
_pthread_zombies_decimate();
sigfillset(&blocksigs);
_npassert(!sigprocmask(SIG_SETMASK, &blocksigs, &oldsigs));
rc = pthread_create_impl(thread, attr, start_routine, arg, oldsigs);
_npassert(!sigprocmask(SIG_SETMASK, &oldsigs, 0));
return rc;
}

View file

@ -23,9 +23,7 @@
#include "libc/thread/spawn.h"
#include "libc/thread/thread.h"
/**
* @fileoverview Memory collector for detached threads.
*/
// TODO(jart): track all threads, not just zombies
static struct Zombie {
struct Zombie *next;
@ -70,6 +68,16 @@ void _pthread_zombies_harvest(void) {
}
}
void _pthread_zombies_purge(void) {
struct Zombie *z, *n;
while ((z = atomic_load_explicit(&_pthread_zombies, memory_order_relaxed))) {
_pthread_free(z->pt);
n = z->next;
free(z);
atomic_store_explicit(&_pthread_zombies, n, memory_order_relaxed);
}
}
__attribute__((__constructor__)) static void init(void) {
atexit(_pthread_zombies_harvest);
}

View file

@ -16,17 +16,36 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/intrin/atomic.h"
#include "libc/limits.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/semaphore.h"
/**
* Destroys unnamed semaphore.
*
* If `sem` was successfully initialized by sem_init() then
* sem_destroy() may be called multiple times, before it may be
* reinitialized again by calling sem_init().
*
* Calling sem_destroy() on a semaphore created by sem_open() has
* undefined behavior. Using `sem` after calling sem_destroy() is
* undefined behavior that will cause semaphore APIs to either crash or
* raise `EINVAL` until `sem` is passed to sem_init() again.
*
* @param sem was created by sem_init()
* @return 0 on success, or -1 w/ errno
* @raise EINVAL if `sem` wasn't valid
* @raise EBUSY if `sem` has waiters
*/
int sem_destroy(sem_t *sem) {
int waiters;
_npassert(sem->sem_magic != SEM_MAGIC_NAMED);
if (sem->sem_magic != SEM_MAGIC_UNNAMED) return einval();
waiters = atomic_load_explicit(&sem->sem_waiters, memory_order_relaxed);
_unassert(waiters >= 0);
if (waiters) return ebusy();
atomic_store_explicit(&sem->sem_value, INT_MIN, memory_order_relaxed);
return 0;
}

View file

@ -16,6 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/intrin/atomic.h"
#include "libc/thread/semaphore.h"
@ -27,6 +29,7 @@
* @return 0 on success, or -1 w/ errno
*/
int sem_getvalue(sem_t *sem, int *sval) {
_unassert(sem->sem_pshared || sem->sem_pid == getpid());
*sval = atomic_load_explicit(&sem->sem_value, memory_order_relaxed);
return 0;
}

View file

@ -16,6 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/intrin/atomic.h"
#include "libc/limits.h"
#include "libc/sysv/errfuns.h"
@ -25,15 +27,25 @@
/**
* Initializes unnamed semaphore.
*
* Calling sem_init() on an already initialized semaphore is undefined.
*
* @param sem should make its way to sem_destroy() if this succeeds
* @param pshared if semaphore may be shared between processes
* @param pshared if semaphore may be shared between processes, provided
* `sem` is backed by `mmap(MAP_ANONYMOUS | MAP_SHARED)` memory
* @param value is initial count of semaphore
* @return 0 on success, or -1 w/ errno
* @raise EINVAL if `value` exceeds `SEM_VALUE_MAX`
* @raise EPERM on OpenBSD if `pshared` is true
*/
int sem_init(sem_t *sem, int pshared, unsigned value) {
if (value > SEM_VALUE_MAX) return einval();
// OpenBSD MAP_ANONYMOUS|MAP_SHARED memory is kind of busted.
// The OpenBSD implementation of sem_init() also EPERMs here.
if (IsOpenbsd() && pshared) return eperm();
sem->sem_magic = SEM_MAGIC_UNNAMED;
atomic_store_explicit(&sem->sem_value, value, memory_order_relaxed);
sem->sem_pshared = pshared;
sem->sem_pshared = !!pshared;
sem->sem_pid = getpid();
sem->sem_waiters = 0;
return 0;
}

View file

@ -18,60 +18,298 @@
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/nopl.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/limits.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/semaphore.h"
#include "libc/thread/semaphore.internal.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
/**
* Initializes and opens named semaphore.
*
* @param name can be absolute path or should be component w/o slashes
* @param oflga can have `O_CREAT` and/or `O_EXCL`
* @return semaphore object which needs sem_close(), or SEM_FAILED w/ errno
* @raise ENOTDIR if a directory component in `name` exists as non-directory
* @raise ENAMETOOLONG if symlink-resolved `name` length exceeds `PATH_MAX`
* @raise ENAMETOOLONG if component in `name` exists longer than `NAME_MAX`
* @raise ELOOP if `flags` had `O_NOFOLLOW` and `name` is a symbolic link
* @raise ENOSPC if file system is full when `name` would be `O_CREAT`ed
* @raise ELOOP if a loop was detected resolving components of `name`
* @raise EEXIST if `O_CREAT|O_EXCL` is used and semaphore exists
* @raise EACCES if we didn't have permission to create semaphore
* @raise EMFILE if process `RLIMIT_NOFILE` has been reached
* @raise ENFILE if system-wide file limit has been reached
* @raise EINTR if signal was delivered instead
*/
sem_t *sem_open(const char *name, int oflag, ...) {
static struct Semaphores {
pthread_once_t once;
pthread_mutex_t lock;
struct Semaphore {
struct Semaphore *next;
sem_t *sem;
char *path;
bool dead;
int refs;
} * list;
} g_semaphores;
static void sem_open_lock(void) {
pthread_mutex_lock(&g_semaphores.lock);
}
static void sem_open_unlock(void) {
pthread_mutex_unlock(&g_semaphores.lock);
}
static void sem_open_funlock(void) {
pthread_mutex_init(&g_semaphores.lock, 0);
}
static void sem_open_setup(void) {
sem_open_funlock();
pthread_atfork(sem_open_lock, sem_open_unlock, sem_open_funlock);
}
static void sem_open_init(void) {
pthread_once(&g_semaphores.once, sem_open_setup);
}
#ifdef _NOPL0
#define sem_open_lock() _NOPL0("__threadcalls", sem_open_lock)
#define sem_open_unlock() _NOPL0("__threadcalls", sem_open_unlock)
#endif
static sem_t *sem_open_impl(const char *path, int oflag, unsigned mode,
unsigned value) {
int fd;
sem_t *sem;
va_list va;
unsigned mode;
char path[PATH_MAX];
va_start(va, oflag);
mode = va_arg(va, unsigned);
va_end(va);
struct stat st;
oflag |= O_RDWR | O_CLOEXEC;
if ((fd = openat(AT_FDCWD, __sem_name(name, path), oflag, mode)) == -1) {
if ((fd = openat(AT_FDCWD, path, oflag, mode)) == -1) {
return SEM_FAILED;
}
if (ftruncate(fd, sizeof(sem_t)) == -1) {
_npassert(!fstat(fd, &st));
if (st.st_size < PAGESIZE && ftruncate(fd, PAGESIZE) == -1) {
_npassert(!close(fd));
return SEM_FAILED;
}
sem = mmap(0, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
sem = mmap(0, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (sem != MAP_FAILED) {
atomic_store_explicit(&sem->sem_value, value, memory_order_relaxed);
sem->sem_magic = SEM_MAGIC_NAMED;
sem->sem_dev = st.st_dev;
sem->sem_ino = st.st_ino;
sem->sem_pshared = true;
} else {
sem = SEM_FAILED;
}
_npassert(!close(fd));
return sem;
}
static struct Semaphore *sem_open_find(const char *path) {
struct Semaphore *s;
for (s = g_semaphores.list; s; s = s->next) {
if (!strcmp(path, s->path)) {
return s;
}
}
return 0;
}
static struct Semaphore *sem_open_reopen(const char *path) {
int e = errno;
struct stat st;
struct Semaphore *s;
for (s = g_semaphores.list; s; s = s->next) {
if (!s->dead && //
!strcmp(path, s->path)) {
if (!fstatat(AT_FDCWD, path, &st, 0) && //
st.st_dev == s->sem->sem_dev && //
st.st_ino == s->sem->sem_ino) {
return s;
} else {
errno = e;
s->dead = true;
}
}
}
return 0;
}
static struct Semaphore *sem_open_get(const sem_t *sem,
struct Semaphore ***out_prev) {
struct Semaphore *s, *t, **p;
for (p = &g_semaphores.list, s = *p; s; p = &s->next, s = s->next) {
if (s && sem == s->sem) {
*out_prev = p;
return s;
}
}
return 0;
}
/**
* Initializes and opens named semaphore.
*
* This function tracks open semaphore objects within a process. When a
* process calls sem_open() multiple times with the same name, then the
* same shared memory address will be returned, unless it was unlinked.
*
* @param name is arbitrary string that begins with a slash character
* @param oflag can have any of:
* - `O_CREAT` to create the named semaphore if it doesn't exist,
* in which case two additional arguments must be supplied
* - `O_EXCL` to raise `EEXIST` if semaphore already exists
* @param mode is octal mode bits, required if `oflag & O_CREAT`
* @param value is initial of semaphore, required if `oflag & O_CREAT`
* @return semaphore object which needs sem_close(), or SEM_FAILED w/ errno
* @raise ENOSPC if file system is full when `name` would be `O_CREAT`ed
* @raise EINVAL if `oflag` has bits other than `O_CREAT | O_EXCL`
* @raise EINVAL if `value` is negative or exceeds `SEM_VALUE_MAX`
* @raise EEXIST if `O_CREAT|O_EXCL` is used and semaphore exists
* @raise EACCES if we didn't have permission to create semaphore
* @raise EACCES if recreating open semaphore pending an unlink
* @raise EMFILE if process `RLIMIT_NOFILE` has been reached
* @raise ENFILE if system-wide file limit has been reached
* @raise ENOMEM if we require more vespene gas
* @raise EINTR if signal handler was called
* @threadsafe
*/
sem_t *sem_open(const char *name, int oflag, ...) {
sem_t *sem;
va_list va;
struct Semaphore *s;
unsigned mode = 0, value = 0;
char *path, pathbuf[PATH_MAX];
if (oflag & ~(O_CREAT | O_EXCL)) {
einval();
return SEM_FAILED;
}
if (oflag & O_CREAT) {
va_start(va, oflag);
mode = va_arg(va, unsigned);
value = va_arg(va, unsigned);
va_end(va);
if (value > SEM_VALUE_MAX) {
einval();
return SEM_FAILED;
}
}
if (!(path = sem_path_np(name, pathbuf, sizeof(pathbuf)))) {
return SEM_FAILED;
}
sem_open_init();
sem_open_lock();
if ((s = sem_open_reopen(path))) {
if (s->sem->sem_lazydelete) {
if (oflag & O_CREAT) {
eacces();
} else {
enoent();
}
sem = SEM_FAILED;
} else if (~oflag & O_EXCL) {
sem = s->sem;
atomic_fetch_add_explicit(&sem->sem_prefs, 1, memory_order_acquire);
++s->refs;
} else {
eexist();
sem = SEM_FAILED;
}
} else if ((s = calloc(1, sizeof(struct Semaphore)))) {
if ((s->path = strdup(path))) {
if ((sem = sem_open_impl(path, oflag, mode, value)) != SEM_FAILED) {
atomic_fetch_add_explicit(&sem->sem_prefs, 1, memory_order_relaxed);
s->next = g_semaphores.list;
s->sem = sem;
s->refs = 1;
g_semaphores.list = s;
} else {
free(s->path);
free(s);
}
} else {
free(s);
sem = SEM_FAILED;
}
} else {
sem = SEM_FAILED;
}
sem_open_unlock();
return sem;
}
/**
* Closes named semaphore.
*
* Calling sem_close() on a semaphore not created by sem_open() has
* undefined behavior. Using `sem` after calling sem_close() from either
* the current process or forked processes sharing the same address is
* also undefined behavior. If any threads in this process or forked
* children are currently blocked on `sem` then calling sem_close() has
* undefined behavior.
*
* @param sem was created with sem_open()
* @return 0 on success, or -1 w/ errno
*/
int sem_close(sem_t *sem) {
int rc, prefs;
bool unmap, delete;
struct Semaphore *s, **p;
_npassert(sem->sem_magic == SEM_MAGIC_NAMED);
sem_open_init();
sem_open_lock();
_npassert((s = sem_open_get(sem, &p)));
prefs = atomic_fetch_add_explicit(&sem->sem_prefs, -1, memory_order_release);
_npassert(s->refs > 0);
if ((unmap = !--s->refs)) {
_npassert(prefs > 0);
delete = sem->sem_lazydelete && prefs == 1;
*p = s->next;
} else {
_npassert(prefs > 1);
delete = false;
}
sem_open_unlock();
if (unmap) {
_npassert(!munmap(sem, PAGESIZE));
}
if (delete) {
rc = unlink(s->path);
}
if (unmap) {
free(s->path);
free(s);
}
return 0;
}
/**
* Removes named semaphore.
*
* This causes the file resource to be deleted. If processes have this
* semaphore currently opened, then on platforms like Windows deletion
* may be postponed until the last process calls sem_close().
*
* @param name can be absolute path or should be component w/o slashes
* @return 0 on success, or -1 w/ errno
* @raise ACCESS if Windows is being fussy about deleting open files
* @raise EPERM if pledge() is in play w/o `cpath` promise
* @raise ENOENT if named semaphore doesn't exist
* @raise EACCES if permission is denied
* @raise ENAMETOOLONG if too long
*/
int sem_unlink(const char *name) {
int rc, e = errno;
struct Semaphore *s;
char *path, pathbuf[PATH_MAX];
if (!(path = sem_path_np(name, pathbuf, sizeof(pathbuf)))) return -1;
if ((rc = unlink(path)) == -1 && IsWindows() && errno == EACCES) {
sem_open_init();
sem_open_lock();
if ((s = sem_open_find(path))) {
s->sem->sem_lazydelete = true;
errno = e;
rc = 0;
}
sem_open_unlock();
}
return rc;
}

53
libc/thread/sem_path_np.c Normal file
View file

@ -0,0 +1,53 @@
/*-*- 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/str/blake2.h"
#include "libc/str/path.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
/**
* Returns filesystem pathname of named semaphore.
*
* @param name is `name` of semaphore which should begin with slash
* @param buf is temporary storage with at least `size` bytes
* @param size is size of `buf` in bytes
* @return pointer to file system path
* @raise ENAMETOOLONG if constructed path would exceed `size`
*/
const char *sem_path_np(const char *name, char *buf, size_t size) {
char *p;
unsigned n;
const char *path, *a;
uint8_t digest[BLAKE2B256_DIGEST_LENGTH];
a = "/tmp/", n = 5;
if (IsLinux()) a = "/dev/shm/", n = 9;
if (n + BLAKE2B256_DIGEST_LENGTH * 2 + 4 < size) {
BLAKE2B256(name, strlen(name), digest);
p = mempcpy(buf, a, n);
p = hexpcpy(p, digest, BLAKE2B256_DIGEST_LENGTH);
p = mempcpy(p, ".sem", 5);
path = buf;
} else {
enametoolong();
path = 0;
}
return path;
}

View file

@ -17,9 +17,10 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/strace.internal.h"
#include "libc/limits.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/semaphore.h"
#include "third_party/nsync/futex.internal.h"
@ -31,14 +32,17 @@
* @raise EINVAL if `sem` isn't valid
*/
int sem_post(sem_t *sem) {
int rc;
int old = atomic_fetch_add_explicit(&sem->sem_value, 1, memory_order_relaxed);
int rc, old, wakeups;
_unassert(sem->sem_pshared || sem->sem_pid == getpid());
old = atomic_fetch_add_explicit(&sem->sem_value, 1, memory_order_relaxed);
_unassert(old > INT_MIN);
if (old >= 0) {
_npassert(nsync_futex_wake_(&sem->sem_value, 1, sem->sem_pshared) >= 0);
wakeups = nsync_futex_wake_(&sem->sem_value, 1, sem->sem_pshared);
_npassert(wakeups >= 0);
rc = 0;
} else {
wakeups = 0;
rc = einval();
}
STRACE("sem_post(%p) → %d% m", sem, rc);
return rc;
}

View file

@ -21,10 +21,9 @@
#include "libc/calls/struct/timespec.internal.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/strace.internal.h"
#include "libc/limits.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/semaphore.h"
#include "libc/thread/semaphore.internal.h"
#include "third_party/nsync/futex.internal.h"
static void sem_delay(int n) {
@ -79,6 +78,9 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime) {
}
}
_unassert(atomic_fetch_add_explicit(&sem->sem_waiters, +1,
memory_order_acquire) >= 0);
do {
if (!(v = atomic_load_explicit(&sem->sem_value, memory_order_relaxed))) {
rc = nsync_futex_wait_(&sem->sem_value, v, sem->sem_pshared,
@ -101,13 +103,15 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime) {
} else if (v > 0) {
rc = 0;
} else {
_unassert(v > INT_MIN);
rc = einval();
}
} while (!rc && (!v || !atomic_compare_exchange_weak_explicit(
&sem->sem_value, &v, v - 1, memory_order_acquire,
memory_order_relaxed)));
STRACE("sem_timedwait(%p, %s) → %d% m", sem, DescribeTimespec(0, abstime),
rc);
_unassert(atomic_fetch_add_explicit(&sem->sem_waiters, -1,
memory_order_release) > 0);
return rc;
}

View file

@ -17,8 +17,10 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/limits.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/semaphore.h"
@ -32,8 +34,10 @@
*/
int sem_trywait(sem_t *sem) {
int v;
_unassert(sem->sem_pshared || sem->sem_pid == getpid());
v = atomic_load_explicit(&sem->sem_value, memory_order_relaxed);
do {
_unassert(v > INT_MIN);
if (!v) return eagain();
if (v < 0) return einval();
} while (!atomic_compare_exchange_weak_explicit(

View file

@ -4,12 +4,21 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define SEM_FAILED ((sem_t *)0)
#define SEM_FAILED ((sem_t *)0)
#define SEM_MAGIC_NAMED 0xDEADBEEFu
#define SEM_MAGIC_UNNAMED 0xFEEDABEEu
typedef struct {
union {
struct {
_Atomic(int) sem_value;
_Atomic(int) sem_waiters;
_Atomic(int) sem_prefs; /* named only */
unsigned sem_magic;
int64_t sem_dev; /* named only */
int64_t sem_ino; /* named only */
int sem_pid; /* unnamed only */
bool sem_lazydelete; /* named only */
bool sem_pshared;
};
void *sem_space[32];
@ -26,6 +35,7 @@ int sem_getvalue(sem_t *, int *);
sem_t *sem_open(const char *, int, ...);
int sem_close(sem_t *);
int sem_unlink(const char *);
const char *sem_path_np(const char *, char *, size_t);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -1,10 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_THREAD_SEMAPHORE_INTERNAL_H_
#define COSMOPOLITAN_LIBC_THREAD_SEMAPHORE_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
const char *__sem_name(const char *, char[hasatleast PATH_MAX]) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_THREAD_SEMAPHORE_INTERNAL_H_ */

View file

@ -63,7 +63,7 @@ typedef struct pthread_mutex_s {
unsigned _pshared : 1;
unsigned _depth : 8;
unsigned _owner : 21;
void *_nsync;
long _pid;
} pthread_mutex_t;
typedef struct pthread_mutexattr_s {
@ -149,6 +149,7 @@ int pthread_mutexattr_gettype(const pthread_mutexattr_t *, int *);
int pthread_mutexattr_settype(pthread_mutexattr_t *, int);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *, int);
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *, int *);
int pthread_atfork(void (*)(void), void (*)(void), void (*)(void));
int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *);
int pthread_mutex_lock(pthread_mutex_t *);
int pthread_mutex_unlock(pthread_mutex_t *);

View file

@ -4,6 +4,8 @@
#define TLS_ALIGNMENT 64
#define TIB_FLAG_TIME_CRITICAL 1
#define TIB_FLAG_VFORKED 2
#define TIB_FLAG_WINCRASHING 4
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -24,8 +26,8 @@ struct CosmoTib {
struct CosmoTib *tib_self2; /* 0x30 */
_Atomic(int32_t) tib_tid; /* 0x38 */
int32_t tib_errno; /* 0x3c */
uint64_t tib_flags; /* 0x40 */
void *tib_nsync;
uint64_t tib_flags;
uint64_t tib_sigmask;
void *tib_reserved3;
void *tib_reserved4;

View file

@ -5,12 +5,12 @@
#include "libc/calls/calls.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/nopl.internal.h"
#include "libc/thread/thread.h"
#include "libc/mem/mem.h"
#include "libc/thread/tls.h"
#include "libc/mem/gc.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
#include "libc/time/tz.internal.h"
@ -47,21 +47,28 @@ STATIC_YOINK("usr/share/zoneinfo/UTC");
static pthread_mutex_t locallock;
int localtime_lock(void) {
void localtime_lock(void) {
pthread_mutex_lock(&locallock);
return 0;
}
void localtime_unlock(void) {
pthread_mutex_unlock(&locallock);
}
void localtime_funlock(void) {
pthread_mutex_init(&locallock, 0);
}
__attribute__((__constructor__)) static void localtime_init(void) {
localtime_funlock();
pthread_atfork(localtime_lock,
localtime_unlock,
localtime_funlock);
}
#ifdef _NOPL0
#define localtime_lock() _NOPL0("__threadcalls", localtime_lock)
#define localtime_unlock() _NOPL0("__threadcalls", localtime_unlock)
#else
#define localtime_lock() (__threaded ? localtime_lock() : 0)
#define localtime_unlock() (__threaded ? localtime_unlock() : 0)
#endif
#ifndef TZ_ABBR_MAX_LEN

View file

@ -28,3 +28,12 @@ void(__zipos_lock)(void) {
void(__zipos_unlock)(void) {
pthread_mutex_unlock(&__zipos_lock_obj);
}
void __zipos_funlock(void) {
pthread_mutex_init(&__zipos_lock_obj, 0);
}
__attribute__((__constructor__)) static void __zipos_init(void) {
__zipos_funlock();
pthread_atfork(__zipos_lock, __zipos_unlock, __zipos_funlock);
}

Some files were not shown because too many files have changed in this diff Show more