mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-14 06:59:10 +00:00
Improve locks and signals
- Introduce fast spinlock API - Double rand64() perf w/ spinlock - Improve raise() on New Technology - Support gettid() across platforms - Implement SA_NODEFER on New Technology - Move the lock intrinsics into LIBC_INTRIN - Make SIGTRAP recoverable on New Technology - Block SIGCHLD in wait4() on New Technology - Add threading prototypes for XNU and FreeBSD - Rewrite abort() fixing its minor bugs on XNU/NT - Shave down a lot of the content in libc/bits/bits.h - Let signal handlers modify CPU registers on New Technology
This commit is contained in:
parent
f68f1789bd
commit
046c7ebd4a
110 changed files with 1514 additions and 876 deletions
|
@ -16,7 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
@ -41,7 +41,7 @@ int atfork(void *fn, void *arg) {
|
|||
for (;;) {
|
||||
i = g_atfork.i;
|
||||
if (i == ARRAYLEN(g_atfork.p)) return enomem();
|
||||
if (cmpxchg(&g_atfork.i, i, i + 1)) {
|
||||
if (_cmpxchg(&g_atfork.i, i, i + 1)) {
|
||||
g_atfork.p[i] = (struct AtForkCallback){.fn = fn, .arg = arg};
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -123,6 +123,7 @@ int getdomainname(char *, size_t);
|
|||
int gethostname(char *, size_t);
|
||||
int getpgid(int);
|
||||
int getpid(void);
|
||||
int gettid(void);
|
||||
int getppid(void);
|
||||
int getpriority(int, unsigned);
|
||||
int getrlimit(int, struct rlimit *);
|
||||
|
@ -227,7 +228,6 @@ uint32_t geteuid(void) nosideeffect;
|
|||
uint32_t getgid(void) nosideeffect;
|
||||
uint32_t getpgrp(void) nosideeffect;
|
||||
uint32_t getsid(int) nosideeffect;
|
||||
uint32_t gettid(void) nosideeffect;
|
||||
uint32_t getuid(void) nosideeffect;
|
||||
uint32_t umask(int32_t);
|
||||
void rewinddir(DIR *);
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
@ -40,7 +40,7 @@ int __ensurefds(int fd) {
|
|||
if ((p2 = weaken(malloc)(n2 * sizeof(*p1)))) {
|
||||
memcpy(p2, p1, n1 * sizeof(*p1));
|
||||
bzero(p2 + n1, (n2 - n1) * sizeof(*p1));
|
||||
if (cmpxchg(&g_fds.p, p1, p2)) {
|
||||
if (_cmpxchg(&g_fds.p, p1, p2)) {
|
||||
g_fds.n = n2;
|
||||
if (weaken(free)) {
|
||||
if (p1 == g_fds.__init_p) {
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/flock.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
|
@ -45,8 +45,8 @@ static textwindows int sys_fcntl_nt_reservefd(int start) {
|
|||
if (fd >= g_fds.n) {
|
||||
if (__ensurefds(fd) == -1) return -1;
|
||||
}
|
||||
cmpxchg(&g_fds.f, fd, fd + 1);
|
||||
if (cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) {
|
||||
_cmpxchg(&g_fds.f, fd, fd + 1);
|
||||
if (_cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) {
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,5 +18,6 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
|
||||
cthread_spinlock_t __sig_lock;
|
||||
unsigned __sighandrvas[NSIG];
|
||||
unsigned __sighandflags[NSIG];
|
||||
|
|
|
@ -1,37 +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 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
|
||||
extern int __pid;
|
||||
|
||||
/**
|
||||
* Returns process id.
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int getpid(void) {
|
||||
int rc;
|
||||
if (!__vforked) {
|
||||
rc = __pid;
|
||||
} else {
|
||||
rc = sys_getpid().ax;
|
||||
}
|
||||
return rc;
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
#include "libc/calls/struct/winsize.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/struct/context.h"
|
||||
|
@ -72,6 +73,7 @@ extern const struct Fd kEmptyFd;
|
|||
|
||||
hidden extern int __vforked;
|
||||
hidden extern bool __time_critical;
|
||||
hidden extern cthread_spinlock_t __sig_lock;
|
||||
hidden extern unsigned __sighandrvas[NSIG];
|
||||
hidden extern unsigned __sighandflags[NSIG];
|
||||
hidden extern struct Fds g_fds;
|
||||
|
@ -176,6 +178,7 @@ i32 sys_renameat(i32, const char *, i32, const char *) hidden;
|
|||
i32 sys_sched_setaffinity(i32, u64, const void *) hidden;
|
||||
i32 sys_sched_yield(void) hidden;
|
||||
i32 sys_setitimer(i32, const struct itimerval *, struct itimerval *) hidden;
|
||||
i32 sys_setpgid(i32, i32) hidden;
|
||||
i32 sys_setpriority(i32, u32, i32) hidden;
|
||||
i32 sys_setresgid(uint32_t, uint32_t, uint32_t) hidden;
|
||||
i32 sys_setresuid(uint32_t, uint32_t, uint32_t) hidden;
|
||||
|
@ -330,7 +333,8 @@ struct NtOverlapped *_offset2overlap(int64_t, struct NtOverlapped *) hidden;
|
|||
unsigned __wincrash_nt(struct NtExceptionPointers *);
|
||||
void *GetProcAddressModule(const char *, const char *) hidden;
|
||||
void WinMainForked(void) hidden;
|
||||
void ntcontext2linux(struct ucontext *, const struct NtContext *) hidden;
|
||||
void _ntcontext2linux(struct ucontext *, const struct NtContext *) hidden;
|
||||
void _ntlinux2context(struct NtContext *, const ucontext_t *) hidden;
|
||||
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
│ cosmopolitan § syscalls » metal ─╬─│┼
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/iphlpapi.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
|
@ -275,7 +275,7 @@ static int createHostInfo(struct NtIpAdapterAddresses *firstAdapter) {
|
|||
if (!node) goto err;
|
||||
if (!__hostInfo) {
|
||||
__hostInfo = node;
|
||||
if (cmpxchg(&once, false, true)) {
|
||||
if (_cmpxchg(&once, false, true)) {
|
||||
atexit(freeHostInfo);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/getconsolectrlevent.h"
|
||||
#include "libc/calls/getconsolectrlevent.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
@ -30,34 +30,60 @@
|
|||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_kill_nt(int pid, int sig) {
|
||||
bool ok;
|
||||
bool32 ok;
|
||||
int64_t handle;
|
||||
int event, ntpid;
|
||||
if (pid) {
|
||||
pid = ABS(pid);
|
||||
if ((event = GetConsoleCtrlEvent(sig)) != -1) {
|
||||
/* kill(pid, SIGINT|SIGQUIT) */
|
||||
if (__isfdkind(pid, kFdProcess)) {
|
||||
ntpid = GetProcessId(g_fds.p[pid].handle);
|
||||
} else if (!__isfdopen(pid)) {
|
||||
/* XXX: this is sloppy (see fork-nt.c) */
|
||||
ntpid = pid;
|
||||
} else {
|
||||
return esrch();
|
||||
}
|
||||
ok = !!GenerateConsoleCtrlEvent(event, ntpid);
|
||||
} else if (__isfdkind(pid, kFdProcess)) {
|
||||
ok = !!TerminateProcess(g_fds.p[pid].handle, 128 + sig);
|
||||
if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true;
|
||||
} else if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) {
|
||||
ok = !!TerminateProcess(handle, 128 + sig);
|
||||
if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true;
|
||||
CloseHandle(handle);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
return ok ? 0 : __winerr();
|
||||
} else {
|
||||
|
||||
// is killing everything except init really worth supporting?
|
||||
if (pid == -1) return einval();
|
||||
|
||||
// XXX: NT doesn't really have process groups. For instance the
|
||||
// CreateProcess() flag for starting a process group actually
|
||||
// just does an "ignore ctrl-c" internally.
|
||||
pid = ABS(pid);
|
||||
|
||||
// If we're targeting current process group then just call raise().
|
||||
if (!pid || pid == getpid()) {
|
||||
return raise(sig);
|
||||
}
|
||||
|
||||
// GenerateConsoleCtrlEvent() will always signal groups and there's
|
||||
// nothing we can do about it, unless we have a GUI GetMessage loop
|
||||
// and alternatively create a centralized signal daemon like cygwin
|
||||
if ((event = GetConsoleCtrlEvent(sig)) != -1) {
|
||||
// we're killing with SIGINT or SIGQUIT which are the only two
|
||||
// signals we can really use, since TerminateProcess() makes
|
||||
// everything else effectively a SIGKILL ;_;
|
||||
if (__isfdkind(pid, kFdProcess)) {
|
||||
ntpid = GetProcessId(g_fds.p[pid].handle);
|
||||
} else if (!__isfdopen(pid)) {
|
||||
ntpid = pid; // XXX: suboptimal
|
||||
} else {
|
||||
return esrch();
|
||||
}
|
||||
if (GenerateConsoleCtrlEvent(event, ntpid)) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Is this a cosmo pid that was returned by fork_nt?
|
||||
if (__isfdkind(pid, kFdProcess)) {
|
||||
ok = TerminateProcess(g_fds.p[pid].handle, 128 + sig);
|
||||
if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// XXX: Is this a raw new technology pid? Because that's messy.
|
||||
if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) {
|
||||
ok = TerminateProcess(handle, 128 + sig);
|
||||
if (!ok && GetLastError() == kNtErrorAccessDenied) {
|
||||
ok = true; // cargo culting other codebases here
|
||||
}
|
||||
CloseHandle(handle);
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Sends signal to process.
|
||||
|
@ -38,9 +40,12 @@
|
|||
* @asyncsignalsafe
|
||||
*/
|
||||
int kill(int pid, int sig) {
|
||||
int rc;
|
||||
if (!IsWindows()) {
|
||||
return sys_kill(pid, sig, 1);
|
||||
rc = sys_kill(pid, sig, 1);
|
||||
} else {
|
||||
return sys_kill_nt(pid, sig);
|
||||
rc = sys_kill_nt(pid, sig);
|
||||
}
|
||||
STRACE("kill(%d, %s) → %d% m", pid, strsignal(sig), rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/bits/asmflag.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/dce.h"
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/bits/asmflag.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "libc/nt/struct/context.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
textwindows void ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) {
|
||||
textwindows void _ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) {
|
||||
if (!cr) return;
|
||||
ctx->uc_flags = cr->EFlags;
|
||||
ctx->uc_mcontext.gregs[REG_EFL] = cr->EFlags;
|
||||
|
@ -48,3 +48,30 @@ textwindows void ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) {
|
|||
ctx->uc_mcontext.fpregs = &ctx->__fpustate;
|
||||
memcpy(&ctx->__fpustate, &cr->FltSave, sizeof(ctx->__fpustate));
|
||||
}
|
||||
|
||||
textwindows void _ntlinux2context(struct NtContext *cr, const ucontext_t *ctx) {
|
||||
if (!cr) return;
|
||||
cr->EFlags = ctx->uc_flags;
|
||||
cr->EFlags = ctx->uc_mcontext.gregs[REG_EFL];
|
||||
cr->Rax = ctx->uc_mcontext.rax;
|
||||
cr->Rbx = ctx->uc_mcontext.rbx;
|
||||
cr->Rcx = ctx->uc_mcontext.rcx;
|
||||
cr->Rdx = ctx->uc_mcontext.rdx;
|
||||
cr->Rdi = ctx->uc_mcontext.rdi;
|
||||
cr->Rsi = ctx->uc_mcontext.rsi;
|
||||
cr->Rbp = ctx->uc_mcontext.rbp;
|
||||
cr->Rsp = ctx->uc_mcontext.rsp;
|
||||
cr->Rip = ctx->uc_mcontext.rip;
|
||||
cr->R8 = ctx->uc_mcontext.r8;
|
||||
cr->R9 = ctx->uc_mcontext.r9;
|
||||
cr->R10 = ctx->uc_mcontext.r10;
|
||||
cr->R11 = ctx->uc_mcontext.r11;
|
||||
cr->R12 = ctx->uc_mcontext.r12;
|
||||
cr->R13 = ctx->uc_mcontext.r13;
|
||||
cr->R14 = ctx->uc_mcontext.r14;
|
||||
cr->R15 = ctx->uc_mcontext.r15;
|
||||
cr->SegCs = ctx->uc_mcontext.cs;
|
||||
cr->SegGs = ctx->uc_mcontext.gs;
|
||||
cr->SegFs = ctx->uc_mcontext.fs;
|
||||
memcpy(&cr->FltSave, &ctx->__fpustate, sizeof(ctx->__fpustate));
|
||||
}
|
||||
|
|
|
@ -17,11 +17,14 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/nexgen32e/nt2sysv.h"
|
||||
#include "libc/nt/enum/ctrlevent.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) {
|
||||
STRACE("__onntconsoleevent(%u)", dwCtrlType);
|
||||
switch (dwCtrlType) {
|
||||
case kNtCtrlCEvent:
|
||||
__sig_add(SIGINT, SI_KERNEL);
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Waits for any signal.
|
||||
* Waits for signal.
|
||||
*
|
||||
* This suspends execution until an unmasked signal is delivered and its
|
||||
* callback function has been called. The current signal mask is used.
|
||||
|
|
|
@ -17,16 +17,25 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/getconsolectrlevent.h"
|
||||
#include "libc/calls/getconsolectrlevent.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
static textwindows inline bool HasWorkingConsole(void) {
|
||||
return !!(__ntconsolemode[0] | __ntconsolemode[1] | __ntconsolemode[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends signal to this process.
|
||||
*
|
||||
|
@ -36,7 +45,7 @@
|
|||
*/
|
||||
int raise(int sig) {
|
||||
int rc, event;
|
||||
STRACE("raise(%d) → [...]", sig);
|
||||
STRACE("raise(%s) → [...]", strsignal(sig));
|
||||
if (sig == SIGTRAP) {
|
||||
DebugBreak();
|
||||
rc = 0;
|
||||
|
@ -45,18 +54,27 @@ int raise(int sig) {
|
|||
x = 1 / x;
|
||||
rc = 0;
|
||||
} else if (!IsWindows()) {
|
||||
// XXX: should be tkill() or tgkill() on linux
|
||||
rc = sys_kill(getpid(), sig, 1);
|
||||
} else {
|
||||
if ((event = GetConsoleCtrlEvent(sig)) != -1) {
|
||||
if (HasWorkingConsole() && (event = GetConsoleCtrlEvent(sig)) != -1) {
|
||||
// XXX: MSDN says "If this parameter is zero, the signal is
|
||||
// generated in all processes that share the console of the
|
||||
// calling process." which seems to imply multiple process
|
||||
// groups potentially. We just shouldn't use this because it
|
||||
// doesn't make any sense and it's so evil.
|
||||
if (GenerateConsoleCtrlEvent(event, 0)) {
|
||||
// XXX: we shouldn't need to sleep here ctrl-c is evil on nt
|
||||
SleepEx(100, false);
|
||||
__sig_check(false);
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
} else {
|
||||
rc = __sig_add(sig, SI_USER);
|
||||
rc = __sig_raise(sig, SI_USER);
|
||||
}
|
||||
}
|
||||
STRACE("[...] raise → %d% m", rc);
|
||||
STRACE("[...] raise(%s) → %d% m", strsignal(sig), rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/intrin/lockcmpxchg.h"
|
||||
|
||||
void __releasefd(int fd) {
|
||||
int x;
|
||||
|
@ -26,6 +27,6 @@ void __releasefd(int fd) {
|
|||
do {
|
||||
x = g_fds.f;
|
||||
if (fd >= x) break;
|
||||
} while (!cmpxchg(&g_fds.f, x, fd));
|
||||
} while (!_lockcmpxchg(&g_fds.f, x, fd));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
|
@ -31,8 +31,8 @@ int __reservefd(void) {
|
|||
if (fd >= g_fds.n) {
|
||||
if (__ensurefds(fd) == -1) return -1;
|
||||
}
|
||||
cmpxchg(&g_fds.f, fd, fd + 1);
|
||||
if (cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) {
|
||||
_cmpxchg(&g_fds.f, fd, fd + 1);
|
||||
if (_cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) {
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ textwindows int __sample_pids(int pids[hasatleast 64],
|
|||
bool exploratory) {
|
||||
static uint64_t rando = 1;
|
||||
uint32_t i, j, base, count;
|
||||
base = KnuthLinearCongruentialGenerator(&rando);
|
||||
base = KnuthLinearCongruentialGenerator(&rando) >> 32;
|
||||
for (count = i = 0; i < g_fds.n; ++i) {
|
||||
j = (base + i) % g_fds.n;
|
||||
if (g_fds.p[j].kind == kFdProcess && (!exploratory || !g_fds.p[j].zombie)) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
|
@ -18,22 +18,41 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Returns current thread id.
|
||||
* @asyncsignalsafe
|
||||
* Changes process group for process.
|
||||
*/
|
||||
uint32_t gettid(void) {
|
||||
uint32_t res;
|
||||
int setpgid(int pid, int pgid) {
|
||||
int rc, me;
|
||||
if (!IsWindows()) {
|
||||
res = sys_gettid();
|
||||
if (res <= 0) {
|
||||
res = getpid();
|
||||
}
|
||||
return res;
|
||||
rc = sys_setpgid(pid, pgid);
|
||||
} else {
|
||||
return GetCurrentThreadId();
|
||||
me = getpid();
|
||||
if (pid == me && pgid == me) {
|
||||
/*
|
||||
* "When a process is created with CREATE_NEW_PROCESS_GROUP
|
||||
* specified, an implicit call to SetConsoleCtrlHandler(NULL,TRUE)
|
||||
* is made on behalf of the new process; this means that the new
|
||||
* process has CTRL+C disabled. This lets shells handle CTRL+C
|
||||
* themselves, and selectively pass that signal on to
|
||||
* sub-processes. CTRL+BREAK is not disabled, and may be used to
|
||||
* interrupt the process/process group."
|
||||
* ──Quoth MSDN § CreateProcessW()
|
||||
*/
|
||||
if (SetConsoleCtrlHandler(0, 1)) {
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
} else {
|
||||
// irregular use cases not supported on windows
|
||||
rc = einval();
|
||||
}
|
||||
}
|
||||
STRACE("setpgid(%d, %d) → %d% m", pid, pgid, rc);
|
||||
return rc;
|
||||
}
|
212
libc/calls/sig.c
212
libc/calls/sig.c
|
@ -16,7 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
|
@ -24,6 +23,9 @@
|
|||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/typedef/sigaction_f.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/intrin/lockcmpxchg.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
@ -37,18 +39,6 @@
|
|||
* @threadsafe
|
||||
*/
|
||||
|
||||
#define LOCK \
|
||||
for (;;) \
|
||||
if (lockcmpxchg(&__sig.lock, false, true)) { \
|
||||
asm volatile("" ::: "memory")
|
||||
|
||||
#define UNLOCK \
|
||||
asm volatile("" ::: "memory"); \
|
||||
__sig.lock = false; \
|
||||
break; \
|
||||
} \
|
||||
else sched_yield()
|
||||
|
||||
struct Signal {
|
||||
struct Signal *next;
|
||||
bool used;
|
||||
|
@ -57,7 +47,6 @@ struct Signal {
|
|||
};
|
||||
|
||||
struct Signals {
|
||||
bool lock;
|
||||
sigset_t mask;
|
||||
struct Signal *queue;
|
||||
struct Signal mem[__SIG_QUEUE_LENGTH];
|
||||
|
@ -67,15 +56,19 @@ struct Signals __sig; // TODO(jart): Need TLS
|
|||
|
||||
/**
|
||||
* Allocates piece of memory for storing pending signal.
|
||||
* @assume lock is held
|
||||
*/
|
||||
static textwindows struct Signal *__sig_alloc(void) {
|
||||
int i;
|
||||
struct Signal *res = 0;
|
||||
for (i = 0; i < ARRAYLEN(__sig.mem); ++i) {
|
||||
if (!__sig.mem[i].used && lockcmpxchg(&__sig.mem[i].used, false, true)) {
|
||||
return __sig.mem + i;
|
||||
if (!__sig.mem[i].used) {
|
||||
__sig.mem[i].used = true;
|
||||
res = __sig.mem + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,7 +85,7 @@ static textwindows void __sig_free(struct Signal *mem) {
|
|||
static textwindows struct Signal *__sig_remove(void) {
|
||||
struct Signal *prev, *res;
|
||||
if (__sig.queue) {
|
||||
LOCK;
|
||||
cthread_spinlock(&__sig_lock);
|
||||
for (prev = 0, res = __sig.queue; res; prev = res, res = res->next) {
|
||||
if (!sigismember(&__sig.mask, res->sig)) {
|
||||
if (res == __sig.queue) {
|
||||
|
@ -106,7 +99,7 @@ static textwindows struct Signal *__sig_remove(void) {
|
|||
STRACE("%s is masked", strsignal(res->sig));
|
||||
}
|
||||
}
|
||||
UNLOCK;
|
||||
cthread_spunlock(&__sig_lock);
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
|
@ -114,28 +107,60 @@ static textwindows struct Signal *__sig_remove(void) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Delivers signal.
|
||||
* Delivers signal to callback.
|
||||
* @note called from main thread
|
||||
* @return true if EINTR should be returned by caller
|
||||
*/
|
||||
static textwindows bool __sig_deliver(bool restartable, struct Signal *sig,
|
||||
unsigned rva) {
|
||||
unsigned flags;
|
||||
siginfo_t info;
|
||||
flags = __sighandflags[sig->sig];
|
||||
// TODO(jart): polyfill prevention of re-entry
|
||||
if (flags & SA_RESETHAND) {
|
||||
__sighandrvas[sig->sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||
static textwindows bool __sig_deliver(bool restartable, int sig, int si_code,
|
||||
ucontext_t *ctx) {
|
||||
unsigned rva, flags;
|
||||
siginfo_t info, *infop;
|
||||
STRACE("delivering %s", strsignal(sig));
|
||||
|
||||
// enter the signal
|
||||
cthread_spinlock(&__sig_lock);
|
||||
rva = __sighandrvas[sig];
|
||||
flags = __sighandflags[sig];
|
||||
if (~flags & SA_NODEFER) {
|
||||
// by default we try to avoid reentering a signal handler. for
|
||||
// example, if a sigsegv handler segfaults, then we'd want the
|
||||
// second signal to just kill the process. doing this means we
|
||||
// track state. that's bad if you want to longjmp() out of the
|
||||
// signal handler. in that case you must use SA_NODEFER.
|
||||
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||
}
|
||||
STRACE("delivering %s", strsignal(sig->sig));
|
||||
bzero(&info, sizeof(info));
|
||||
info.si_signo = sig->sig;
|
||||
info.si_code = sig->si_code;
|
||||
((sigaction_f)(_base + rva))(sig->sig, &info, 0);
|
||||
cthread_spunlock(&__sig_lock);
|
||||
|
||||
// setup the somewhat expensive information args
|
||||
// only if they're requested by the user in sigaction()
|
||||
if (flags & SA_SIGINFO) {
|
||||
bzero(&info, sizeof(info));
|
||||
info.si_signo = sig;
|
||||
info.si_code = si_code;
|
||||
infop = &info;
|
||||
} else {
|
||||
infop = 0;
|
||||
ctx = 0;
|
||||
}
|
||||
|
||||
// handover control to user
|
||||
((sigaction_f)(_base + rva))(sig, infop, ctx);
|
||||
|
||||
// leave the signal
|
||||
cthread_spinlock(&__sig_lock);
|
||||
if (~flags & SA_NODEFER) {
|
||||
_cmpxchg(__sighandrvas + sig, (int32_t)(intptr_t)SIG_DFL, rva);
|
||||
}
|
||||
if (flags & SA_RESETHAND) {
|
||||
STRACE("resetting oneshot signal handler");
|
||||
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||
}
|
||||
cthread_spunlock(&__sig_lock);
|
||||
|
||||
if (!restartable) {
|
||||
return true; // always send EINTR for wait4(), poll(), etc.
|
||||
} else if (flags & SA_RESTART) {
|
||||
STRACE("restarting syscall on %s", strsignal(sig->sig));
|
||||
STRACE("restarting syscall on %s", strsignal(sig));
|
||||
return false; // resume syscall for read(), write(), etc.
|
||||
} else {
|
||||
return true; // default course is to raise EINTR
|
||||
|
@ -149,27 +174,86 @@ static textwindows bool __sig_isfatal(int sig) {
|
|||
return sig != SIGCHLD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles signal.
|
||||
*
|
||||
* @param restartable can be used to suppress true return if SA_RESTART
|
||||
* @return true if signal was delivered
|
||||
*/
|
||||
textwindows bool __sig_handle(bool restartable, int sig, int si_code,
|
||||
ucontext_t *ctx) {
|
||||
bool delivered;
|
||||
switch (__sighandrvas[sig]) {
|
||||
case (intptr_t)SIG_DFL:
|
||||
if (__sig_isfatal(sig)) {
|
||||
STRACE("terminating on %s", strsignal(sig));
|
||||
__restorewintty();
|
||||
_Exit(128 + sig);
|
||||
}
|
||||
// fallthrough
|
||||
case (intptr_t)SIG_IGN:
|
||||
STRACE("ignoring %s", strsignal(sig));
|
||||
delivered = false;
|
||||
break;
|
||||
default:
|
||||
delivered = __sig_deliver(restartable, sig, si_code, ctx);
|
||||
break;
|
||||
}
|
||||
return delivered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles signal immediately if not blocked.
|
||||
*
|
||||
* @param restartable is for functions like read() but not poll()
|
||||
* @return true if EINTR should be returned by caller
|
||||
* @return 1 if delivered, 0 if enqueued, otherwise -1 w/ errno
|
||||
* @note called from main thread
|
||||
* @threadsafe
|
||||
*/
|
||||
textwindows int __sig_raise(int sig, int si_code) {
|
||||
int rc;
|
||||
int candeliver;
|
||||
cthread_spinlock(&__sig_lock);
|
||||
candeliver = !sigismember(&__sig.mask, sig);
|
||||
cthread_spunlock(&__sig_lock);
|
||||
switch (candeliver) {
|
||||
case 1:
|
||||
__sig_handle(false, sig, si_code, 0);
|
||||
return 0;
|
||||
case 0:
|
||||
STRACE("%s is masked", strsignal(sig));
|
||||
return __sig_add(sig, si_code);
|
||||
default:
|
||||
return -1; // sigismember() validates `sig`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues generic signal for delivery on New Technology.
|
||||
* @return 0 if enqueued, otherwise -1 w/ errno
|
||||
* @threadsafe
|
||||
*/
|
||||
textwindows int __sig_add(int sig, int si_code) {
|
||||
int rc;
|
||||
struct Signal *mem;
|
||||
if (1 <= sig && sig <= NSIG) {
|
||||
STRACE("enqueuing %s", strsignal(sig));
|
||||
cthread_spinlock(&__sig_lock);
|
||||
if ((mem = __sig_alloc())) {
|
||||
mem->sig = sig;
|
||||
mem->si_code = si_code;
|
||||
LOCK;
|
||||
mem->next = __sig.queue;
|
||||
__sig.queue = mem;
|
||||
UNLOCK;
|
||||
return 0;
|
||||
rc = 0;
|
||||
} else {
|
||||
return enomem();
|
||||
rc = enomem();
|
||||
}
|
||||
cthread_spunlock(&__sig_lock);
|
||||
} else {
|
||||
return einval();
|
||||
rc = einval();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -186,21 +270,7 @@ textwindows bool __sig_check(bool restartable) {
|
|||
struct Signal *sig;
|
||||
delivered = false;
|
||||
while ((sig = __sig_remove())) {
|
||||
switch ((rva = __sighandrvas[sig->sig])) {
|
||||
case (intptr_t)SIG_DFL:
|
||||
if (__sig_isfatal(sig->sig)) {
|
||||
STRACE("terminating on %s", strsignal(sig->sig));
|
||||
__restorewintty();
|
||||
_Exit(128 + sig->sig);
|
||||
}
|
||||
// fallthrough
|
||||
case (intptr_t)SIG_IGN:
|
||||
STRACE("ignoring %s", strsignal(sig->sig));
|
||||
break;
|
||||
default:
|
||||
delivered = __sig_deliver(restartable, sig, rva);
|
||||
break;
|
||||
}
|
||||
delivered |= __sig_handle(restartable, sig->sig, sig->si_code, 0);
|
||||
__sig_free(sig);
|
||||
}
|
||||
return delivered;
|
||||
|
@ -208,13 +278,31 @@ textwindows bool __sig_check(bool restartable) {
|
|||
|
||||
/**
|
||||
* Changes signal mask for main thread.
|
||||
* @return old mask
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
*/
|
||||
textwindows sigset_t __sig_mask(const sigset_t *neu) {
|
||||
sigset_t old;
|
||||
LOCK;
|
||||
old = __sig.mask;
|
||||
if (neu) __sig.mask = *neu;
|
||||
UNLOCK;
|
||||
return old;
|
||||
textwindows int __sig_mask(int how, const sigset_t *neu, sigset_t *old) {
|
||||
int i;
|
||||
uint64_t a, b;
|
||||
if (how == SIG_BLOCK || how == SIG_UNBLOCK || how == SIG_SETMASK) {
|
||||
cthread_spinlock(&__sig_lock);
|
||||
if (old) {
|
||||
*old = __sig.mask;
|
||||
}
|
||||
if (neu) {
|
||||
for (i = 0; i < ARRAYLEN(__sig.mask.__bits); ++i) {
|
||||
if (how == SIG_BLOCK) {
|
||||
__sig.mask.__bits[i] |= neu->__bits[i];
|
||||
} else if (how == SIG_UNBLOCK) {
|
||||
__sig.mask.__bits[i] &= ~neu->__bits[i];
|
||||
} else {
|
||||
__sig.mask.__bits[i] = neu->__bits[i];
|
||||
}
|
||||
}
|
||||
__sig.mask.__bits[0] &= ~(SIGKILL | SIGSTOP);
|
||||
}
|
||||
cthread_spunlock(&__sig_lock);
|
||||
return 0;
|
||||
} else {
|
||||
return einval();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
|
||||
#define __SIG_QUEUE_LENGTH 8
|
||||
#define __SIG_POLLING_INTERVAL_MS 50
|
||||
#define __SIG_LOGGING_INTERVAL_MS 1700
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
bool __sig_check(bool) hidden;
|
||||
bool __sig_handle(bool, int, int, ucontext_t *) hidden;
|
||||
int __sig_add(int, int) hidden;
|
||||
sigset_t __sig_mask(const sigset_t *) hidden;
|
||||
int __sig_mask(int, const sigset_t *, sigset_t *) hidden;
|
||||
int __sig_raise(int, int) hidden;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
@ -212,6 +213,7 @@ static int __sigaction(int sig, const struct sigaction *act,
|
|||
rc = 0;
|
||||
}
|
||||
if (rc != -1 && !__vforked) {
|
||||
cthread_spinlock(&__sig_lock);
|
||||
if (oldact) {
|
||||
oldrva = __sighandrvas[sig];
|
||||
oldact->sa_sigaction = (sigaction_f)(
|
||||
|
@ -221,6 +223,7 @@ static int __sigaction(int sig, const struct sigaction *act,
|
|||
__sighandrvas[sig] = rva;
|
||||
__sighandflags[sig] = act->sa_flags;
|
||||
}
|
||||
cthread_spunlock(&__sig_lock);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
@ -233,6 +236,58 @@ static int __sigaction(int sig, const struct sigaction *act,
|
|||
* .sa_flags = SA_RESETHAND|SA_RESTART|SA_SIGINFO};
|
||||
* CHECK_NE(-1, sigaction(SIGINT, &sa, NULL));
|
||||
*
|
||||
* Here's an example of the most professional way to handle signals.
|
||||
* It's generally a best practice to have signal handlers do the fewest
|
||||
* number of things possible. The trick is to have your signals work
|
||||
* hand-in-glove with the EINTR errno returned by i/o.
|
||||
*
|
||||
* static volatile bool gotctrlc;
|
||||
* void OnCtrlC(int sig) {
|
||||
* gotctrlc = true;
|
||||
* }
|
||||
* int main() {
|
||||
* size_t got;
|
||||
* ssize_t rc;
|
||||
* char buf[1];
|
||||
* struct sigaction oldint;
|
||||
* struct sigaction saint = {.sa_handler = GotCtrlC};
|
||||
* if (sigaction(SIGINT, &saint, &oldint) == -1) {
|
||||
* perror("sigaction");
|
||||
* exit(1);
|
||||
* }
|
||||
* for (;;) {
|
||||
* rc = read(0, buf, sizeof(buf));
|
||||
* if (rc == -1) {
|
||||
* if (errno == EINTR) {
|
||||
* if (gotctrlc) {
|
||||
* break;
|
||||
* }
|
||||
* } else {
|
||||
* perror("read");
|
||||
* exit(2);
|
||||
* }
|
||||
* }
|
||||
* if (!(got = rc)) {
|
||||
* break;
|
||||
* }
|
||||
* for (;;) {
|
||||
* rc = write(1, buf, got);
|
||||
* if (rc != -1) {
|
||||
* assert(rc == 1);
|
||||
* break;
|
||||
* } else if (errno != EINTR) {
|
||||
* perror("write");
|
||||
* exit(3);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* sigaction(SIGINT, &oldint, 0);
|
||||
* }
|
||||
*
|
||||
* Please note that you can't do the above if you use SA_RESTART. Since
|
||||
* the purpose of SA_RESTART is to restart i/o operations whose docs say
|
||||
* that they're @restartable and read() is one such function.
|
||||
*
|
||||
* @return 0 on success or -1 w/ errno
|
||||
* @see xsigaction() for a much better api
|
||||
* @asyncsignalsafe
|
||||
|
@ -241,7 +296,11 @@ static int __sigaction(int sig, const struct sigaction *act,
|
|||
int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) {
|
||||
int rc;
|
||||
char buf[2][128];
|
||||
rc = __sigaction(sig, act, oldact);
|
||||
if (sig == SIGKILL || sig == SIGSTOP) {
|
||||
rc = einval();
|
||||
} else {
|
||||
rc = __sigaction(sig, act, oldact);
|
||||
}
|
||||
STRACE("sigaction(%s, %s, [%s]) → %d% m", strsignal(sig),
|
||||
__strace_sigaction(buf[0], sizeof(buf[0]), 0, act),
|
||||
__strace_sigaction(buf[1], sizeof(buf[1]), rc, oldact), rc);
|
||||
|
|
|
@ -20,104 +20,12 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/sigaction-freebsd.internal.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/struct/ucontext-freebsd.internal.h"
|
||||
#include "libc/calls/typedef/sigaction_f.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
union sigval_freebsd {
|
||||
int32_t sival_int;
|
||||
void *sival_ptr;
|
||||
int32_t sigval_int;
|
||||
void *sigval_ptr;
|
||||
};
|
||||
|
||||
struct siginfo_freebsd {
|
||||
int32_t si_signo;
|
||||
int32_t si_errno;
|
||||
int32_t si_code;
|
||||
int32_t si_pid;
|
||||
uint32_t si_uid;
|
||||
int32_t si_status;
|
||||
void *si_addr;
|
||||
union sigval_freebsd si_value;
|
||||
union {
|
||||
struct {
|
||||
int32_t _trapno;
|
||||
} _fault;
|
||||
struct {
|
||||
int32_t _timerid;
|
||||
int32_t _overrun;
|
||||
} _timer;
|
||||
struct {
|
||||
int32_t _mqd;
|
||||
} _mesgq;
|
||||
struct {
|
||||
int64_t _band;
|
||||
} _poll;
|
||||
struct {
|
||||
int64_t __spare1__;
|
||||
int32_t __spare2__[7];
|
||||
} __spare__;
|
||||
} _reason;
|
||||
};
|
||||
|
||||
struct stack_freebsd {
|
||||
void *ss_sp;
|
||||
uint64_t ss_size;
|
||||
int32_t ss_flags;
|
||||
};
|
||||
|
||||
struct mcontext_freebsd {
|
||||
int64_t mc_onstack;
|
||||
int64_t mc_rdi;
|
||||
int64_t mc_rsi;
|
||||
int64_t mc_rdx;
|
||||
int64_t mc_rcx;
|
||||
int64_t mc_r8;
|
||||
int64_t mc_r9;
|
||||
int64_t mc_rax;
|
||||
int64_t mc_rbx;
|
||||
int64_t mc_rbp;
|
||||
int64_t mc_r10;
|
||||
int64_t mc_r11;
|
||||
int64_t mc_r12;
|
||||
int64_t mc_r13;
|
||||
int64_t mc_r14;
|
||||
int64_t mc_r15;
|
||||
uint32_t mc_trapno;
|
||||
uint16_t mc_fs;
|
||||
uint16_t mc_gs;
|
||||
int64_t mc_addr;
|
||||
uint32_t mc_flags;
|
||||
uint16_t mc_es;
|
||||
uint16_t mc_ds;
|
||||
int64_t mc_err;
|
||||
int64_t mc_rip;
|
||||
int64_t mc_cs;
|
||||
int64_t mc_rflags;
|
||||
int64_t mc_rsp;
|
||||
int64_t mc_ss;
|
||||
int64_t mc_len;
|
||||
int64_t mc_fpformat;
|
||||
int64_t mc_ownedfp;
|
||||
int64_t mc_fpstate[64];
|
||||
int64_t mc_fsbase;
|
||||
int64_t mc_gsbase;
|
||||
int64_t mc_xfpustate;
|
||||
int64_t mc_xfpustate_len;
|
||||
int64_t mc_spare[4];
|
||||
};
|
||||
|
||||
struct ucontext_freebsd {
|
||||
struct sigset_freebsd uc_sigmask;
|
||||
struct mcontext_freebsd uc_mcontext;
|
||||
struct ucontext_freebsd *uc_link;
|
||||
struct stack_freebsd uc_stack;
|
||||
int32_t uc_flags;
|
||||
int32_t __spare__[4];
|
||||
};
|
||||
|
||||
void __sigenter_freebsd(int sig, struct siginfo_freebsd *si,
|
||||
struct ucontext_freebsd *ctx) {
|
||||
int rva;
|
||||
|
|
|
@ -21,12 +21,20 @@
|
|||
#include "libc/sysv/consts/sa.h"
|
||||
|
||||
/**
|
||||
* Installs kernel interrupt handler.
|
||||
* Installs kernel interrupt handler, e.g.
|
||||
*
|
||||
* void GotCtrlC(int sig) { ... }
|
||||
* CHECK_NE(SIG_ERR, signal(SIGINT, GotCtrlC));
|
||||
*
|
||||
* @return old signal handler on success or SIG_ERR w/ errno
|
||||
* @note this function has BSD semantics, i.e. SA_RESTART
|
||||
* @see sigaction() which has more features
|
||||
*/
|
||||
sighandler_t(signal)(int sig, sighandler_t func) {
|
||||
struct sigaction sa_old, sa = {.sa_handler = func, .sa_flags = SA_RESTART};
|
||||
if ((sigaction)(sig, &sa, &sa_old) == -1) return SIG_ERR;
|
||||
return sa_old.sa_handler;
|
||||
struct sigaction old, sa = {.sa_handler = func, .sa_flags = SA_RESTART};
|
||||
if ((sigaction)(sig, &sa, &old) != -1) {
|
||||
return old.sa_handler;
|
||||
} else {
|
||||
return SIG_ERR;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,8 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
|
|||
char buf[2][41];
|
||||
int res, rc, arg1;
|
||||
const sigset_t *arg2;
|
||||
STRACE("sigprocmask(%s, %s, [...]", DescribeHow(howbuf, how),
|
||||
__strace_sigset(buf[0], sizeof(buf[0]), 0, opt_set));
|
||||
sigemptyset(&old);
|
||||
if (IsAsan() &&
|
||||
((opt_set && !__asan_is_valid(opt_set, sizeof(*opt_set))) ||
|
||||
|
@ -86,14 +88,13 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
|
|||
rc = -1;
|
||||
}
|
||||
} else { // windows or metal
|
||||
old = __sig_mask(opt_set);
|
||||
rc = __sig_mask(how, opt_set, &old);
|
||||
_check_interrupts(false, 0);
|
||||
rc = 0;
|
||||
}
|
||||
if (rc != -1 && opt_out_oldset) {
|
||||
*opt_out_oldset = old;
|
||||
}
|
||||
STRACE("sigprocmask(%s, %s, [%s]) → %d% m", DescribeHow(howbuf, how),
|
||||
STRACE("[...] sigprocmask(%s, %s, [%s]) → %d% m", DescribeHow(howbuf, how),
|
||||
__strace_sigset(buf[0], sizeof(buf[0]), 0, opt_set),
|
||||
__strace_sigset(buf[1], sizeof(buf[1]), rc, opt_out_oldset), rc);
|
||||
return rc;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
|
@ -24,20 +25,23 @@
|
|||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Blocks until SIG ∉ MASK is delivered to process.
|
||||
*
|
||||
* @param ignore is a bitset of signals to block temporarily
|
||||
* @return -1 w/ EINTR or possibly EFAULT
|
||||
* @param ignore is a bitset of signals to block temporarily, which if
|
||||
* NULL is equivalent to passing an empty signal set
|
||||
* @return -1 w/ EINTR (or possibly EFAULT)
|
||||
* @asyncsignalsafe
|
||||
* @norestart
|
||||
*/
|
||||
int sigsuspend(const sigset_t *ignore) {
|
||||
int rc;
|
||||
char buf[41];
|
||||
long ms, totoms;
|
||||
sigset_t save, mask, *arg;
|
||||
STRACE("sigsuspend(%s) → [...]",
|
||||
__strace_sigset(buf, sizeof(buf), 0, ignore));
|
||||
|
@ -62,15 +66,24 @@ int sigsuspend(const sigset_t *ignore) {
|
|||
if (!IsWindows()) {
|
||||
rc = sys_sigsuspend(arg, 8);
|
||||
} else {
|
||||
save = __sig_mask(arg);
|
||||
__sig_mask(SIG_SETMASK, arg, &save);
|
||||
ms = 0;
|
||||
totoms = 0;
|
||||
do {
|
||||
if (_check_interrupts(false, g_fds.p)) {
|
||||
rc = eintr();
|
||||
break;
|
||||
}
|
||||
SleepEx(__SIG_POLLING_INTERVAL_MS, true);
|
||||
#ifdef SYSDEBUG
|
||||
ms += __SIG_POLLING_INTERVAL_MS;
|
||||
if (ms >= __SIG_LOGGING_INTERVAL_MS) {
|
||||
totoms += ms, ms = 0;
|
||||
STRACE("[...] sigsuspending for %'lums...", totoms);
|
||||
}
|
||||
#endif
|
||||
} while (1);
|
||||
__sig_mask(&save);
|
||||
__sig_mask(SIG_SETMASK, &save, 0);
|
||||
}
|
||||
} else {
|
||||
// TODO(jart): sigsuspend metal support
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/typedef/sigaction_f.h"
|
||||
#include "libc/calls/typedef/sighandler_t.h"
|
||||
#include "libc/dce.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
|
||||
struct sigaction { /* cosmo abi */
|
||||
|
|
102
libc/calls/struct/ucontext-freebsd.internal.h
Normal file
102
libc/calls/struct/ucontext-freebsd.internal.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_FREEBSD_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_FREEBSD_INTERNAL_H_
|
||||
#include "libc/calls/struct/sigaction-freebsd.internal.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
union sigval_freebsd {
|
||||
int32_t sival_int;
|
||||
void *sival_ptr;
|
||||
int32_t sigval_int;
|
||||
void *sigval_ptr;
|
||||
};
|
||||
|
||||
struct siginfo_freebsd {
|
||||
int32_t si_signo;
|
||||
int32_t si_errno;
|
||||
int32_t si_code;
|
||||
int32_t si_pid;
|
||||
uint32_t si_uid;
|
||||
int32_t si_status;
|
||||
void *si_addr;
|
||||
union sigval_freebsd si_value;
|
||||
union {
|
||||
struct {
|
||||
int32_t _trapno;
|
||||
} _fault;
|
||||
struct {
|
||||
int32_t _timerid;
|
||||
int32_t _overrun;
|
||||
} _timer;
|
||||
struct {
|
||||
int32_t _mqd;
|
||||
} _mesgq;
|
||||
struct {
|
||||
int64_t _band;
|
||||
} _poll;
|
||||
struct {
|
||||
int64_t __spare1__;
|
||||
int32_t __spare2__[7];
|
||||
} __spare__;
|
||||
} _reason;
|
||||
};
|
||||
|
||||
struct stack_freebsd {
|
||||
void *ss_sp;
|
||||
uint64_t ss_size;
|
||||
int32_t ss_flags;
|
||||
};
|
||||
|
||||
struct mcontext_freebsd {
|
||||
int64_t mc_onstack;
|
||||
int64_t mc_rdi;
|
||||
int64_t mc_rsi;
|
||||
int64_t mc_rdx;
|
||||
int64_t mc_rcx;
|
||||
int64_t mc_r8;
|
||||
int64_t mc_r9;
|
||||
int64_t mc_rax;
|
||||
int64_t mc_rbx;
|
||||
int64_t mc_rbp;
|
||||
int64_t mc_r10;
|
||||
int64_t mc_r11;
|
||||
int64_t mc_r12;
|
||||
int64_t mc_r13;
|
||||
int64_t mc_r14;
|
||||
int64_t mc_r15;
|
||||
uint32_t mc_trapno;
|
||||
uint16_t mc_fs;
|
||||
uint16_t mc_gs;
|
||||
int64_t mc_addr;
|
||||
uint32_t mc_flags;
|
||||
uint16_t mc_es;
|
||||
uint16_t mc_ds;
|
||||
int64_t mc_err;
|
||||
int64_t mc_rip;
|
||||
int64_t mc_cs;
|
||||
int64_t mc_rflags;
|
||||
int64_t mc_rsp;
|
||||
int64_t mc_ss;
|
||||
int64_t mc_len;
|
||||
int64_t mc_fpformat;
|
||||
int64_t mc_ownedfp;
|
||||
int64_t mc_fpstate[64];
|
||||
int64_t mc_fsbase;
|
||||
int64_t mc_gsbase;
|
||||
int64_t mc_xfpustate;
|
||||
int64_t mc_xfpustate_len;
|
||||
int64_t mc_spare[4];
|
||||
};
|
||||
|
||||
struct ucontext_freebsd {
|
||||
struct sigset_freebsd uc_sigmask;
|
||||
struct mcontext_freebsd uc_mcontext;
|
||||
struct ucontext_freebsd *uc_link;
|
||||
struct stack_freebsd uc_stack;
|
||||
int32_t uc_flags;
|
||||
int32_t __spare__[4];
|
||||
};
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_FREEBSD_INTERNAL_H_ */
|
|
@ -16,8 +16,8 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/lockxchg.h"
|
||||
|
||||
/**
|
||||
* Deletes file.
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
|
@ -40,11 +41,13 @@
|
|||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/w.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
|
||||
struct rusage *opt_out_rusage) {
|
||||
static textwindows int sys_wait4_nt_impl(int pid, int *opt_out_wstatus,
|
||||
int options,
|
||||
struct rusage *opt_out_rusage) {
|
||||
int pids[64];
|
||||
int64_t handle;
|
||||
int64_t handles[64];
|
||||
|
@ -129,3 +132,15 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
|
|||
return pids[i];
|
||||
}
|
||||
}
|
||||
|
||||
textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
|
||||
struct rusage *opt_out_rusage) {
|
||||
int rc;
|
||||
sigset_t mask, oldmask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGCHLD);
|
||||
__sig_mask(SIG_BLOCK, &mask, &oldmask);
|
||||
rc = sys_wait4_nt_impl(pid, opt_out_wstatus, options, opt_out_rusage);
|
||||
__sig_mask(SIG_SETMASK, &oldmask, 0);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/typedef/sigaction_f.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
|
@ -24,14 +25,14 @@
|
|||
#include "libc/nt/enum/signal.h"
|
||||
#include "libc/nt/struct/ntexceptionpointers.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
textwindows unsigned __wincrash(struct NtExceptionPointers *ep) {
|
||||
int sig, rva, code;
|
||||
struct Goodies {
|
||||
ucontext_t ctx;
|
||||
struct siginfo si;
|
||||
} g;
|
||||
int64_t rip;
|
||||
int sig, code;
|
||||
ucontext_t ctx;
|
||||
STRACE("__wincrash");
|
||||
switch (ep->ExceptionRecord->ExceptionCode) {
|
||||
case kNtSignalBreakpoint:
|
||||
|
@ -96,12 +97,22 @@ textwindows unsigned __wincrash(struct NtExceptionPointers *ep) {
|
|||
default:
|
||||
return kNtExceptionContinueSearch;
|
||||
}
|
||||
bzero(&g, sizeof(g));
|
||||
g.si.si_code = code;
|
||||
rva = __sighandrvas[sig];
|
||||
if (rva >= kSigactionMinRva) {
|
||||
ntcontext2linux(&g.ctx, ep->ContextRecord);
|
||||
((sigaction_f)(_base + rva))(sig, &g.si, &g.ctx);
|
||||
rip = ep->ContextRecord->Rip;
|
||||
|
||||
if (__sighandflags[sig] & SA_SIGINFO) {
|
||||
_ntcontext2linux(&ctx, ep->ContextRecord);
|
||||
__sig_handle(false, sig, code, &ctx);
|
||||
_ntlinux2context(ep->ContextRecord, &ctx);
|
||||
} else {
|
||||
__sig_handle(false, sig, code, 0);
|
||||
}
|
||||
|
||||
// Windows seems to be the only operating system that traps INT3 at
|
||||
// addressof(INT3) rather than addressof(INT3)+1. So we must adjust
|
||||
// RIP to prevent the same INT3 from being trapped forevermore.
|
||||
if (sig == SIGTRAP && rip == ep->ContextRecord->Rip) {
|
||||
ep->ContextRecord->Rip++;
|
||||
}
|
||||
|
||||
return kNtExceptionContinueExecution;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
|
@ -29,22 +30,10 @@
|
|||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static textwindows ssize_t sys_write_nt_epipe(int fd) {
|
||||
siginfo_t info;
|
||||
STRACE("WriteFile(%d:%p) → %m", fd, g_fds.p[fd].handle);
|
||||
if (!__sighandrvas[SIGPIPE]) {
|
||||
__restorewintty();
|
||||
_Exit(128 + SIGPIPE);
|
||||
} else if (__sighandrvas[SIGPIPE] >= kSigactionMinRva) {
|
||||
bzero(&info, sizeof(info));
|
||||
((sigaction_f)(_base + __sighandrvas[SIGPIPE]))(SIGPIPE, &info, 0);
|
||||
}
|
||||
return epipe();
|
||||
}
|
||||
|
||||
static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
|
||||
ssize_t offset) {
|
||||
uint32_t sent;
|
||||
|
@ -53,7 +42,8 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
|
|||
_offset2overlap(offset, &overlap))) {
|
||||
return sent;
|
||||
} else if (GetLastError() == kNtErrorBrokenPipe) {
|
||||
return sys_write_nt_epipe(fd);
|
||||
__sig_raise(SIGPIPE, SI_KERNEL);
|
||||
return epipe();
|
||||
} else {
|
||||
return __winerr();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue