mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 15:38:22 +00:00
Implement pthread_atfork()
If threads are being used, then fork() will now acquire and release and runtime locks so that fork() may be safely used from threads. This also makes vfork() thread safe, because pthread mutexes will do nothing when the process is a child of vfork(). More torture tests have been written to confirm this all works like a charm. Additionally: - Invent hexpcpy() api - Rename nsync_malloc_() to kmalloc() - Complete posix named semaphore implementation - Make pthread_create() asynchronous signal safe - Add rm, rmdir, and touch to command interpreter builtins - Invent sigisprecious() and modify sigset functions to use it - Add unit tests for posix_spawn() attributes and fix its bugs One unresolved problem is the reclaiming of *NSYNC waiter memory in the forked child processes, within apps which have threads waiting on locks
This commit is contained in:
parent
64c284003d
commit
60cb435cb4
124 changed files with 2169 additions and 718 deletions
1
Makefile
1
Makefile
|
@ -92,6 +92,7 @@ o/$(MODE): \
|
||||||
.UNVEIL = \
|
.UNVEIL = \
|
||||||
libc/integral \
|
libc/integral \
|
||||||
libc/disclaimer.inc \
|
libc/disclaimer.inc \
|
||||||
|
rwc:/dev/shm \
|
||||||
rx:build/bootstrap \
|
rx:build/bootstrap \
|
||||||
rx:o/third_party/gcc \
|
rx:o/third_party/gcc \
|
||||||
/proc/stat \
|
/proc/stat \
|
||||||
|
|
|
@ -17,7 +17,7 @@ void __assert_fail(const char *, const char *, int) hidden relegated;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
#ifndef TINY
|
#if !defined(TINY) && !defined(MODE_DBG)
|
||||||
/**
|
/**
|
||||||
* Specifies expression can't possibly be false.
|
* 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.
|
* In `MODE=tiny` these assertions are redefined as undefined behavior.
|
||||||
*/
|
*/
|
||||||
#define _npassert(x) \
|
#define _npassert(x) \
|
||||||
({ \
|
__extension__({ \
|
||||||
if (__builtin_expect(!(x), 0)) { \
|
if (__builtin_expect(!(x), 0)) { \
|
||||||
notpossible; \
|
notpossible; \
|
||||||
} \
|
} \
|
||||||
|
@ -63,7 +63,7 @@ void __assert_fail(const char *, const char *, int) hidden relegated;
|
||||||
* that's capable of debugging this macro.
|
* that's capable of debugging this macro.
|
||||||
*/
|
*/
|
||||||
#define _unassert(x) \
|
#define _unassert(x) \
|
||||||
({ \
|
__extension__({ \
|
||||||
if (__builtin_expect(!(x), 0)) { \
|
if (__builtin_expect(!(x), 0)) { \
|
||||||
unreachable; \
|
unreachable; \
|
||||||
} \
|
} \
|
||||||
|
|
|
@ -84,7 +84,7 @@ int clock_gettime(int clock, struct timespec *ts) {
|
||||||
rc = __clock_gettime(clock, ts);
|
rc = __clock_gettime(clock, ts);
|
||||||
}
|
}
|
||||||
#if SYSDEBUG
|
#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),
|
STRACE("clock_gettime(%s, [%s]) → %d% m", DescribeClockName(clock),
|
||||||
DescribeTimespec(rc, ts), rc);
|
DescribeTimespec(rc, ts), rc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,7 @@ errno_t clock_nanosleep(int clock, int flags, const struct timespec *req,
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SYSDEBUG
|
#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),
|
STRACE("clock_nanosleep(%s, %s, %s, [%s]) → %s", DescribeClockName(clock),
|
||||||
DescribeSleepFlags(flags), DescribeTimespec(0, req),
|
DescribeSleepFlags(flags), DescribeTimespec(0, req),
|
||||||
DescribeTimespec(rc, rem), DescribeErrnoResult(rc));
|
DescribeTimespec(rc, rem), DescribeErrnoResult(rc));
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "libc/calls/syscall-sysv.internal.h"
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/intrin/asan.internal.h"
|
#include "libc/intrin/asan.internal.h"
|
||||||
|
#include "libc/intrin/describeflags.internal.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/intrin/likely.h"
|
#include "libc/intrin/likely.h"
|
||||||
#include "libc/intrin/promises.internal.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)))) {
|
!__asan_is_valid_strlist(envp)))) {
|
||||||
rc = efault();
|
rc = efault();
|
||||||
} else {
|
} else {
|
||||||
#ifdef SYSDEBUG
|
STRACE("execve(%#s, %s, %s) → ...", prog, DescribeStringList(argv),
|
||||||
if (UNLIKELY(__strace > 0)) {
|
DescribeStringList(envp));
|
||||||
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
|
|
||||||
if (!IsWindows()) {
|
if (!IsWindows()) {
|
||||||
rc = 0;
|
rc = 0;
|
||||||
if (IsLinux() && __execpromises && _weaken(sys_pledge_linux)) {
|
if (IsLinux() && __execpromises && _weaken(sys_pledge_linux)) {
|
||||||
|
|
|
@ -63,7 +63,7 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) {
|
||||||
rc = __gettimeofday(tv, tz, 0).ax;
|
rc = __gettimeofday(tv, tz, 0).ax;
|
||||||
}
|
}
|
||||||
#if SYSDEBUG
|
#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);
|
STRACE("gettimeofday([%s], %p) → %d% m", DescribeTimeval(rc, tv), tz, rc);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -56,7 +56,7 @@ static long double GetTimeSample(void) {
|
||||||
static long double MeasureNanosPerCycle(void) {
|
static long double MeasureNanosPerCycle(void) {
|
||||||
int i, n;
|
int i, n;
|
||||||
long double avg, samp;
|
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()) {
|
if (IsWindows()) {
|
||||||
n = 10;
|
n = 10;
|
||||||
} else {
|
} else {
|
||||||
|
@ -66,7 +66,7 @@ static long double MeasureNanosPerCycle(void) {
|
||||||
samp = GetTimeSample();
|
samp = GetTimeSample();
|
||||||
avg += (samp - avg) / i;
|
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));
|
STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000));
|
||||||
return avg;
|
return avg;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ static long double GetTimeSample(void) {
|
||||||
static long double MeasureNanosPerCycle(void) {
|
static long double MeasureNanosPerCycle(void) {
|
||||||
int i, n;
|
int i, n;
|
||||||
long double avg, samp;
|
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()) {
|
if (IsWindows()) {
|
||||||
n = 30;
|
n = 30;
|
||||||
} else {
|
} else {
|
||||||
|
@ -63,7 +63,7 @@ static long double MeasureNanosPerCycle(void) {
|
||||||
samp = GetTimeSample();
|
samp = GetTimeSample();
|
||||||
avg += (samp - avg) / i;
|
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));
|
STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000));
|
||||||
return avg;
|
return avg;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
#include "libc/stdio/rand.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
#include "libc/stdio/lcg.internal.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns handles of windows pids being tracked.
|
* Returns handles of windows pids being tracked.
|
||||||
|
@ -35,12 +35,8 @@
|
||||||
textwindows int __sample_pids(int pids[hasatleast 64],
|
textwindows int __sample_pids(int pids[hasatleast 64],
|
||||||
int64_t handles[hasatleast 64],
|
int64_t handles[hasatleast 64],
|
||||||
bool exploratory) {
|
bool exploratory) {
|
||||||
static uint64_t rando = 1;
|
|
||||||
static pthread_spinlock_t lock;
|
|
||||||
uint32_t i, j, base, count;
|
uint32_t i, j, base, count;
|
||||||
if (__threaded) pthread_spin_lock(&lock);
|
base = _rand64() >> 32;
|
||||||
base = KnuthLinearCongruentialGenerator(&rando) >> 32;
|
|
||||||
pthread_spin_unlock(&lock);
|
|
||||||
for (count = i = 0; i < g_fds.n; ++i) {
|
for (count = i = 0; i < g_fds.n; ++i) {
|
||||||
j = (base + i) % g_fds.n;
|
j = (base + i) % g_fds.n;
|
||||||
if (g_fds.p[j].kind == kFdProcess && (!exploratory || !g_fds.p[j].zombie)) {
|
if (g_fds.p[j].kind == kFdProcess && (!exploratory || !g_fds.p[j].zombie)) {
|
||||||
|
|
|
@ -17,10 +17,10 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
|
||||||
#include "libc/calls/syscall-sysv.internal.h"
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
#include "libc/calls/syscall_support-nt.internal.h"
|
#include "libc/calls/syscall_support-nt.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
#include "libc/intrin/strace.internal.h"
|
||||||
#include "libc/nt/console.h"
|
#include "libc/nt/console.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ int setpgid(int pid, int pgid) {
|
||||||
rc = sys_setpgid(pid, pgid);
|
rc = sys_setpgid(pid, pgid);
|
||||||
} else {
|
} else {
|
||||||
me = getpid();
|
me = getpid();
|
||||||
if (pid == me && pgid == me) {
|
if ((!pid || pid == me) && (!pgid || pgid == me)) {
|
||||||
/*
|
/*
|
||||||
* "When a process is created with CREATE_NEW_PROCESS_GROUP
|
* "When a process is created with CREATE_NEW_PROCESS_GROUP
|
||||||
* specified, an implicit call to SetConsoleCtrlHandler(NULL,TRUE)
|
* specified, an implicit call to SetConsoleCtrlHandler(NULL,TRUE)
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/state.internal.h"
|
#include "libc/calls/state.internal.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
void(__sig_lock)(void) {
|
void(__sig_lock)(void) {
|
||||||
|
@ -27,6 +28,11 @@ void(__sig_unlock)(void) {
|
||||||
pthread_mutex_unlock(&__sig_lock_obj);
|
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;
|
__sig_lock_obj._type = PTHREAD_MUTEX_RECURSIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__attribute__((__constructor__)) static void __sig_init(void) {
|
||||||
|
pthread_atfork(__sig_lock, __sig_unlock, __sig_funlock);
|
||||||
|
}
|
||||||
|
|
|
@ -16,8 +16,10 @@ hidden extern const struct NtSecurityAttributes kNtIsInheritable;
|
||||||
|
|
||||||
void __fds_lock(void);
|
void __fds_lock(void);
|
||||||
void __fds_unlock(void);
|
void __fds_unlock(void);
|
||||||
|
void __fds_funlock(void);
|
||||||
void __sig_lock(void);
|
void __sig_lock(void);
|
||||||
void __sig_unlock(void);
|
void __sig_unlock(void);
|
||||||
|
void __sig_funlock(void);
|
||||||
|
|
||||||
#ifdef _NOPL0
|
#ifdef _NOPL0
|
||||||
#define __fds_lock() _NOPL0("__threadcalls", __fds_lock)
|
#define __fds_lock() _NOPL0("__threadcalls", __fds_lock)
|
||||||
|
@ -35,6 +37,8 @@ void __sig_unlock(void);
|
||||||
#define __sig_unlock() (__threaded ? __sig_unlock() : 0)
|
#define __sig_unlock() (__threaded ? __sig_unlock() : 0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define __vforked (__tls_enabled && (__get_tls()->tib_flags & TIB_FLAG_VFORKED))
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
#endif /* COSMOPOLITAN_LIBC_CALLS_STATE_INTERNAL_H_ */
|
#endif /* COSMOPOLITAN_LIBC_CALLS_STATE_INTERNAL_H_ */
|
||||||
|
|
|
@ -16,6 +16,7 @@ int sigorset(sigset_t *, const sigset_t *, const sigset_t *) paramsnonnull();
|
||||||
int sigisemptyset(const sigset_t *) paramsnonnull() nosideeffect;
|
int sigisemptyset(const sigset_t *) paramsnonnull() nosideeffect;
|
||||||
int sigismember(const sigset_t *, int) paramsnonnull() nosideeffect;
|
int sigismember(const sigset_t *, int) paramsnonnull() nosideeffect;
|
||||||
int sigcountset(const sigset_t *) paramsnonnull() nosideeffect;
|
int sigcountset(const sigset_t *) paramsnonnull() nosideeffect;
|
||||||
|
int sigisprecious(int) nosideeffect;
|
||||||
int sigprocmask(int, const sigset_t *, sigset_t *);
|
int sigprocmask(int, const sigset_t *, sigset_t *);
|
||||||
int sigsuspend(const sigset_t *);
|
int sigsuspend(const sigset_t *);
|
||||||
int sigpending(sigset_t *);
|
int sigpending(sigset_t *);
|
||||||
|
|
|
@ -18,21 +18,46 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/sig.internal.h"
|
#include "libc/calls/sig.internal.h"
|
||||||
#include "libc/calls/state.internal.h"
|
#include "libc/calls/state.internal.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
|
||||||
#include "libc/calls/struct/ucontext.internal.h"
|
#include "libc/calls/struct/ucontext.internal.h"
|
||||||
#include "libc/calls/ucontext.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/exceptionhandleractions.h"
|
||||||
#include "libc/nt/enum/signal.h"
|
#include "libc/nt/enum/signal.h"
|
||||||
|
#include "libc/nt/runtime.h"
|
||||||
#include "libc/nt/struct/ntexceptionpointers.h"
|
#include "libc/nt/struct/ntexceptionpointers.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/sa.h"
|
#include "libc/sysv/consts/sa.h"
|
||||||
#include "libc/sysv/consts/sicode.h"
|
#include "libc/sysv/consts/sicode.h"
|
||||||
#include "libc/sysv/consts/sig.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
|
#include "libc/thread/tls.h"
|
||||||
|
#include "libc/thread/tls2.h"
|
||||||
|
|
||||||
privileged unsigned __wincrash(struct NtExceptionPointers *ep) {
|
privileged unsigned __wincrash(struct NtExceptionPointers *ep) {
|
||||||
int64_t rip;
|
int64_t rip;
|
||||||
int sig, code;
|
int sig, code;
|
||||||
ucontext_t ctx;
|
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");
|
STRACE("__wincrash");
|
||||||
|
|
||||||
switch (ep->ExceptionRecord->ExceptionCode) {
|
switch (ep->ExceptionRecord->ExceptionCode) {
|
||||||
|
@ -115,5 +140,10 @@ privileged unsigned __wincrash(struct NtExceptionPointers *ep) {
|
||||||
ep->ContextRecord->Rip++;
|
ep->ContextRecord->Rip++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
noreentry = false;
|
||||||
|
if (tib) {
|
||||||
|
tib->tib_flags &= ~TIB_FLAG_WINCRASHING;
|
||||||
|
}
|
||||||
|
|
||||||
return kNtExceptionContinueExecution;
|
return kNtExceptionContinueExecution;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,3 +28,11 @@ void(__cxa_lock)(void) {
|
||||||
void(__cxa_unlock)(void) {
|
void(__cxa_unlock)(void) {
|
||||||
pthread_mutex_unlock(&__cxa_lock_obj);
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -16,22 +16,25 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/intrin/describebacktrace.internal.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/nexgen32e/stackframe.h"
|
||||||
#include "libc/str/path.h"
|
|
||||||
#include "libc/str/str.h"
|
|
||||||
#include "libc/thread/semaphore.internal.h"
|
|
||||||
#include "libc/thread/thread.h"
|
|
||||||
|
|
||||||
const char *__sem_name(const char *name, char path[hasatleast PATH_MAX]) {
|
#define N 64
|
||||||
const char *res;
|
|
||||||
if (_isabspath(name)) {
|
#define append(...) o += ksnprintf(buf + o, N - o, __VA_ARGS__)
|
||||||
res = name;
|
|
||||||
} else {
|
const char *(DescribeBacktrace)(char buf[N], struct StackFrame *fr) {
|
||||||
strlcpy(path, kTmpPath, PATH_MAX);
|
int o = 0;
|
||||||
strlcat(path, ".sem-", PATH_MAX);
|
bool gotsome = false;
|
||||||
strlcat(path, name, PATH_MAX);
|
while (fr) {
|
||||||
res = path;
|
if (gotsome) {
|
||||||
|
append(" ");
|
||||||
|
} else {
|
||||||
|
gotsome = true;
|
||||||
|
}
|
||||||
|
append("%x", fr->addr);
|
||||||
|
fr = fr->next;
|
||||||
}
|
}
|
||||||
return res;
|
return buf;
|
||||||
}
|
}
|
13
libc/intrin/describebacktrace.internal.h
Normal file
13
libc/intrin/describebacktrace.internal.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef COSMOPOLITAN_LIBC_INTRIN_DESCRIBEBACKTRACE_INTERNAL_H_
|
||||||
|
#define COSMOPOLITAN_LIBC_INTRIN_DESCRIBEBACKTRACE_INTERNAL_H_
|
||||||
|
#include "libc/mem/alloca.h"
|
||||||
|
#include "libc/nexgen32e/stackframe.h"
|
||||||
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
const char *DescribeBacktrace(char[64], struct StackFrame *);
|
||||||
|
#define DescribeBacktrace(x) DescribeBacktrace(alloca(64), x)
|
||||||
|
|
||||||
|
COSMOPOLITAN_C_END_
|
||||||
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
#endif /* COSMOPOLITAN_LIBC_INTRIN_DESCRIBEBACKTRACE_INTERNAL_H_ */
|
|
@ -62,6 +62,7 @@ const char *DescribeSocketFamily(char[12], int);
|
||||||
const char *DescribeSocketProtocol(char[12], int);
|
const char *DescribeSocketProtocol(char[12], int);
|
||||||
const char *DescribeSocketType(char[64], int);
|
const char *DescribeSocketType(char[64], int);
|
||||||
const char *DescribeStdioState(char[12], int);
|
const char *DescribeStdioState(char[12], int);
|
||||||
|
const char *DescribeStringList(char[300], char *const[]);
|
||||||
const char *DescribeWhence(char[12], int);
|
const char *DescribeWhence(char[12], int);
|
||||||
const char *DescribeWhichPrio(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 DescribeCapability(x) DescribeCapability(alloca(20), x)
|
||||||
#define DescribeClockName(x) DescribeClockName(alloca(32), x)
|
#define DescribeClockName(x) DescribeClockName(alloca(32), x)
|
||||||
#define DescribeDirfd(x) DescribeDirfd(alloca(12), x)
|
#define DescribeDirfd(x) DescribeDirfd(alloca(12), x)
|
||||||
|
#define DescribeDnotifyFlags(x) DescribeDnotifyFlags(alloca(80), x)
|
||||||
#define DescribeErrno(x) DescribeErrno(alloca(12), x)
|
#define DescribeErrno(x) DescribeErrno(alloca(12), x)
|
||||||
#define DescribeErrnoResult(x) DescribeErrnoResult(alloca(12), x)
|
#define DescribeErrnoResult(x) DescribeErrnoResult(alloca(12), x)
|
||||||
#define DescribeFcntlCmd(x) DescribeFcntlCmd(alloca(20), 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 DescribeSocketProtocol(x) DescribeSocketProtocol(alloca(12), x)
|
||||||
#define DescribeSocketType(x) DescribeSocketType(alloca(64), x)
|
#define DescribeSocketType(x) DescribeSocketType(alloca(64), x)
|
||||||
#define DescribeStdioState(x) DescribeStdioState(alloca(12), x)
|
#define DescribeStdioState(x) DescribeStdioState(alloca(12), x)
|
||||||
|
#define DescribeStringList(x) DescribeStringList(alloca(300), x)
|
||||||
#define DescribeWhence(x) DescribeWhence(alloca(12), x)
|
#define DescribeWhence(x) DescribeWhence(alloca(12), x)
|
||||||
#define DescribeWhichPrio(x) DescribeWhichPrio(alloca(12), x)
|
#define DescribeWhichPrio(x) DescribeWhichPrio(alloca(12), x)
|
||||||
#define DescribeDnotifyFlags(x) DescribeDnotifyFlags(alloca(80), x)
|
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
|
@ -54,8 +54,8 @@ static const char *GetFrameName(int x) {
|
||||||
return "g_fds";
|
return "g_fds";
|
||||||
} else if (IsZiposFrame(x)) {
|
} else if (IsZiposFrame(x)) {
|
||||||
return "zipos";
|
return "zipos";
|
||||||
} else if (IsNsyncFrame(x)) {
|
} else if (IsKmallocFrame(x)) {
|
||||||
return "nsync";
|
return "kmalloc";
|
||||||
} else if (IsMemtrackFrame(x)) {
|
} else if (IsMemtrackFrame(x)) {
|
||||||
return "memtrack";
|
return "memtrack";
|
||||||
} else if (IsOldStackFrame(x)) {
|
} else if (IsOldStackFrame(x)) {
|
||||||
|
|
|
@ -23,14 +23,15 @@
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/intrin/popcnt.h"
|
#include "libc/intrin/popcnt.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/sysv/consts/sig.h"
|
||||||
|
|
||||||
#define N 128
|
#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) {
|
const char *(DescribeSigset)(char buf[N], int rc, const sigset_t *ss) {
|
||||||
int i, sig;
|
|
||||||
bool gotsome;
|
bool gotsome;
|
||||||
|
int sig, o = 0;
|
||||||
sigset_t sigset;
|
sigset_t sigset;
|
||||||
|
|
||||||
if (rc == -1) return "n/a";
|
if (rc == -1) return "n/a";
|
||||||
|
@ -41,17 +42,22 @@ const char *(DescribeSigset)(char buf[N], int rc, const sigset_t *ss) {
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
i = 0;
|
if (sigcountset(ss) > 16) {
|
||||||
sigset = *ss;
|
|
||||||
gotsome = false;
|
|
||||||
if (popcnt(sigset.__bits[0] & 0xffffffff) > 16) {
|
|
||||||
append("~");
|
append("~");
|
||||||
sigset.__bits[0] = ~sigset.__bits[0];
|
sigemptyset(&sigset);
|
||||||
sigset.__bits[1] = ~sigset.__bits[1];
|
for (sig = 1; sig <= NSIG; ++sig) {
|
||||||
|
if (!sigismember(ss, sig)) {
|
||||||
|
sigaddset(&sigset, sig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sigset = *ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
append("{");
|
append("{");
|
||||||
for (sig = 1; sig < 32; ++sig) {
|
gotsome = false;
|
||||||
if (sigismember(&sigset, sig)) {
|
for (sig = 1; sig <= NSIG; ++sig) {
|
||||||
|
if (sigismember(&sigset, sig) > 0) {
|
||||||
if (gotsome) {
|
if (gotsome) {
|
||||||
append(",");
|
append(",");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -24,10 +24,10 @@
|
||||||
|
|
||||||
#define N 300
|
#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) {
|
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 (rc == -1) return "n/a";
|
||||||
if (!st) return "NULL";
|
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);
|
append(", .st_%s=%lu", "gid", st->st_gid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (st->st_dev) {
|
||||||
|
append(", .st_%s=%lu", "dev", st->st_dev);
|
||||||
|
}
|
||||||
|
|
||||||
if (st->st_ino) {
|
if (st->st_ino) {
|
||||||
append(", .st_%s=%lu", "ino", st->st_ino);
|
append(", .st_%s=%lu", "ino", st->st_ino);
|
||||||
}
|
}
|
||||||
|
|
46
libc/intrin/describestringlist.c
Normal file
46
libc/intrin/describestringlist.c
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/dce.h"
|
||||||
|
#include "libc/intrin/asan.internal.h"
|
||||||
|
#include "libc/intrin/describeflags.internal.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
|
|
||||||
|
#define N 300
|
||||||
|
|
||||||
|
#define append(...) o += ksnprintf(buf + o, N - o, __VA_ARGS__)
|
||||||
|
|
||||||
|
const char *(DescribeStringList)(char buf[N], char *const list[]) {
|
||||||
|
int i, o = 0;
|
||||||
|
|
||||||
|
if (!list) return "NULL";
|
||||||
|
if (IsAsan() && !__asan_is_valid_strlist(list)) {
|
||||||
|
ksnprintf(buf, N, "%p", list);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
append("{");
|
||||||
|
i = 0;
|
||||||
|
do {
|
||||||
|
if (i++) append(", ");
|
||||||
|
append("%#s", *list);
|
||||||
|
} while (*list++);
|
||||||
|
append("}");
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/state.internal.h"
|
#include "libc/calls/state.internal.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
void(__fds_lock)(void) {
|
void(__fds_lock)(void) {
|
||||||
|
@ -26,3 +27,8 @@ void(__fds_lock)(void) {
|
||||||
void(__fds_unlock)(void) {
|
void(__fds_unlock)(void) {
|
||||||
pthread_mutex_unlock(&__fds_lock_obj);
|
pthread_mutex_unlock(&__fds_lock_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void __fds_funlock(void) {
|
||||||
|
bzero(&__fds_lock_obj, sizeof(__fds_lock_obj));
|
||||||
|
__fds_lock_obj._type = PTHREAD_MUTEX_RECURSIVE;
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "libc/intrin/weaken.h"
|
#include "libc/intrin/weaken.h"
|
||||||
#include "libc/nt/runtime.h"
|
#include "libc/nt/runtime.h"
|
||||||
#include "libc/runtime/memtrack.internal.h"
|
#include "libc/runtime/memtrack.internal.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/map.h"
|
#include "libc/sysv/consts/map.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
#include "libc/thread/thread.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) {
|
textstartup void InitializeFileDescriptors(void) {
|
||||||
struct Fds *fds;
|
struct Fds *fds;
|
||||||
__fds_lock_obj._type = PTHREAD_MUTEX_RECURSIVE;
|
__fds_lock_obj._type = PTHREAD_MUTEX_RECURSIVE;
|
||||||
|
pthread_atfork(_weaken(__fds_lock), _weaken(__fds_unlock),
|
||||||
|
_weaken(__fds_funlock));
|
||||||
fds = VEIL("r", &g_fds);
|
fds = VEIL("r", &g_fds);
|
||||||
fds->p = fds->e = (void *)kMemtrackFdsStart;
|
fds->p = fds->e = (void *)kMemtrackFdsStart;
|
||||||
fds->n = 4;
|
fds->n = 4;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
│ │
|
│ │
|
||||||
|
@ -16,37 +16,60 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/assert.h"
|
||||||
#include "libc/atomic.h"
|
#include "libc/atomic.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
|
#include "libc/intrin/asan.internal.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/extend.internal.h"
|
#include "libc/intrin/extend.internal.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/runtime/memtrack.internal.h"
|
#include "libc/runtime/memtrack.internal.h"
|
||||||
#include "libc/sysv/consts/map.h"
|
#include "libc/sysv/consts/map.h"
|
||||||
#include "third_party/nsync/common.internal.h"
|
#include "libc/thread/thread.h"
|
||||||
#include "third_party/nsync/malloc.internal.h"
|
#include "libc/thread/tls.h"
|
||||||
// clang-format off
|
|
||||||
|
|
||||||
static char *nsync_malloc_endptr_;
|
#define KMALLOC_ALIGN __BIGGEST_ALIGNMENT__
|
||||||
static size_t nsync_malloc_total_;
|
|
||||||
static atomic_char nsync_malloc_lock_;
|
|
||||||
|
|
||||||
/* nsync_malloc_() is a malloc-like routine used by mutex and condition
|
static struct {
|
||||||
variable code to allocate waiter structs. This allows *NSYNC mutexes
|
char *endptr;
|
||||||
to be used by malloc(), by providing another, simpler allocator here.
|
size_t total;
|
||||||
The intent is that the implicit NULL value here can be overridden by
|
pthread_spinlock_t lock;
|
||||||
a client declaration that uses an initializer. */
|
} g_kmalloc;
|
||||||
void *nsync_malloc_ (size_t size) {
|
|
||||||
char *start;
|
static void kmalloc_lock(void) {
|
||||||
size_t offset;
|
if (__threaded) pthread_spin_lock(&g_kmalloc.lock);
|
||||||
size = ROUNDUP (size, __BIGGEST_ALIGNMENT__);
|
}
|
||||||
while (atomic_exchange (&nsync_malloc_lock_, 1)) nsync_yield_ ();
|
|
||||||
offset = nsync_malloc_total_;
|
static void kmalloc_unlock(void) {
|
||||||
nsync_malloc_total_ += size;
|
pthread_spin_unlock(&g_kmalloc.lock);
|
||||||
start = (char *) kMemtrackNsyncStart;
|
}
|
||||||
if (!nsync_malloc_endptr_) nsync_malloc_endptr_ = start;
|
|
||||||
nsync_malloc_endptr_ =
|
__attribute__((__constructor__)) static void kmalloc_init(void) {
|
||||||
_extend (start, nsync_malloc_total_, nsync_malloc_endptr_,
|
pthread_atfork(kmalloc_lock, kmalloc_unlock, kmalloc_unlock);
|
||||||
MAP_PRIVATE, kMemtrackNsyncStart + kMemtrackNsyncSize);
|
}
|
||||||
atomic_store_explicit (&nsync_malloc_lock_, 0, memory_order_relaxed);
|
|
||||||
return start + offset;
|
/**
|
||||||
|
* Allocates permanent memory.
|
||||||
|
*
|
||||||
|
* The code malloc() depends upon uses this function to allocate memory.
|
||||||
|
* The returned memory can't be freed, and leak detection is impossible.
|
||||||
|
* This function panics when memory isn't available.
|
||||||
|
*/
|
||||||
|
void *kmalloc(size_t size) {
|
||||||
|
char *start;
|
||||||
|
size_t i, n;
|
||||||
|
n = ROUNDUP(size + (IsAsan() * 8), KMALLOC_ALIGN);
|
||||||
|
kmalloc_lock();
|
||||||
|
i = g_kmalloc.total;
|
||||||
|
g_kmalloc.total += n;
|
||||||
|
start = (char *)kMemtrackKmallocStart;
|
||||||
|
if (!g_kmalloc.endptr) g_kmalloc.endptr = start;
|
||||||
|
g_kmalloc.endptr =
|
||||||
|
_extend(start, g_kmalloc.total, g_kmalloc.endptr, MAP_PRIVATE,
|
||||||
|
kMemtrackKmallocStart + kMemtrackKmallocSize);
|
||||||
|
kmalloc_unlock();
|
||||||
|
_unassert(!((intptr_t)(start + i) & (KMALLOC_ALIGN - 1)));
|
||||||
|
if (IsAsan()) __asan_poison(start + i + size, n - size, kAsanHeapOverrun);
|
||||||
|
return start + i;
|
||||||
}
|
}
|
10
libc/intrin/kmalloc.h
Normal file
10
libc/intrin/kmalloc.h
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef COSMOPOLITAN_LIBC_INTRIN_KMALLOC_H_
|
||||||
|
#define COSMOPOLITAN_LIBC_INTRIN_KMALLOC_H_
|
||||||
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
void *kmalloc(size_t) attributeallocsize((1)) mallocesque returnsnonnull;
|
||||||
|
|
||||||
|
COSMOPOLITAN_C_END_
|
||||||
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
#endif /* COSMOPOLITAN_LIBC_INTRIN_KMALLOC_H_ */
|
|
@ -206,6 +206,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt,
|
||||||
const char *abet;
|
const char *abet;
|
||||||
signed char type;
|
signed char type;
|
||||||
const char *s, *f;
|
const char *s, *f;
|
||||||
|
struct CosmoTib *tib;
|
||||||
unsigned long long x;
|
unsigned long long x;
|
||||||
unsigned i, j, m, rem, sign, hash, cols, prec;
|
unsigned i, j, m, rem, sign, hash, cols, prec;
|
||||||
char c, *p, *e, pdot, zero, flip, dang, base, quot, uppr, ansi, z[128];
|
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;
|
goto FormatUnsigned;
|
||||||
|
|
||||||
case 'P':
|
case 'P':
|
||||||
if (!__vforked) {
|
tib = __tls_enabled ? __get_tls_privileged() : 0;
|
||||||
if (!__tls_enabled) {
|
if (!(tib && (tib->tib_flags & TIB_FLAG_VFORKED))) {
|
||||||
x = __pid;
|
if (tib) {
|
||||||
|
x = atomic_load_explicit(&tib->tib_tid, memory_order_relaxed);
|
||||||
} else {
|
} else {
|
||||||
x = atomic_load_explicit(&__get_tls_privileged()->tib_tid,
|
x = sys_gettid();
|
||||||
memory_order_relaxed);
|
|
||||||
}
|
}
|
||||||
if (!__nocolor && p + 7 <= e) {
|
if (!__nocolor && p + 7 <= e) {
|
||||||
*p++ = '\e';
|
*p++ = '\e';
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/runtime/memtrack.internal.h"
|
#include "libc/runtime/memtrack.internal.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
// this lock currently needs to be (1) recursive and (2) not nsync
|
// this lock currently needs to be (1) recursive and (2) not nsync
|
||||||
|
@ -30,3 +31,12 @@ void(__mmi_lock)(void) {
|
||||||
void(__mmi_unlock)(void) {
|
void(__mmi_unlock)(void) {
|
||||||
pthread_mutex_unlock(&__mmi_lock_obj);
|
pthread_mutex_unlock(&__mmi_lock_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void(__mmi_funlock)(void) {
|
||||||
|
bzero(&__mmi_lock_obj, sizeof(__mmi_lock_obj));
|
||||||
|
__mmi_lock_obj._type = PTHREAD_MUTEX_RECURSIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((__constructor__)) static void init(void) {
|
||||||
|
pthread_atfork(__mmi_lock, __mmi_unlock, __mmi_funlock);
|
||||||
|
}
|
||||||
|
|
84
libc/intrin/pthread_atfork.c
Normal file
84
libc/intrin/pthread_atfork.c
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/intrin/weaken.h"
|
||||||
|
#include "libc/thread/posixthread.internal.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers fork() handlers.
|
||||||
|
*
|
||||||
|
* Parent and child functions are called in the same order they're
|
||||||
|
* registered. Prepare functions are called in reverse order.
|
||||||
|
*
|
||||||
|
* Here's an example of how pthread_atfork() can be used:
|
||||||
|
*
|
||||||
|
* static struct {
|
||||||
|
* pthread_once_t once;
|
||||||
|
* pthread_mutex_t lock;
|
||||||
|
* // data structures...
|
||||||
|
* } g_lib;
|
||||||
|
*
|
||||||
|
* static void lib_lock(void) {
|
||||||
|
* pthread_mutex_lock(&g_lib.lock);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* static void lib_unlock(void) {
|
||||||
|
* pthread_mutex_unlock(&g_lib.lock);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* static void lib_funlock(void) {
|
||||||
|
* pthread_mutex_init(&g_lib.lock, 0);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* static void lib_setup(void) {
|
||||||
|
* lib_funlock();
|
||||||
|
* pthread_atfork(lib_lock, lib_unlock, lib_funlock);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* static void lib_init(void) {
|
||||||
|
* pthread_once(&g_lib.once, lib_setup);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void lib(void) {
|
||||||
|
* lib_init();
|
||||||
|
* lib_lock();
|
||||||
|
* // do stuff...
|
||||||
|
* lib_unlock();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* This won't actually aspect fork() until pthread_create() is called,
|
||||||
|
* since we don't want normal non-threaded programs to have to acquire
|
||||||
|
* exclusive locks on every resource in the entire app just to fork().
|
||||||
|
*
|
||||||
|
* The vfork() function is *never* aspected. What happens instead is a
|
||||||
|
* global variable named `__vforked` is set to true in the child which
|
||||||
|
* causes lock functions to do nothing. So far, it works like a charm.
|
||||||
|
*
|
||||||
|
* @param prepare is run by fork() before forking happens
|
||||||
|
* @param parent is run by fork() after forking happens in parent process
|
||||||
|
* @param child is run by fork() after forking happens in childe process
|
||||||
|
* @return 0 on success, or errno on error
|
||||||
|
*/
|
||||||
|
int pthread_atfork(atfork_f prepare, atfork_f parent, atfork_f child) {
|
||||||
|
if (_weaken(_pthread_atfork)) {
|
||||||
|
return _weaken(_pthread_atfork)(prepare, parent, child);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,7 +28,7 @@
|
||||||
*/
|
*/
|
||||||
int pthread_key_create(pthread_key_t *key, pthread_key_dtor dtor) {
|
int pthread_key_create(pthread_key_t *key, pthread_key_dtor dtor) {
|
||||||
int i, j, rc = EAGAIN;
|
int i, j, rc = EAGAIN;
|
||||||
pthread_spin_lock(&_pthread_keys_lock);
|
_pthread_key_lock();
|
||||||
for (i = 0; i < (PTHREAD_KEYS_MAX + 63) / 64; ++i) {
|
for (i = 0; i < (PTHREAD_KEYS_MAX + 63) / 64; ++i) {
|
||||||
if (~_pthread_key_usage[i]) {
|
if (~_pthread_key_usage[i]) {
|
||||||
j = _bsrl(~_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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pthread_spin_unlock(&_pthread_keys_lock);
|
_pthread_key_unlock();
|
||||||
return rc;
|
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) {
|
static textexit void _pthread_key_atexit(void) {
|
||||||
_pthread_key_destruct(0);
|
_pthread_key_destruct(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((__constructor__)) static textstartup void _pthread_key_init() {
|
__attribute__((__constructor__)) static textstartup void _pthread_key_init() {
|
||||||
atexit(_pthread_key_atexit);
|
atexit(_pthread_key_atexit);
|
||||||
|
pthread_atfork(_pthread_key_lock, //
|
||||||
|
_pthread_key_unlock, //
|
||||||
|
_pthread_key_funlock);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
*/
|
*/
|
||||||
int pthread_key_delete(pthread_key_t key) {
|
int pthread_key_delete(pthread_key_t key) {
|
||||||
int rc;
|
int rc;
|
||||||
pthread_spin_lock(&_pthread_keys_lock);
|
_pthread_key_lock();
|
||||||
if (key < PTHREAD_KEYS_MAX) {
|
if (key < PTHREAD_KEYS_MAX) {
|
||||||
_pthread_key_usage[key / 64] &= ~(1ul << (key % 64));
|
_pthread_key_usage[key / 64] &= ~(1ul << (key % 64));
|
||||||
_pthread_key_dtor[key] = 0;
|
_pthread_key_dtor[key] = 0;
|
||||||
|
@ -33,6 +33,6 @@ int pthread_key_delete(pthread_key_t key) {
|
||||||
} else {
|
} else {
|
||||||
rc = EINVAL;
|
rc = EINVAL;
|
||||||
}
|
}
|
||||||
pthread_spin_unlock(&_pthread_keys_lock);
|
_pthread_key_unlock();
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,13 +21,14 @@
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
|
|
||||||
|
// TODO(jart): why does this even need a lock?
|
||||||
void _pthread_key_destruct(void *key[PTHREAD_KEYS_MAX]) {
|
void _pthread_key_destruct(void *key[PTHREAD_KEYS_MAX]) {
|
||||||
int i, j;
|
int i, j;
|
||||||
uint64_t x;
|
uint64_t x;
|
||||||
void *value;
|
void *value;
|
||||||
pthread_key_dtor dtor;
|
pthread_key_dtor dtor;
|
||||||
if (!__tls_enabled) return;
|
if (!__tls_enabled) return;
|
||||||
pthread_spin_lock(&_pthread_keys_lock);
|
_pthread_key_lock();
|
||||||
if (!key) key = __get_tls()->tib_keys;
|
if (!key) key = __get_tls()->tib_keys;
|
||||||
StartOver:
|
StartOver:
|
||||||
for (i = 0; i < (PTHREAD_KEYS_MAX + 63) / 64; ++i) {
|
for (i = 0; i < (PTHREAD_KEYS_MAX + 63) / 64; ++i) {
|
||||||
|
@ -36,13 +37,13 @@ StartOver:
|
||||||
j = _bsrl(x);
|
j = _bsrl(x);
|
||||||
if ((value = key[i * 64 + j]) && (dtor = _pthread_key_dtor[i * 64 + j])) {
|
if ((value = key[i * 64 + j]) && (dtor = _pthread_key_dtor[i * 64 + j])) {
|
||||||
key[i * 64 + j] = 0;
|
key[i * 64 + j] = 0;
|
||||||
pthread_spin_unlock(&_pthread_keys_lock);
|
_pthread_key_unlock();
|
||||||
dtor(value);
|
dtor(value);
|
||||||
pthread_spin_lock(&_pthread_keys_lock);
|
_pthread_key_lock();
|
||||||
goto StartOver;
|
goto StartOver;
|
||||||
}
|
}
|
||||||
x &= ~(1ul << j);
|
x &= ~(1ul << j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pthread_spin_unlock(&_pthread_keys_lock);
|
_pthread_key_unlock();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,12 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/state.internal.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
|
#include "libc/intrin/strace.internal.h"
|
||||||
#include "libc/intrin/weaken.h"
|
#include "libc/intrin/weaken.h"
|
||||||
|
#include "libc/runtime/internal.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
#include "third_party/nsync/mu.h"
|
#include "third_party/nsync/mu.h"
|
||||||
|
@ -55,12 +58,19 @@
|
||||||
* pthread_mutex_unlock(&lock);
|
* pthread_mutex_unlock(&lock);
|
||||||
* pthread_mutex_destroy(&lock);
|
* pthread_mutex_destroy(&lock);
|
||||||
*
|
*
|
||||||
|
* This function does nothing in vfork() children.
|
||||||
|
*
|
||||||
* @return 0 on success, or error number on failure
|
* @return 0 on success, or error number on failure
|
||||||
* @see pthread_spin_lock()
|
* @see pthread_spin_lock()
|
||||||
|
* @vforksafe
|
||||||
*/
|
*/
|
||||||
int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||||
int c, d, t;
|
int c, d, t;
|
||||||
|
|
||||||
|
if (__vforked) return 0;
|
||||||
|
|
||||||
|
LOCKTRACE("pthread_mutex_lock(%t)", mutex);
|
||||||
|
|
||||||
if (__tls_enabled && //
|
if (__tls_enabled && //
|
||||||
mutex->_type == PTHREAD_MUTEX_NORMAL && //
|
mutex->_type == PTHREAD_MUTEX_NORMAL && //
|
||||||
mutex->_pshared == PTHREAD_PROCESS_PRIVATE && //
|
mutex->_pshared == PTHREAD_PROCESS_PRIVATE && //
|
||||||
|
@ -96,6 +106,7 @@ int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||||
|
|
||||||
mutex->_depth = 0;
|
mutex->_depth = 0;
|
||||||
mutex->_owner = t;
|
mutex->_owner = t;
|
||||||
|
mutex->_pid = __pid;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,12 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/state.internal.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
|
#include "libc/intrin/strace.internal.h"
|
||||||
#include "libc/intrin/weaken.h"
|
#include "libc/intrin/weaken.h"
|
||||||
|
#include "libc/runtime/internal.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
#include "third_party/nsync/mu.h"
|
#include "third_party/nsync/mu.h"
|
||||||
|
@ -27,12 +30,19 @@
|
||||||
/**
|
/**
|
||||||
* Releases mutex.
|
* Releases mutex.
|
||||||
*
|
*
|
||||||
|
* This function does nothing in vfork() children.
|
||||||
|
*
|
||||||
* @return 0 on success or error number on failure
|
* @return 0 on success or error number on failure
|
||||||
* @raises EPERM if in error check mode and not owned by caller
|
* @raises EPERM if in error check mode and not owned by caller
|
||||||
|
* @vforksafe
|
||||||
*/
|
*/
|
||||||
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
||||||
int c, t;
|
int c, t;
|
||||||
|
|
||||||
|
if (__vforked) return 0;
|
||||||
|
|
||||||
|
LOCKTRACE("pthread_mutex_unlock(%t)", mutex);
|
||||||
|
|
||||||
if (__tls_enabled && //
|
if (__tls_enabled && //
|
||||||
mutex->_type == PTHREAD_MUTEX_NORMAL && //
|
mutex->_type == PTHREAD_MUTEX_NORMAL && //
|
||||||
mutex->_pshared == PTHREAD_PROCESS_PRIVATE && //
|
mutex->_pshared == PTHREAD_PROCESS_PRIVATE && //
|
||||||
|
@ -47,7 +57,11 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t = gettid();
|
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;
|
return EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/struct/sigset.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,7 +30,9 @@
|
||||||
int sigaddset(sigset_t *set, int sig) {
|
int sigaddset(sigset_t *set, int sig) {
|
||||||
_Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, "");
|
_Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, "");
|
||||||
if (1 <= sig && sig <= NSIG) {
|
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;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return einval();
|
return einval();
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/struct/sigset.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/sysv/consts/sig.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds all signals to set.
|
* Adds all signals to set.
|
||||||
|
@ -28,5 +29,7 @@
|
||||||
*/
|
*/
|
||||||
int sigfillset(sigset_t *set) {
|
int sigfillset(sigset_t *set) {
|
||||||
memset(set->__bits, -1, sizeof(set->__bits));
|
memset(set->__bits, -1, sizeof(set->__bits));
|
||||||
|
sigdelset(set, SIGKILL);
|
||||||
|
sigdelset(set, SIGSTOP);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,17 +16,13 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/thread/semaphore.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes named semaphore.
|
* Returns true if you're not authorized to block this signal.
|
||||||
*
|
|
||||||
* @param sem was created with sem_open()
|
|
||||||
* @return 0 on success, or -1 w/ errno
|
|
||||||
*/
|
*/
|
||||||
int sem_close(sem_t *sem) {
|
int sigisprecious(int sig) {
|
||||||
_npassert(!munmap(sem, FRAMESIZE));
|
return sig == SIGKILL || //
|
||||||
return 0;
|
sig == SIGSTOP;
|
||||||
}
|
}
|
|
@ -7,7 +7,8 @@
|
||||||
#define _POLLTRACE 0 /* not configurable w/ flag yet */
|
#define _POLLTRACE 0 /* not configurable w/ flag yet */
|
||||||
#define _DATATRACE 1 /* not configurable w/ flag yet */
|
#define _DATATRACE 1 /* not configurable w/ flag yet */
|
||||||
#define _STDIOTRACE 0 /* 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 "
|
#define STRACE_PROLOGUE "%rSYS %6P %'18T "
|
||||||
|
|
||||||
|
@ -55,6 +56,12 @@ COSMOPOLITAN_C_START_
|
||||||
#define NTTRACE(FMT, ...) (void)0
|
#define NTTRACE(FMT, ...) (void)0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(SYSDEBUG) && _LOCKTRACE
|
||||||
|
#define LOCKTRACE(FMT, ...) STRACE(FMT, ##__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define LOCKTRACE(FMT, ...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
void __stracef(const char *, ...);
|
void __stracef(const char *, ...);
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/intrin/strace.internal.h"
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
|
|
||||||
void __require_tls(void) {
|
void __require_tls(void) {
|
||||||
|
|
|
@ -228,8 +228,8 @@ static void __ubsan_warning(const struct UbsanSourceLocation *loc,
|
||||||
|
|
||||||
dontdiscard __ubsan_die_f *__ubsan_abort(const struct UbsanSourceLocation *loc,
|
dontdiscard __ubsan_die_f *__ubsan_abort(const struct UbsanSourceLocation *loc,
|
||||||
const char *description) {
|
const char *description) {
|
||||||
kprintf("\n%s:%d: %subsan error%s: %s\n", loc->file, loc->line, RED2, RESET,
|
kprintf("\n%s:%d: %subsan error%s: %s (tid %d)\n", loc->file, loc->line, RED2,
|
||||||
description);
|
RESET, description, gettid());
|
||||||
return __ubsan_die();
|
return __ubsan_die();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
4
libc/isystem/cpio.h
Normal 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_ */
|
|
@ -284,7 +284,6 @@ relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
|
||||||
int gdbpid, err;
|
int gdbpid, err;
|
||||||
static int sync;
|
static int sync;
|
||||||
static bool _notpossible;
|
static bool _notpossible;
|
||||||
__tls_enabled = false;
|
|
||||||
STRACE("__oncrash rip %x", ctx->uc_mcontext.rip);
|
STRACE("__oncrash rip %x", ctx->uc_mcontext.rip);
|
||||||
--__ftrace;
|
--__ftrace;
|
||||||
--__strace;
|
--__strace;
|
||||||
|
|
|
@ -51,7 +51,6 @@ struct mallinfo {
|
||||||
|
|
||||||
struct mallinfo mallinfo(void);
|
struct mallinfo mallinfo(void);
|
||||||
|
|
||||||
void malloc_stats(void);
|
|
||||||
size_t malloc_footprint(void);
|
size_t malloc_footprint(void);
|
||||||
size_t malloc_max_footprint(void);
|
size_t malloc_max_footprint(void);
|
||||||
size_t malloc_footprint_limit(void);
|
size_t malloc_footprint_limit(void);
|
||||||
|
|
|
@ -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);
|
pthread_mutex_lock(&__brk.m);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void brk_unlock(void) {
|
void brk_unlock(void) {
|
||||||
pthread_mutex_unlock(&__brk.m);
|
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
|
#ifdef _NOPL0
|
||||||
#define brk_lock() _NOPL0("__threadcalls", brk_lock)
|
#define brk_lock() _NOPL0("__threadcalls", brk_lock)
|
||||||
#define brk_unlock() _NOPL0("__threadcalls", brk_unlock)
|
#define brk_unlock() _NOPL0("__threadcalls", brk_unlock)
|
||||||
#else
|
|
||||||
#define brk_lock() (__threaded ? brk_lock() : 0)
|
|
||||||
#define brk_unlock() (__threaded ? brk_unlock() : 0)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -301,7 +301,7 @@ static int Kill(void) {
|
||||||
int sig, rc = 0, i = 1;
|
int sig, rc = 0, i = 1;
|
||||||
if (i < n && args[i][0] == '-') {
|
if (i < n && args[i][0] == '-') {
|
||||||
sig = GetSignalByName(args[i++] + 1);
|
sig = GetSignalByName(args[i++] + 1);
|
||||||
if (!sig) return 1;
|
if (!sig) return -1; // fallback to system kill command
|
||||||
} else {
|
} else {
|
||||||
sig = SIGTERM;
|
sig = SIGTERM;
|
||||||
}
|
}
|
||||||
|
@ -340,15 +340,15 @@ static int Usleep(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Test(void) {
|
static int Test(void) {
|
||||||
int w;
|
int w, m = n;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (n && READ16LE(args[n - 1]) == READ16LE("]")) --n;
|
if (m && READ16LE(args[m - 1]) == READ16LE("]")) --m;
|
||||||
if (n == 4) {
|
if (m == 4) {
|
||||||
w = READ32LE(args[2]) & 0x00ffffff;
|
w = READ32LE(args[2]) & 0x00ffffff;
|
||||||
if ((w & 65535) == READ16LE("=")) return !!strcmp(args[1], args[3]);
|
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]);
|
||||||
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;
|
w = READ32LE(args[1]) & 0x00ffffff;
|
||||||
if (w == READ24("-n")) return !(strlen(args[2]) > 0);
|
if (w == READ24("-n")) return !(strlen(args[2]) > 0);
|
||||||
if (w == READ24("-z")) 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("-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));
|
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 **)) {
|
static int Fake(int main(int, char **)) {
|
||||||
|
@ -377,6 +422,7 @@ static int TryBuiltin(void) {
|
||||||
if (!n) return 0;
|
if (!n) return 0;
|
||||||
if (!strcmp(args[0], "exit")) Exit();
|
if (!strcmp(args[0], "exit")) Exit();
|
||||||
if (!strcmp(args[0], "cd")) return Cd();
|
if (!strcmp(args[0], "cd")) return Cd();
|
||||||
|
if (!strcmp(args[0], "rm")) return Rm();
|
||||||
if (!strcmp(args[0], "[")) return Test();
|
if (!strcmp(args[0], "[")) return Test();
|
||||||
if (!strcmp(args[0], "wait")) return Wait();
|
if (!strcmp(args[0], "wait")) return Wait();
|
||||||
if (!strcmp(args[0], "echo")) return Echo();
|
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], "true")) return True();
|
||||||
if (!strcmp(args[0], "test")) return Test();
|
if (!strcmp(args[0], "test")) return Test();
|
||||||
if (!strcmp(args[0], "kill")) return Kill();
|
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], "mkdir")) return Mkdir();
|
||||||
if (!strcmp(args[0], "false")) return False();
|
if (!strcmp(args[0], "false")) return False();
|
||||||
if (!strcmp(args[0], "usleep")) return Usleep();
|
if (!strcmp(args[0], "usleep")) return Usleep();
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "ape/sections.internal.h"
|
#include "ape/sections.internal.h"
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
#include "libc/intrin/strace.internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
|
@ -28,7 +29,7 @@ extern int __threadcalls_start[];
|
||||||
#pragma weak __threadcalls_start
|
#pragma weak __threadcalls_start
|
||||||
#pragma weak __threadcalls_end
|
#pragma weak __threadcalls_end
|
||||||
|
|
||||||
static privileged dontinline void FixupLocks(void) {
|
static privileged dontinline void FixupLockNops(void) {
|
||||||
__morph_begin();
|
__morph_begin();
|
||||||
/*
|
/*
|
||||||
* _NOPL("__threadcalls", func)
|
* _NOPL("__threadcalls", func)
|
||||||
|
@ -59,6 +60,6 @@ static privileged dontinline void FixupLocks(void) {
|
||||||
void __enable_threads(void) {
|
void __enable_threads(void) {
|
||||||
if (__threaded) return;
|
if (__threaded) return;
|
||||||
STRACE("__enable_threads()");
|
STRACE("__enable_threads()");
|
||||||
FixupLocks();
|
FixupLockNops();
|
||||||
__threaded = gettid();
|
__threaded = sys_gettid();
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,7 +273,7 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
|
||||||
char *p, forkvar[6 + 21 + 1 + 21 + 1];
|
char *p, forkvar[6 + 21 + 1 + 21 + 1];
|
||||||
struct NtProcessInformation procinfo;
|
struct NtProcessInformation procinfo;
|
||||||
if (!setjmp(jb)) {
|
if (!setjmp(jb)) {
|
||||||
pid = untrackpid = __reservefd(-1);
|
pid = untrackpid = __reservefd_unlocked(-1);
|
||||||
reader = CreateNamedPipe(CreatePipeName(pipename),
|
reader = CreateNamedPipe(CreatePipeName(pipename),
|
||||||
kNtPipeAccessInbound | kNtFileFlagOverlapped,
|
kNtPipeAccessInbound | kNtFileFlagOverlapped,
|
||||||
kNtPipeTypeMessage | kNtPipeReadmodeMessage, 1,
|
kNtPipeTypeMessage | kNtPipeReadmodeMessage, 1,
|
||||||
|
@ -347,7 +347,7 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
|
||||||
rc = 0;
|
rc = 0;
|
||||||
}
|
}
|
||||||
if (untrackpid != -1) {
|
if (untrackpid != -1) {
|
||||||
__releasefd(untrackpid);
|
__releasefd_unlocked(untrackpid);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/struct/sigset.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
#include "libc/calls/struct/sigset.internal.h"
|
#include "libc/calls/struct/sigset.internal.h"
|
||||||
|
@ -33,14 +34,15 @@
|
||||||
|
|
||||||
int _fork(uint32_t dwCreationFlags) {
|
int _fork(uint32_t dwCreationFlags) {
|
||||||
axdx_t ad;
|
axdx_t ad;
|
||||||
|
bool threaded;
|
||||||
sigset_t old, all;
|
sigset_t old, all;
|
||||||
int ax, dx, parent, parent_tid = 0;
|
int ax, dx, parent;
|
||||||
if (_weaken(_pthread_atfork)) {
|
sigfillset(&all);
|
||||||
parent_tid = gettid();
|
_unassert(!sigprocmask(SIG_BLOCK, &all, &old));
|
||||||
|
if (__threaded && _weaken(_pthread_onfork_prepare)) {
|
||||||
|
_weaken(_pthread_onfork_prepare)();
|
||||||
}
|
}
|
||||||
if (!IsWindows()) {
|
if (!IsWindows()) {
|
||||||
sigfillset(&all);
|
|
||||||
sys_sigprocmask(SIG_BLOCK, &all, &old);
|
|
||||||
ad = sys_fork();
|
ad = sys_fork();
|
||||||
ax = ad.ax;
|
ax = ad.ax;
|
||||||
dx = ad.dx;
|
dx = ad.dx;
|
||||||
|
@ -50,7 +52,11 @@ int _fork(uint32_t dwCreationFlags) {
|
||||||
ax &= dx - 1;
|
ax &= dx - 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
threaded = __threaded;
|
||||||
ax = sys_fork_nt(dwCreationFlags);
|
ax = sys_fork_nt(dwCreationFlags);
|
||||||
|
if (threaded && !__threaded && _weaken(__enable_threads)) {
|
||||||
|
_weaken(__enable_threads)();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!ax) {
|
if (!ax) {
|
||||||
if (!IsWindows()) {
|
if (!IsWindows()) {
|
||||||
|
@ -65,15 +71,17 @@ int _fork(uint32_t dwCreationFlags) {
|
||||||
IsLinux() ? dx : sys_gettid(),
|
IsLinux() ? dx : sys_gettid(),
|
||||||
memory_order_relaxed);
|
memory_order_relaxed);
|
||||||
}
|
}
|
||||||
if (_weaken(_pthread_atfork)) {
|
if (__threaded && _weaken(_pthread_onfork_child)) {
|
||||||
_weaken(_pthread_atfork)(parent_tid);
|
_weaken(_pthread_onfork_child)();
|
||||||
}
|
}
|
||||||
if (!IsWindows()) sys_sigprocmask(SIG_SETMASK, &old, 0);
|
|
||||||
STRACE("fork() → 0 (child of %d)", parent);
|
STRACE("fork() → 0 (child of %d)", parent);
|
||||||
} else {
|
} else {
|
||||||
if (!IsWindows()) sys_sigprocmask(SIG_SETMASK, &old, 0);
|
if (__threaded && _weaken(_pthread_onfork_parent)) {
|
||||||
|
_weaken(_pthread_onfork_parent)();
|
||||||
|
}
|
||||||
STRACE("fork() → %d% m", ax);
|
STRACE("fork() → %d% m", ax);
|
||||||
}
|
}
|
||||||
|
_unassert(!sigprocmask(SIG_SETMASK, &old, 0));
|
||||||
return ax;
|
return ax;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,19 +11,19 @@
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
#define kAutomapStart 0x100080040000
|
#define kAutomapStart 0x100080040000
|
||||||
#define kAutomapSize (kMemtrackStart - kAutomapStart)
|
#define kAutomapSize (kMemtrackStart - kAutomapStart)
|
||||||
#define kMemtrackStart 0x1fe7fffc0000
|
#define kMemtrackStart 0x1fe7fffc0000
|
||||||
#define kMemtrackSize (0x1ffffffc0000 - kMemtrackStart)
|
#define kMemtrackSize (0x1ffffffc0000 - kMemtrackStart)
|
||||||
#define kFixedmapStart 0x300000040000
|
#define kFixedmapStart 0x300000040000
|
||||||
#define kFixedmapSize (0x400000040000 - kFixedmapStart)
|
#define kFixedmapSize (0x400000040000 - kFixedmapStart)
|
||||||
#define kMemtrackFdsStart 0x6fe000040000
|
#define kMemtrackFdsStart 0x6fe000040000
|
||||||
#define kMemtrackFdsSize (0x6feffffc0000 - kMemtrackFdsStart)
|
#define kMemtrackFdsSize (0x6feffffc0000 - kMemtrackFdsStart)
|
||||||
#define kMemtrackZiposStart 0x6fd000040000
|
#define kMemtrackZiposStart 0x6fd000040000
|
||||||
#define kMemtrackZiposSize (0x6fdffffc0000 - kMemtrackZiposStart)
|
#define kMemtrackZiposSize (0x6fdffffc0000 - kMemtrackZiposStart)
|
||||||
#define kMemtrackNsyncStart 0x6fc000040000
|
#define kMemtrackKmallocStart 0x6fc000040000
|
||||||
#define kMemtrackNsyncSize (0x6fcffffc0000 - kMemtrackNsyncStart)
|
#define kMemtrackKmallocSize (0x6fcffffc0000 - kMemtrackKmallocStart)
|
||||||
#define kMemtrackGran (!IsAsan() ? FRAMESIZE : FRAMESIZE * 8)
|
#define kMemtrackGran (!IsAsan() ? FRAMESIZE : FRAMESIZE * 8)
|
||||||
|
|
||||||
struct MemoryInterval {
|
struct MemoryInterval {
|
||||||
int x;
|
int x;
|
||||||
|
@ -47,6 +47,7 @@ extern struct MemoryIntervals _mmi;
|
||||||
|
|
||||||
void __mmi_lock(void);
|
void __mmi_lock(void);
|
||||||
void __mmi_unlock(void);
|
void __mmi_unlock(void);
|
||||||
|
void __mmi_funlock(void);
|
||||||
bool IsMemtracked(int, int);
|
bool IsMemtracked(int, int);
|
||||||
void PrintSystemMappings(int);
|
void PrintSystemMappings(int);
|
||||||
unsigned FindMemoryInterval(const struct MemoryIntervals *, int) nosideeffect;
|
unsigned FindMemoryInterval(const struct MemoryIntervals *, int) nosideeffect;
|
||||||
|
@ -95,9 +96,9 @@ forceinline pureconst bool IsZiposFrame(int x) {
|
||||||
x <= (int)((kMemtrackZiposStart + kMemtrackZiposSize - 1) >> 16);
|
x <= (int)((kMemtrackZiposStart + kMemtrackZiposSize - 1) >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
forceinline pureconst bool IsNsyncFrame(int x) {
|
forceinline pureconst bool IsKmallocFrame(int x) {
|
||||||
return (int)(kMemtrackNsyncStart >> 16) <= x &&
|
return (int)(kMemtrackKmallocStart >> 16) <= x &&
|
||||||
x <= (int)((kMemtrackNsyncStart + kMemtrackNsyncSize - 1) >> 16);
|
x <= (int)((kMemtrackKmallocStart + kMemtrackKmallocSize - 1) >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
forceinline pureconst bool IsShadowFrame(int x) {
|
forceinline pureconst bool IsShadowFrame(int x) {
|
||||||
|
|
|
@ -1808,7 +1808,7 @@
|
||||||
6f900000-6f9fffff 64gb free
|
6f900000-6f9fffff 64gb free
|
||||||
6fa00000-6fafffff 64gb free
|
6fa00000-6fafffff 64gb free
|
||||||
6fb00000-6fbfffff 64gb free
|
6fb00000-6fbfffff 64gb free
|
||||||
6fc00004-6fcffffb 64gb nsync
|
6fc00004-6fcffffb 64gb kmalloc
|
||||||
6fd00004-6fdffffb 64gb zipos
|
6fd00004-6fdffffb 64gb zipos
|
||||||
6fe00004-6feffffb 64gb g_fds
|
6fe00004-6feffffb 64gb g_fds
|
||||||
6ff00004-70000003 64gb free
|
6ff00004-70000003 64gb free
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
#include "libc/intrin/strace.internal.h"
|
||||||
|
#include "libc/thread/tls.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
.privileged
|
.privileged
|
||||||
|
|
||||||
|
@ -26,7 +27,10 @@
|
||||||
// This is the same as fork() except it's optimized for the case
|
// This is the same as fork() except it's optimized for the case
|
||||||
// where the caller invokes execve() immediately afterwards. You
|
// where the caller invokes execve() immediately afterwards. You
|
||||||
// can also call functions like close(), dup2(), etc. Call _exit
|
// 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
|
// Do not make the assumption that the parent is suspended until
|
||||||
// the child terminates since this impl calls fork() on Windows,
|
// the child terminates since this impl calls fork() on Windows,
|
||||||
|
@ -34,8 +38,10 @@
|
||||||
//
|
//
|
||||||
// @return pid of child process or 0 if forked process
|
// @return pid of child process or 0 if forked process
|
||||||
// @returnstwice
|
// @returnstwice
|
||||||
|
// @threadsafe
|
||||||
// @vforksafe
|
// @vforksafe
|
||||||
vfork: xor %edi,%edi # dwCreationFlags
|
vfork: call __require_tls
|
||||||
|
xor %edi,%edi # dwCreationFlags
|
||||||
#ifdef __SANITIZE_ADDRESS__
|
#ifdef __SANITIZE_ADDRESS__
|
||||||
jmp fork # TODO: asan and vfork don't mix?
|
jmp fork # TODO: asan and vfork don't mix?
|
||||||
.endfn vfork,globl
|
.endfn vfork,globl
|
||||||
|
@ -56,45 +62,31 @@ vfork: xor %edi,%edi # dwCreationFlags
|
||||||
ezlea .Llog,di
|
ezlea .Llog,di
|
||||||
call __stracef
|
call __stracef
|
||||||
#endif /* SYSDEBUG */
|
#endif /* SYSDEBUG */
|
||||||
mov __NR_vfork(%rip),%eax
|
mov %fs:0,%r9 # get thread information block
|
||||||
mov __errno(%rip),%r8d # avoid question of @vforksafe errno
|
mov 0x3c(%r9),%r8d # avoid question of @vforksafe errno
|
||||||
pop %rsi # saves return address in a register
|
pop %rsi # saves return address in a register
|
||||||
|
mov __NR_vfork(%rip),%eax
|
||||||
#if SupportsBsd()
|
#if SupportsBsd()
|
||||||
testb IsBsd()
|
clc
|
||||||
jnz vfork.bsd
|
|
||||||
#endif
|
#endif
|
||||||
syscall
|
syscall
|
||||||
|
#if SupportsBsd()
|
||||||
|
jnc 0f
|
||||||
|
neg %rax
|
||||||
|
0:
|
||||||
|
#endif
|
||||||
push %rsi # note it happens twice in same page
|
push %rsi # note it happens twice in same page
|
||||||
#if SupportsLinux()
|
|
||||||
cmp $-4095,%eax
|
cmp $-4095,%eax
|
||||||
jae systemfive_error
|
jae systemfive_error
|
||||||
#endif
|
mov %r8d,0x3c(%r9) # restore errno
|
||||||
0: mov %r8d,__errno(%rip)
|
|
||||||
ezlea __vforked,di
|
|
||||||
test %eax,%eax
|
test %eax,%eax
|
||||||
jz 1f
|
jnz .Lprnt
|
||||||
decl (%rdi)
|
.Lchld: orb $TIB_FLAG_VFORKED,0x40(%r9)
|
||||||
jns 2f # openbsd doesn't actually share mem
|
ret
|
||||||
1: incl (%rdi)
|
.Lprnt: andb $~TIB_FLAG_VFORKED,0x40(%r9)
|
||||||
2: ret
|
ret
|
||||||
.endfn vfork,globl
|
.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
|
#ifdef SYSDEBUG
|
||||||
.rodata.str1.1
|
.rodata.str1.1
|
||||||
.Llog: .ascii STRACE_PROLOGUE
|
.Llog: .ascii STRACE_PROLOGUE
|
||||||
|
|
|
@ -117,6 +117,8 @@ struct dirent_netbsd {
|
||||||
char d_name[512];
|
char d_name[512];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO(jart): wipe these locks when forking
|
||||||
|
|
||||||
void _lockdir(DIR *dir) {
|
void _lockdir(DIR *dir) {
|
||||||
pthread_mutex_lock(&dir->lock);
|
pthread_mutex_lock(&dir->lock);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,43 @@ void(__fflush_unlock)(void) {
|
||||||
pthread_mutex_unlock(&__fflush_lock_obj);
|
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.
|
* Blocks until data from stream buffer is written out.
|
||||||
*
|
*
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/stdio/posix_spawn.h"
|
#include "libc/stdio/posix_spawn.h"
|
||||||
#include "libc/stdio/posix_spawn.internal.h"
|
#include "libc/stdio/posix_spawn.internal.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
|
|
||||||
static int RunFileActions(struct _posix_faction *a) {
|
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 pid if non-null shall be set to child pid on success
|
||||||
* @param path is resolved path of program which is not `$PATH` searched
|
* @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
|
* @param envp is environment variables, or `environ` if null
|
||||||
* @return 0 on success or error number on failure
|
* @return 0 on success or error number on failure
|
||||||
* @see posix_spawnp() for `$PATH` searching
|
* @see posix_spawnp() for `$PATH` searching
|
||||||
|
* @tlsrequired
|
||||||
|
* @threadsafe
|
||||||
*/
|
*/
|
||||||
int posix_spawn(int *pid, const char *path,
|
int posix_spawn(int *pid, const char *path,
|
||||||
const posix_spawn_file_actions_t *file_actions,
|
const posix_spawn_file_actions_t *file_actions,
|
||||||
const posix_spawnattr_t *attrp, char *const argv[],
|
const posix_spawnattr_t *attrp, char *const argv[],
|
||||||
char *const envp[]) {
|
char *const envp[]) {
|
||||||
int s, child;
|
short flags = 0;
|
||||||
sigset_t allsigs;
|
sigset_t sigmask;
|
||||||
struct sigaction dfl;
|
int s, child, policy;
|
||||||
if (!(child = _weaken(pthread_create) ? fork() : vfork())) {
|
struct sched_param param;
|
||||||
|
struct sigaction dfl = {0};
|
||||||
|
if (!(child = vfork())) {
|
||||||
if (attrp && *attrp) {
|
if (attrp && *attrp) {
|
||||||
if ((*attrp)->flags & POSIX_SPAWN_SETPGROUP) {
|
posix_spawnattr_getflags(attrp, &flags);
|
||||||
if (setpgid(0, (*attrp)->pgroup)) _Exit(127);
|
if (flags & POSIX_SPAWN_SETPGROUP) {
|
||||||
|
if (setpgid(0, (*attrp)->pgroup)) {
|
||||||
|
_Exit(127);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ((*attrp)->flags & POSIX_SPAWN_SETSIGMASK) {
|
if (flags & POSIX_SPAWN_SETSIGMASK) {
|
||||||
sigprocmask(SIG_SETMASK, &(*attrp)->sigmask, 0);
|
posix_spawnattr_getsigmask(attrp, &sigmask);
|
||||||
|
sigprocmask(SIG_SETMASK, &sigmask, 0);
|
||||||
}
|
}
|
||||||
if ((*attrp)->flags & POSIX_SPAWN_RESETIDS) {
|
if (flags & POSIX_SPAWN_RESETIDS) {
|
||||||
setuid(getuid());
|
setuid(getuid());
|
||||||
setgid(getgid());
|
setgid(getgid());
|
||||||
}
|
}
|
||||||
if ((*attrp)->flags & POSIX_SPAWN_SETSIGDEF) {
|
if (flags & POSIX_SPAWN_SETSIGDEF) {
|
||||||
dfl.sa_handler = SIG_DFL;
|
for (s = 1; s < 32; s++) {
|
||||||
dfl.sa_flags = 0;
|
|
||||||
sigfillset(&allsigs);
|
|
||||||
for (s = 0; sigismember(&allsigs, s); s++) {
|
|
||||||
if (sigismember(&(*attrp)->sigdefault, 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 && *attrp) {
|
||||||
if ((*attrp)->flags & POSIX_SPAWN_SETSCHEDULER) {
|
if (flags & POSIX_SPAWN_SETSCHEDULER) {
|
||||||
if (sched_setscheduler(0, (*attrp)->schedpolicy,
|
posix_spawnattr_getschedpolicy(attrp, &policy);
|
||||||
&(*attrp)->schedparam) == -1) {
|
posix_spawnattr_getschedparam(attrp, ¶m);
|
||||||
if (errno != ENOSYS) _Exit(127);
|
if (sched_setscheduler(0, policy, ¶m) == -1) {
|
||||||
|
_Exit(127);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((*attrp)->flags & POSIX_SPAWN_SETSCHEDPARAM) {
|
if (flags & POSIX_SPAWN_SETSCHEDPARAM) {
|
||||||
if (sched_setparam(0, &(*attrp)->schedparam) == -1) {
|
posix_spawnattr_getschedparam(attrp, ¶m);
|
||||||
if (errno != ENOSYS) _Exit(127);
|
if (sched_setparam(0, ¶m) == -1) {
|
||||||
|
_Exit(127);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,10 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/struct/sigset.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/stdio/posix_spawn.h"
|
#include "libc/stdio/posix_spawn.h"
|
||||||
|
@ -32,19 +34,15 @@
|
||||||
* @raise ENOMEM if we require more vespene gas
|
* @raise ENOMEM if we require more vespene gas
|
||||||
*/
|
*/
|
||||||
int posix_spawnattr_init(posix_spawnattr_t *attr) {
|
int posix_spawnattr_init(posix_spawnattr_t *attr) {
|
||||||
int e, rc;
|
int rc, e = errno;
|
||||||
struct _posix_spawna *a;
|
struct _posix_spawna *a;
|
||||||
e = errno;
|
if ((a = calloc(1, sizeof(struct _posix_spawna)))) {
|
||||||
errno = 0;
|
*attr = a;
|
||||||
if ((a = calloc(1, sizeof(*a)))) {
|
rc = 0;
|
||||||
a->flags = 0;
|
} else {
|
||||||
a->pgroup = 0;
|
rc = errno;
|
||||||
sigemptyset(&a->sigdefault);
|
errno = e;
|
||||||
a->schedpolicy = sched_getscheduler(0);
|
|
||||||
sched_getparam(0, &a->schedparam);
|
|
||||||
}
|
}
|
||||||
rc = errno;
|
|
||||||
errno = e;
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +77,9 @@ int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags) {
|
||||||
* Sets posix_spawn() flags.
|
* Sets posix_spawn() flags.
|
||||||
*
|
*
|
||||||
* Setting these flags is needed in order for the other setters in this
|
* 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 attr was initialized by posix_spawnattr_init()
|
||||||
* @param flags may have any of the following
|
* @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
|
* @raise EINVAL if `flags` has invalid bits
|
||||||
*/
|
*/
|
||||||
int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) {
|
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 |
|
if (flags & ~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP |
|
||||||
POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK |
|
POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK |
|
||||||
POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) {
|
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 attr was initialized by posix_spawnattr_init()
|
||||||
* @param schedpolicy receives the result
|
* @param schedpolicy receives the result
|
||||||
* @return 0 on success, or errno on error
|
* @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 posix_spawnattr_getschedpolicy(const posix_spawnattr_t *attr,
|
||||||
int *schedpolicy) {
|
int *schedpolicy) {
|
||||||
if (!(*attr)->schedpolicy_isset) {
|
int rc, e = errno;
|
||||||
(*attr)->schedpolicy = sched_getscheduler(0);
|
struct _posix_spawna *a = *(/*unconst*/ posix_spawnattr_t *)attr;
|
||||||
(*attr)->schedpolicy_isset = true;
|
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;
|
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 attr was initialized by posix_spawnattr_init()
|
||||||
* @param schedparam receives the result
|
* @param schedparam receives the result
|
||||||
* @return 0 on success, or errno on error
|
* @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,
|
int posix_spawnattr_getschedparam(const posix_spawnattr_t *attr,
|
||||||
struct sched_param *schedparam) {
|
struct sched_param *schedparam) {
|
||||||
if (!(*attr)->schedparam_isset) {
|
int rc, e = errno;
|
||||||
sched_getparam(0, &(*attr)->schedparam);
|
struct _posix_spawna *a = *(/*unconst*/ posix_spawnattr_t *)attr;
|
||||||
(*attr)->schedparam_isset = true;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,16 +203,32 @@ int posix_spawnattr_setschedparam(posix_spawnattr_t *attr,
|
||||||
return 0;
|
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,
|
int posix_spawnattr_getsigmask(const posix_spawnattr_t *attr,
|
||||||
sigset_t *sigmask) {
|
sigset_t *sigmask) {
|
||||||
if (!(*attr)->sigmask_isset) {
|
struct _posix_spawna *a = *(/*unconst*/ posix_spawnattr_t *)attr;
|
||||||
sigprocmask(SIG_SETMASK, 0, &(*attr)->sigmask);
|
if (!a->sigmask_isset) {
|
||||||
(*attr)->sigmask_isset = true;
|
_npassert(!sigprocmask(SIG_SETMASK, 0, &a->sigmask));
|
||||||
|
a->sigmask_isset = true;
|
||||||
}
|
}
|
||||||
*sigmask = (*attr)->sigmask;
|
*sigmask = a->sigmask;
|
||||||
return 0;
|
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,
|
int posix_spawnattr_setsigmask(posix_spawnattr_t *attr,
|
||||||
const sigset_t *sigmask) {
|
const sigset_t *sigmask) {
|
||||||
(*attr)->sigmask = *sigmask;
|
(*attr)->sigmask = *sigmask;
|
||||||
|
@ -200,12 +236,22 @@ int posix_spawnattr_setsigmask(posix_spawnattr_t *attr,
|
||||||
return 0;
|
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,
|
int posix_spawnattr_getsigdefault(const posix_spawnattr_t *attr,
|
||||||
sigset_t *sigdefault) {
|
sigset_t *sigdefault) {
|
||||||
*sigdefault = (*attr)->sigdefault;
|
*sigdefault = (*attr)->sigdefault;
|
||||||
return 0;
|
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,
|
int posix_spawnattr_setsigdefault(posix_spawnattr_t *attr,
|
||||||
const sigset_t *sigdefault) {
|
const sigset_t *sigdefault) {
|
||||||
(*attr)->sigdefault = *sigdefault;
|
(*attr)->sigdefault = *sigdefault;
|
||||||
|
|
|
@ -166,6 +166,10 @@ int BLAKE2B256_Final(struct Blake2b *b2b,
|
||||||
* blake2b256 n=256 1 ns/byte 662 mb/s
|
* blake2b256 n=256 1 ns/byte 662 mb/s
|
||||||
* blake2b256 n=22851 1 ns/byte 683 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,
|
int BLAKE2B256(const void *data, size_t len,
|
||||||
uint8_t out[BLAKE2B256_DIGEST_LENGTH]) {
|
uint8_t out[BLAKE2B256_DIGEST_LENGTH]) {
|
||||||
|
|
|
@ -16,20 +16,25 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/thread/semaphore.h"
|
|
||||||
#include "libc/thread/semaphore.internal.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes named semaphore.
|
* Turns data into lowercase hex.
|
||||||
*
|
*
|
||||||
* @param name can be absolute path or should be component w/o slashes
|
* This routine always writes a nul terminator, even if `n` is zero.
|
||||||
* @return 0 on success, or -1 w/ errno
|
* There's no failure condition for this function.
|
||||||
* @raise EPERM if pledge() is in play w/o `cpath` promise
|
*
|
||||||
* @raise ENOENT if named semaphore doesn't exist
|
* @param s must have `n*2+1` bytes
|
||||||
* @raise EACCES if permission is denied
|
* @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 *hexpcpy(char *restrict s, const void *restrict p, size_t n) {
|
||||||
char path[PATH_MAX];
|
const char *d, *e;
|
||||||
return unlink(__sem_name(name, path));
|
for (d = p, e = d + n; d < e; ++d) {
|
||||||
|
*s++ = "0123456789abcdef"[(*d >> 4) & 15];
|
||||||
|
*s++ = "0123456789abcdef"[(*d >> 0) & 15];
|
||||||
|
}
|
||||||
|
*s = 0;
|
||||||
|
return s;
|
||||||
}
|
}
|
|
@ -46,6 +46,7 @@ void *memset(void *, int, size_t) memcpyesque;
|
||||||
void *memmove(void *, const void *, size_t) memcpyesque;
|
void *memmove(void *, const void *, size_t) memcpyesque;
|
||||||
void *memcpy(void *restrict, const void *restrict, size_t) memcpyesque;
|
void *memcpy(void *restrict, const void *restrict, size_t) memcpyesque;
|
||||||
void *mempcpy(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 *memccpy(void *restrict, const void *restrict, int, size_t) memcpyesque;
|
||||||
void bcopy(const void *, void *, size_t) memcpyesque;
|
void bcopy(const void *, void *, size_t) memcpyesque;
|
||||||
void explicit_bzero(void *, size_t);
|
void explicit_bzero(void *, size_t);
|
||||||
|
|
|
@ -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_WANPIPE 25 0 0 0 0 0
|
||||||
syscon pf PF_X25 9 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
|
# getdents() constants
|
||||||
#
|
#
|
||||||
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
|
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX_CANTCREAT,73,73,73,73,73,73
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX_CONFIG,78,78,78,78,78,78
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX_NOHOST,68,68,68,68,68,68
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX_NOINPUT,66,66,66,66,66,66
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX_NOPERM,77,77,77,77,77,77
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX_NOUSER,67,67,67,67,67,67
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX_OK,0,0,0,0,0,0
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX_OSERR,71,71,71,71,71,71
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX_OSFILE,72,72,72,72,72,72
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX_PROTOCOL,76,76,76,76,76,76
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX_SOFTWARE,70,70,70,70,70,70
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX_TEMPFAIL,75,75,75,75,75,75
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX_UNAVAILABLE,69,69,69,69,69,69
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX_USAGE,64,64,64,64,64,64
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX__BASE,64,64,64,64,64,64
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
|
||||||
.syscon ex,EX__MAX,78,78,78,78,78,78
|
|
2
libc/sysv/consts/LINK_MAX.s
Normal file
2
libc/sysv/consts/LINK_MAX.s
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.include "o/libc/sysv/consts/syscon.internal.inc"
|
||||||
|
.syscon limits,LINK_MAX,127,32767,32767,32767,32767,64
|
|
@ -1,2 +1,2 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
.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
|
|
@ -1,2 +1,2 @@
|
||||||
.include "o/libc/sysv/consts/syscon.internal.inc"
|
.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
|
|
@ -1,25 +1,29 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_CPIO_H_
|
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_CPIO_H_
|
||||||
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_CPIO_H_
|
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_CPIO_H_
|
||||||
|
|
||||||
#define C_IXOTH 0000001
|
#define MAGIC "070707"
|
||||||
#define C_IWOTH 0000002
|
|
||||||
#define C_IROTH 0000004
|
#define C_IRUSR 000400
|
||||||
#define C_IXGRP 0000010
|
#define C_IWUSR 000200
|
||||||
#define C_IWGRP 0000020
|
#define C_IXUSR 000100
|
||||||
#define C_IRGRP 0000040
|
#define C_IRGRP 000040
|
||||||
#define C_IXUSR 0000100
|
#define C_IWGRP 000020
|
||||||
#define C_IWUSR 0000200
|
#define C_IXGRP 000010
|
||||||
#define C_IRUSR 0000400
|
#define C_IROTH 000004
|
||||||
#define C_ISVTX 0001000
|
#define C_IWOTH 000002
|
||||||
#define C_ISGID 0002000
|
#define C_IXOTH 000001
|
||||||
#define C_ISUID 0004000
|
|
||||||
#define C_ISFIFO 0010000
|
#define C_ISUID 004000
|
||||||
#define C_ISCHR 0020000
|
#define C_ISGID 002000
|
||||||
#define C_ISDIR 0040000
|
#define C_ISVTX 001000
|
||||||
#define C_ISBLK 0060000
|
|
||||||
#define C_ISREG 0100000
|
#define C_ISBLK 060000
|
||||||
#define C_ISCTG 0110000
|
#define C_ISCHR 020000
|
||||||
#define C_ISLNK 0120000
|
#define C_ISDIR 040000
|
||||||
|
#define C_ISFIFO 010000
|
||||||
#define C_ISSOCK 0140000
|
#define C_ISSOCK 0140000
|
||||||
|
#define C_ISLNK 0120000
|
||||||
|
#define C_ISCTG 0110000
|
||||||
|
#define C_ISREG 0100000
|
||||||
|
|
||||||
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_CPIO_H_ */
|
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_CPIO_H_ */
|
||||||
|
|
|
@ -1,48 +1,32 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_EX_H_
|
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_EX_H_
|
||||||
#define 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;
|
* @fileoverview Eric Allman's exit() codes
|
||||||
extern const int EX_DATAERR;
|
*
|
||||||
extern const int EX_IOERR;
|
* - Broadly supported style guideline;
|
||||||
extern const int EX_NOHOST;
|
* - Dating back to 1980 in 4.0BSD;
|
||||||
extern const int EX_NOINPUT;
|
* - That won't be standardized.
|
||||||
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;
|
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
#define EX_CANTCREAT 73
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#define EX_CONFIG 78
|
||||||
|
#define EX_DATAERR 65
|
||||||
#define EX_CANTCREAT LITERALLY(73)
|
#define EX_IOERR 74
|
||||||
#define EX_CONFIG LITERALLY(78)
|
#define EX_NOHOST 68
|
||||||
#define EX_DATAERR LITERALLY(65)
|
#define EX_NOINPUT 66
|
||||||
#define EX_IOERR LITERALLY(74)
|
#define EX_NOPERM 77
|
||||||
#define EX_NOHOST LITERALLY(68)
|
#define EX_NOUSER 67
|
||||||
#define EX_NOINPUT LITERALLY(66)
|
#define EX_OK 0
|
||||||
#define EX_NOPERM LITERALLY(77)
|
#define EX_OSERR 71
|
||||||
#define EX_NOUSER LITERALLY(67)
|
#define EX_OSFILE 72
|
||||||
#define EX_OK LITERALLY(0)
|
#define EX_PROTOCOL 76
|
||||||
#define EX_OSERR LITERALLY(71)
|
#define EX_SOFTWARE 70
|
||||||
#define EX_OSFILE LITERALLY(72)
|
#define EX_TEMPFAIL 75
|
||||||
#define EX_PROTOCOL LITERALLY(76)
|
#define EX_UNAVAILABLE 69
|
||||||
#define EX_SOFTWARE LITERALLY(70)
|
#define EX_USAGE 64
|
||||||
#define EX_TEMPFAIL LITERALLY(75)
|
#define EX__BASE 64
|
||||||
#define EX_UNAVAILABLE LITERALLY(69)
|
#define EX__MAX 78
|
||||||
#define EX_USAGE LITERALLY(64)
|
|
||||||
#define EX__BASE LITERALLY(64)
|
|
||||||
#define EX__MAX LITERALLY(78)
|
|
||||||
|
|
||||||
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_EX_H_ */
|
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_EX_H_ */
|
||||||
|
|
|
@ -63,7 +63,7 @@ void testlib_finish(void) {
|
||||||
void testlib_error_enter(const char *file, const char *func) {
|
void testlib_error_enter(const char *file, const char *func) {
|
||||||
atomic_fetch_sub_explicit(&__ftrace, 1, memory_order_relaxed);
|
atomic_fetch_sub_explicit(&__ftrace, 1, memory_order_relaxed);
|
||||||
atomic_fetch_sub_explicit(&__strace, 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(); /* make strace easier to read */
|
||||||
if (!IsWindows()) sys_getpid();
|
if (!IsWindows()) sys_getpid();
|
||||||
if (g_testlib_shoulddebugbreak) {
|
if (g_testlib_shoulddebugbreak) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
|
#ifndef COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
|
||||||
#define COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
|
#define COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
|
||||||
#include "libc/calls/struct/sched_param.h"
|
#include "libc/calls/struct/sched_param.h"
|
||||||
|
#include "libc/calls/struct/sigset.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
|
@ -15,6 +16,8 @@ COSMOPOLITAN_C_START_
|
||||||
* @fileoverview Cosmopolitan POSIX Thread Internals
|
* @fileoverview Cosmopolitan POSIX Thread Internals
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
typedef void (*atfork_f)(void);
|
||||||
|
|
||||||
// LEGAL TRANSITIONS ┌──> TERMINATED
|
// LEGAL TRANSITIONS ┌──> TERMINATED
|
||||||
// pthread_create ─┬─> JOINABLE ─┴┬─> DETACHED ──> ZOMBIE
|
// pthread_create ─┬─> JOINABLE ─┴┬─> DETACHED ──> ZOMBIE
|
||||||
// └──────────────┘
|
// └──────────────┘
|
||||||
|
@ -73,6 +76,7 @@ struct PosixThread {
|
||||||
struct CosmoTib *tib; // middle of tls allocation
|
struct CosmoTib *tib; // middle of tls allocation
|
||||||
jmp_buf exiter; // for pthread_exit
|
jmp_buf exiter; // for pthread_exit
|
||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
|
sigset_t sigmask;
|
||||||
struct _pthread_cleanup_buffer *cleanup;
|
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 pthread_key_dtor _pthread_key_dtor[PTHREAD_KEYS_MAX];
|
||||||
hidden extern _Thread_local void *_pthread_keys[PTHREAD_KEYS_MAX];
|
hidden extern _Thread_local void *_pthread_keys[PTHREAD_KEYS_MAX];
|
||||||
|
|
||||||
void _pthread_atfork(int) hidden;
|
int _pthread_atfork(atfork_f, atfork_f, atfork_f) hidden;
|
||||||
void _pthread_funlock(pthread_mutex_t *, int) hidden;
|
|
||||||
int _pthread_reschedule(struct PosixThread *) hidden;
|
int _pthread_reschedule(struct PosixThread *) hidden;
|
||||||
int _pthread_setschedparam_freebsd(int, int, const struct sched_param *) hidden;
|
int _pthread_setschedparam_freebsd(int, int, const struct sched_param *) hidden;
|
||||||
void _pthread_free(struct PosixThread *) hidden;
|
void _pthread_free(struct PosixThread *) hidden;
|
||||||
|
@ -91,9 +94,15 @@ void _pthread_cleanup(struct PosixThread *) hidden;
|
||||||
void _pthread_ungarbage(void) hidden;
|
void _pthread_ungarbage(void) hidden;
|
||||||
void _pthread_wait(struct PosixThread *) hidden;
|
void _pthread_wait(struct PosixThread *) hidden;
|
||||||
void _pthread_zombies_add(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_decimate(void) hidden;
|
||||||
void _pthread_zombies_harvest(void) hidden;
|
void _pthread_zombies_harvest(void) hidden;
|
||||||
void _pthread_key_destruct(void *[PTHREAD_KEYS_MAX]) 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_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
79
libc/thread/pthread_atfork.c
Normal file
79
libc/thread/pthread_atfork.c
Normal 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;
|
||||||
|
}
|
|
@ -21,6 +21,7 @@
|
||||||
#include "libc/calls/sched-sysv.internal.h"
|
#include "libc/calls/sched-sysv.internal.h"
|
||||||
#include "libc/calls/state.internal.h"
|
#include "libc/calls/state.internal.h"
|
||||||
#include "libc/calls/struct/sigaltstack.h"
|
#include "libc/calls/struct/sigaltstack.h"
|
||||||
|
#include "libc/calls/struct/sigset.h"
|
||||||
#include "libc/calls/syscall-sysv.internal.h"
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
@ -41,6 +42,7 @@
|
||||||
#include "libc/sysv/consts/clone.h"
|
#include "libc/sysv/consts/clone.h"
|
||||||
#include "libc/sysv/consts/map.h"
|
#include "libc/sysv/consts/map.h"
|
||||||
#include "libc/sysv/consts/prot.h"
|
#include "libc/sysv/consts/prot.h"
|
||||||
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/sysv/consts/ss.h"
|
#include "libc/sysv/consts/ss.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
|
@ -52,6 +54,7 @@
|
||||||
|
|
||||||
STATIC_YOINK("nsync_mu_lock");
|
STATIC_YOINK("nsync_mu_lock");
|
||||||
STATIC_YOINK("nsync_mu_unlock");
|
STATIC_YOINK("nsync_mu_unlock");
|
||||||
|
STATIC_YOINK("_pthread_atfork");
|
||||||
|
|
||||||
#define MAP_ANON_OPENBSD 0x1000
|
#define MAP_ANON_OPENBSD 0x1000
|
||||||
#define MAP_STACK_OPENBSD 0x4000
|
#define MAP_STACK_OPENBSD 0x4000
|
||||||
|
@ -76,30 +79,6 @@ void _pthread_free(struct PosixThread *pt) {
|
||||||
free(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) {
|
static int PosixThread(void *arg, int tid) {
|
||||||
struct PosixThread *pt = arg;
|
struct PosixThread *pt = arg;
|
||||||
enum PosixThreadStatus status;
|
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
|
// set long jump handler so pthread_exit can bring control back here
|
||||||
if (!setjmp(pt->exiter)) {
|
if (!setjmp(pt->exiter)) {
|
||||||
__get_tls()->tib_pthread = (pthread_t)pt;
|
__get_tls()->tib_pthread = (pthread_t)pt;
|
||||||
|
sigprocmask(SIG_SETMASK, &pt->sigmask, 0);
|
||||||
pt->rc = pt->start_routine(pt->arg);
|
pt->rc = pt->start_routine(pt->arg);
|
||||||
// ensure pthread_cleanup_pop(), and pthread_exit() popped cleanup
|
// ensure pthread_cleanup_pop(), and pthread_exit() popped cleanup
|
||||||
_npassert(!pt->cleanup);
|
_npassert(!pt->cleanup);
|
||||||
|
@ -158,62 +138,12 @@ static int FixupCustomStackOnOpenbsd(pthread_attr_t *attr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static errno_t pthread_create_impl(pthread_t *thread,
|
||||||
* Creates thread, e.g.
|
const pthread_attr_t *attr,
|
||||||
*
|
void *(*start_routine)(void *), void *arg,
|
||||||
* void *worker(void *arg) {
|
sigset_t oldsigs) {
|
||||||
* 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) {
|
|
||||||
int rc, e = errno;
|
int rc, e = errno;
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
__require_tls();
|
|
||||||
_pthread_zombies_decimate();
|
|
||||||
|
|
||||||
// create posix thread object
|
// create posix thread object
|
||||||
if (!(pt = calloc(1, sizeof(struct PosixThread)))) {
|
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
|
// launch PosixThread(pt) in new thread
|
||||||
|
pt->sigmask = oldsigs;
|
||||||
if (clone(PosixThread, pt->attr.__stackaddr,
|
if (clone(PosixThread, pt->attr.__stackaddr,
|
||||||
pt->attr.__stacksize - (IsOpenbsd() ? 16 : 0),
|
pt->attr.__stacksize - (IsOpenbsd() ? 16 : 0),
|
||||||
CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
|
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;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -23,9 +23,7 @@
|
||||||
#include "libc/thread/spawn.h"
|
#include "libc/thread/spawn.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
/**
|
// TODO(jart): track all threads, not just zombies
|
||||||
* @fileoverview Memory collector for detached threads.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static struct Zombie {
|
static struct Zombie {
|
||||||
struct Zombie *next;
|
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) {
|
__attribute__((__constructor__)) static void init(void) {
|
||||||
atexit(_pthread_zombies_harvest);
|
atexit(_pthread_zombies_harvest);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,17 +16,36 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/assert.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/semaphore.h"
|
#include "libc/thread/semaphore.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys unnamed semaphore.
|
* 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()
|
* @param sem was created by sem_init()
|
||||||
* @return 0 on success, or -1 w/ errno
|
* @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 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);
|
atomic_store_explicit(&sem->sem_value, INT_MIN, memory_order_relaxed);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/assert.h"
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/thread/semaphore.h"
|
#include "libc/thread/semaphore.h"
|
||||||
|
|
||||||
|
@ -27,6 +29,7 @@
|
||||||
* @return 0 on success, or -1 w/ errno
|
* @return 0 on success, or -1 w/ errno
|
||||||
*/
|
*/
|
||||||
int sem_getvalue(sem_t *sem, int *sval) {
|
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);
|
*sval = atomic_load_explicit(&sem->sem_value, memory_order_relaxed);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
@ -25,15 +27,25 @@
|
||||||
/**
|
/**
|
||||||
* Initializes unnamed semaphore.
|
* 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 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
|
* @param value is initial count of semaphore
|
||||||
* @return 0 on success, or -1 w/ errno
|
* @return 0 on success, or -1 w/ errno
|
||||||
* @raise EINVAL if `value` exceeds `SEM_VALUE_MAX`
|
* @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) {
|
int sem_init(sem_t *sem, int pshared, unsigned value) {
|
||||||
if (value > SEM_VALUE_MAX) return einval();
|
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);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,60 +18,298 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/calls.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/runtime/runtime.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/at.h"
|
#include "libc/sysv/consts/at.h"
|
||||||
#include "libc/sysv/consts/map.h"
|
#include "libc/sysv/consts/map.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
#include "libc/sysv/consts/prot.h"
|
#include "libc/sysv/consts/prot.h"
|
||||||
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/semaphore.h"
|
#include "libc/thread/semaphore.h"
|
||||||
#include "libc/thread/semaphore.internal.h"
|
#include "libc/thread/thread.h"
|
||||||
|
#include "libc/thread/tls.h"
|
||||||
|
|
||||||
/**
|
static struct Semaphores {
|
||||||
* Initializes and opens named semaphore.
|
pthread_once_t once;
|
||||||
*
|
pthread_mutex_t lock;
|
||||||
* @param name can be absolute path or should be component w/o slashes
|
struct Semaphore {
|
||||||
* @param oflga can have `O_CREAT` and/or `O_EXCL`
|
struct Semaphore *next;
|
||||||
* @return semaphore object which needs sem_close(), or SEM_FAILED w/ errno
|
sem_t *sem;
|
||||||
* @raise ENOTDIR if a directory component in `name` exists as non-directory
|
char *path;
|
||||||
* @raise ENAMETOOLONG if symlink-resolved `name` length exceeds `PATH_MAX`
|
bool dead;
|
||||||
* @raise ENAMETOOLONG if component in `name` exists longer than `NAME_MAX`
|
int refs;
|
||||||
* @raise ELOOP if `flags` had `O_NOFOLLOW` and `name` is a symbolic link
|
} * list;
|
||||||
* @raise ENOSPC if file system is full when `name` would be `O_CREAT`ed
|
} g_semaphores;
|
||||||
* @raise ELOOP if a loop was detected resolving components of `name`
|
|
||||||
* @raise EEXIST if `O_CREAT|O_EXCL` is used and semaphore exists
|
static void sem_open_lock(void) {
|
||||||
* @raise EACCES if we didn't have permission to create semaphore
|
pthread_mutex_lock(&g_semaphores.lock);
|
||||||
* @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
|
static void sem_open_unlock(void) {
|
||||||
*/
|
pthread_mutex_unlock(&g_semaphores.lock);
|
||||||
sem_t *sem_open(const char *name, int oflag, ...) {
|
}
|
||||||
|
|
||||||
|
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;
|
int fd;
|
||||||
sem_t *sem;
|
sem_t *sem;
|
||||||
va_list va;
|
struct stat st;
|
||||||
unsigned mode;
|
|
||||||
char path[PATH_MAX];
|
|
||||||
|
|
||||||
va_start(va, oflag);
|
|
||||||
mode = va_arg(va, unsigned);
|
|
||||||
va_end(va);
|
|
||||||
|
|
||||||
oflag |= O_RDWR | O_CLOEXEC;
|
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;
|
return SEM_FAILED;
|
||||||
}
|
}
|
||||||
|
_npassert(!fstat(fd, &st));
|
||||||
if (ftruncate(fd, sizeof(sem_t)) == -1) {
|
if (st.st_size < PAGESIZE && ftruncate(fd, PAGESIZE) == -1) {
|
||||||
_npassert(!close(fd));
|
_npassert(!close(fd));
|
||||||
return SEM_FAILED;
|
return SEM_FAILED;
|
||||||
}
|
}
|
||||||
|
sem = mmap(0, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
sem = mmap(0, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
||||||
if (sem != MAP_FAILED) {
|
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;
|
sem->sem_pshared = true;
|
||||||
} else {
|
} else {
|
||||||
sem = SEM_FAILED;
|
sem = SEM_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
_npassert(!close(fd));
|
_npassert(!close(fd));
|
||||||
return sem;
|
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
53
libc/thread/sem_path_np.c
Normal 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;
|
||||||
|
}
|
|
@ -17,9 +17,10 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/semaphore.h"
|
#include "libc/thread/semaphore.h"
|
||||||
#include "third_party/nsync/futex.internal.h"
|
#include "third_party/nsync/futex.internal.h"
|
||||||
|
@ -31,14 +32,17 @@
|
||||||
* @raise EINVAL if `sem` isn't valid
|
* @raise EINVAL if `sem` isn't valid
|
||||||
*/
|
*/
|
||||||
int sem_post(sem_t *sem) {
|
int sem_post(sem_t *sem) {
|
||||||
int rc;
|
int rc, old, wakeups;
|
||||||
int old = atomic_fetch_add_explicit(&sem->sem_value, 1, memory_order_relaxed);
|
_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) {
|
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;
|
rc = 0;
|
||||||
} else {
|
} else {
|
||||||
|
wakeups = 0;
|
||||||
rc = einval();
|
rc = einval();
|
||||||
}
|
}
|
||||||
STRACE("sem_post(%p) → %d% m", sem, rc);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,9 @@
|
||||||
#include "libc/calls/struct/timespec.internal.h"
|
#include "libc/calls/struct/timespec.internal.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/semaphore.h"
|
#include "libc/thread/semaphore.h"
|
||||||
#include "libc/thread/semaphore.internal.h"
|
|
||||||
#include "third_party/nsync/futex.internal.h"
|
#include "third_party/nsync/futex.internal.h"
|
||||||
|
|
||||||
static void sem_delay(int n) {
|
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 {
|
do {
|
||||||
if (!(v = atomic_load_explicit(&sem->sem_value, memory_order_relaxed))) {
|
if (!(v = atomic_load_explicit(&sem->sem_value, memory_order_relaxed))) {
|
||||||
rc = nsync_futex_wait_(&sem->sem_value, v, sem->sem_pshared,
|
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) {
|
} else if (v > 0) {
|
||||||
rc = 0;
|
rc = 0;
|
||||||
} else {
|
} else {
|
||||||
|
_unassert(v > INT_MIN);
|
||||||
rc = einval();
|
rc = einval();
|
||||||
}
|
}
|
||||||
} while (!rc && (!v || !atomic_compare_exchange_weak_explicit(
|
} while (!rc && (!v || !atomic_compare_exchange_weak_explicit(
|
||||||
&sem->sem_value, &v, v - 1, memory_order_acquire,
|
&sem->sem_value, &v, v - 1, memory_order_acquire,
|
||||||
memory_order_relaxed)));
|
memory_order_relaxed)));
|
||||||
|
|
||||||
STRACE("sem_timedwait(%p, %s) → %d% m", sem, DescribeTimespec(0, abstime),
|
_unassert(atomic_fetch_add_explicit(&sem->sem_waiters, -1,
|
||||||
rc);
|
memory_order_release) > 0);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,10 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/semaphore.h"
|
#include "libc/thread/semaphore.h"
|
||||||
|
|
||||||
|
@ -32,8 +34,10 @@
|
||||||
*/
|
*/
|
||||||
int sem_trywait(sem_t *sem) {
|
int sem_trywait(sem_t *sem) {
|
||||||
int v;
|
int v;
|
||||||
|
_unassert(sem->sem_pshared || sem->sem_pid == getpid());
|
||||||
v = atomic_load_explicit(&sem->sem_value, memory_order_relaxed);
|
v = atomic_load_explicit(&sem->sem_value, memory_order_relaxed);
|
||||||
do {
|
do {
|
||||||
|
_unassert(v > INT_MIN);
|
||||||
if (!v) return eagain();
|
if (!v) return eagain();
|
||||||
if (v < 0) return einval();
|
if (v < 0) return einval();
|
||||||
} while (!atomic_compare_exchange_weak_explicit(
|
} while (!atomic_compare_exchange_weak_explicit(
|
||||||
|
|
|
@ -4,12 +4,21 @@
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
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 {
|
typedef struct {
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
_Atomic(int) sem_value;
|
_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;
|
bool sem_pshared;
|
||||||
};
|
};
|
||||||
void *sem_space[32];
|
void *sem_space[32];
|
||||||
|
@ -26,6 +35,7 @@ int sem_getvalue(sem_t *, int *);
|
||||||
sem_t *sem_open(const char *, int, ...);
|
sem_t *sem_open(const char *, int, ...);
|
||||||
int sem_close(sem_t *);
|
int sem_close(sem_t *);
|
||||||
int sem_unlink(const char *);
|
int sem_unlink(const char *);
|
||||||
|
const char *sem_path_np(const char *, char *, size_t);
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
|
@ -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_ */
|
|
|
@ -63,7 +63,7 @@ typedef struct pthread_mutex_s {
|
||||||
unsigned _pshared : 1;
|
unsigned _pshared : 1;
|
||||||
unsigned _depth : 8;
|
unsigned _depth : 8;
|
||||||
unsigned _owner : 21;
|
unsigned _owner : 21;
|
||||||
void *_nsync;
|
long _pid;
|
||||||
} pthread_mutex_t;
|
} pthread_mutex_t;
|
||||||
|
|
||||||
typedef struct pthread_mutexattr_s {
|
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_settype(pthread_mutexattr_t *, int);
|
||||||
int pthread_mutexattr_setpshared(pthread_mutexattr_t *, int);
|
int pthread_mutexattr_setpshared(pthread_mutexattr_t *, int);
|
||||||
int pthread_mutexattr_getpshared(const 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_init(pthread_mutex_t *, const pthread_mutexattr_t *);
|
||||||
int pthread_mutex_lock(pthread_mutex_t *);
|
int pthread_mutex_lock(pthread_mutex_t *);
|
||||||
int pthread_mutex_unlock(pthread_mutex_t *);
|
int pthread_mutex_unlock(pthread_mutex_t *);
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#define TLS_ALIGNMENT 64
|
#define TLS_ALIGNMENT 64
|
||||||
|
|
||||||
#define TIB_FLAG_TIME_CRITICAL 1
|
#define TIB_FLAG_TIME_CRITICAL 1
|
||||||
|
#define TIB_FLAG_VFORKED 2
|
||||||
|
#define TIB_FLAG_WINCRASHING 4
|
||||||
|
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
@ -24,8 +26,8 @@ struct CosmoTib {
|
||||||
struct CosmoTib *tib_self2; /* 0x30 */
|
struct CosmoTib *tib_self2; /* 0x30 */
|
||||||
_Atomic(int32_t) tib_tid; /* 0x38 */
|
_Atomic(int32_t) tib_tid; /* 0x38 */
|
||||||
int32_t tib_errno; /* 0x3c */
|
int32_t tib_errno; /* 0x3c */
|
||||||
|
uint64_t tib_flags; /* 0x40 */
|
||||||
void *tib_nsync;
|
void *tib_nsync;
|
||||||
uint64_t tib_flags;
|
|
||||||
uint64_t tib_sigmask;
|
uint64_t tib_sigmask;
|
||||||
void *tib_reserved3;
|
void *tib_reserved3;
|
||||||
void *tib_reserved4;
|
void *tib_reserved4;
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/intrin/bits.h"
|
#include "libc/intrin/bits.h"
|
||||||
#include "libc/intrin/nopl.internal.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/gc.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/o.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/struct/tm.h"
|
||||||
#include "libc/time/time.h"
|
#include "libc/time/time.h"
|
||||||
#include "libc/time/tz.internal.h"
|
#include "libc/time/tz.internal.h"
|
||||||
|
@ -47,21 +47,28 @@ STATIC_YOINK("usr/share/zoneinfo/UTC");
|
||||||
|
|
||||||
static pthread_mutex_t locallock;
|
static pthread_mutex_t locallock;
|
||||||
|
|
||||||
int localtime_lock(void) {
|
void localtime_lock(void) {
|
||||||
pthread_mutex_lock(&locallock);
|
pthread_mutex_lock(&locallock);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void localtime_unlock(void) {
|
void localtime_unlock(void) {
|
||||||
pthread_mutex_unlock(&locallock);
|
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
|
#ifdef _NOPL0
|
||||||
#define localtime_lock() _NOPL0("__threadcalls", localtime_lock)
|
#define localtime_lock() _NOPL0("__threadcalls", localtime_lock)
|
||||||
#define localtime_unlock() _NOPL0("__threadcalls", localtime_unlock)
|
#define localtime_unlock() _NOPL0("__threadcalls", localtime_unlock)
|
||||||
#else
|
|
||||||
#define localtime_lock() (__threaded ? localtime_lock() : 0)
|
|
||||||
#define localtime_unlock() (__threaded ? localtime_unlock() : 0)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef TZ_ABBR_MAX_LEN
|
#ifndef TZ_ABBR_MAX_LEN
|
||||||
|
|
|
@ -28,3 +28,12 @@ void(__zipos_lock)(void) {
|
||||||
void(__zipos_unlock)(void) {
|
void(__zipos_unlock)(void) {
|
||||||
pthread_mutex_unlock(&__zipos_lock_obj);
|
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
Loading…
Add table
Reference in a new issue