mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
Implement raise() with getcontext() / setcontext()
This commit is contained in:
parent
dd83db9567
commit
736fdb757a
12 changed files with 73 additions and 97 deletions
|
@ -20,12 +20,6 @@
|
|||
|
||||
// Gets machine state.
|
||||
//
|
||||
// This function goes 14x slower if sigaction() has ever been used to
|
||||
// install a signal handling function. If you don't care about signal
|
||||
// safety and just want fast fibers, then you may override the global
|
||||
// variable `__interruptible` to disable the sigprocmask() calls, for
|
||||
// pure userspace context switching.
|
||||
//
|
||||
// @return 0 on success, or -1 w/ errno
|
||||
// @see makecontext()
|
||||
// @see swapcontext()
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/runtime/syslib.internal.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Sends signal to self.
|
||||
|
@ -35,6 +36,7 @@
|
|||
*
|
||||
* @param sig can be SIGALRM, SIGINT, SIGTERM, SIGKILL, etc.
|
||||
* @return 0 on success, or nonzero on failure
|
||||
* @raise EINVAL if `sig` is invalid
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int raise(int sig) {
|
||||
|
@ -42,8 +44,12 @@ int raise(int sig) {
|
|||
if (IsXnuSilicon()) {
|
||||
rc = __syslib->__raise(sig);
|
||||
} else if (IsWindows()) {
|
||||
__sig_raise(sig, SI_TKILL);
|
||||
rc = 0;
|
||||
if (0 <= sig && sig <= 64) {
|
||||
__sig_raise(sig, SI_TKILL);
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = einval();
|
||||
}
|
||||
} else {
|
||||
rc = sys_tkill(gettid(), sig, 0);
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/bsf.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/popcnt.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
|
@ -169,53 +170,59 @@ static textwindows bool __sig_start(struct PosixThread *pt, int sig,
|
|||
}
|
||||
|
||||
static textwindows sigaction_f __sig_handler(unsigned rva) {
|
||||
atomic_fetch_add_explicit(&__sig.count, 1, memory_order_relaxed);
|
||||
return (sigaction_f)(__executable_start + rva);
|
||||
}
|
||||
|
||||
textwindows int __sig_raise(int sig, int sic) {
|
||||
textwindows int __sig_raise(volatile int sig, int sic) {
|
||||
|
||||
// create machine context object
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
ucontext_t ctx = {.uc_sigmask = pt->tib->tib_sigmask};
|
||||
struct NtContext nc;
|
||||
nc.ContextFlags = kNtContextFull;
|
||||
GetThreadContext(GetCurrentThread(), &nc);
|
||||
_ntcontext2linux(&ctx, &nc);
|
||||
// bitset of kinds of handlers called
|
||||
volatile int handler_was_called = 0;
|
||||
|
||||
// loop over pending signals
|
||||
ucontext_t ctx;
|
||||
getcontext(&ctx);
|
||||
if (!sig) {
|
||||
if ((sig = __sig_get(ctx.uc_sigmask))) {
|
||||
sic = SI_KERNEL;
|
||||
} else {
|
||||
return handler_was_called;
|
||||
}
|
||||
}
|
||||
|
||||
// process signal(s)
|
||||
int handler_was_called = 0;
|
||||
do {
|
||||
// start the signal
|
||||
unsigned rva, flags;
|
||||
if (__sig_start(pt, sig, &rva, &flags)) {
|
||||
if (flags & SA_RESETHAND) {
|
||||
STRACE("resetting %G handler", sig);
|
||||
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||
}
|
||||
|
||||
// update the signal mask in preparation for signal handller
|
||||
sigset_t blocksigs = __sighandmask[sig];
|
||||
if (!(flags & SA_NODEFER)) blocksigs |= 1ull << (sig - 1);
|
||||
ctx.uc_sigmask = atomic_fetch_or_explicit(
|
||||
&pt->tib->tib_sigmask, blocksigs, memory_order_acquire);
|
||||
|
||||
// call the user's signal handler
|
||||
char ssbuf[2][128];
|
||||
siginfo_t si = {.si_signo = sig, .si_code = sic};
|
||||
STRACE("__sig_raise(%G, %t) mask %s → %s", sig, __sig_handler(rva),
|
||||
(DescribeSigset)(ssbuf[0], 0, &ctx.uc_sigmask),
|
||||
(DescribeSigset)(ssbuf[1], 0, (sigset_t *)&pt->tib->tib_sigmask));
|
||||
__sig_handler(rva)(sig, &si, &ctx);
|
||||
(void)ssbuf;
|
||||
|
||||
// restore the original signal mask
|
||||
atomic_store_explicit(&pt->tib->tib_sigmask, ctx.uc_sigmask,
|
||||
memory_order_release);
|
||||
handler_was_called |= (flags & SA_RESTART) ? 2 : 1;
|
||||
unsigned rva, flags;
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
if (__sig_start(pt, sig, &rva, &flags)) {
|
||||
if (flags & SA_RESETHAND) {
|
||||
STRACE("resetting %G handler", sig);
|
||||
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||
}
|
||||
sic = SI_KERNEL;
|
||||
} while ((sig = __sig_get(ctx.uc_sigmask)));
|
||||
return handler_was_called;
|
||||
|
||||
// update the signal mask in preparation for signal handller
|
||||
sigset_t blocksigs = __sighandmask[sig];
|
||||
if (!(flags & SA_NODEFER)) blocksigs |= 1ull << (sig - 1);
|
||||
ctx.uc_sigmask = atomic_fetch_or_explicit(&pt->tib->tib_sigmask, blocksigs,
|
||||
memory_order_acquire);
|
||||
|
||||
// call the user's signal handler
|
||||
char ssbuf[2][128];
|
||||
siginfo_t si = {.si_signo = sig, .si_code = sic};
|
||||
STRACE("__sig_raise(%G, %t) mask %s → %s", sig, __sig_handler(rva),
|
||||
(DescribeSigset)(ssbuf[0], 0, &ctx.uc_sigmask),
|
||||
(DescribeSigset)(ssbuf[1], 0, (sigset_t *)&pt->tib->tib_sigmask));
|
||||
__sig_handler(rva)(sig, &si, &ctx);
|
||||
(void)ssbuf;
|
||||
|
||||
// record this handler
|
||||
handler_was_called |= (flags & SA_RESTART) ? 2 : 1;
|
||||
}
|
||||
|
||||
// restore sigmask
|
||||
// loop back to top
|
||||
// jump where handler says
|
||||
sig = 0;
|
||||
return setcontext(&ctx);
|
||||
}
|
||||
|
||||
textwindows int __sig_relay(int sig, int sic, sigset_t waitmask) {
|
||||
|
@ -258,7 +265,6 @@ static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
|
|||
struct CosmoTib *tib = __get_tls();
|
||||
struct PosixThread *pt = (struct PosixThread *)tib->tib_pthread;
|
||||
for (;;) {
|
||||
atomic_fetch_add_explicit(&__sig.count, 1, memory_order_relaxed);
|
||||
|
||||
// update the signal mask in preparation for signal handller
|
||||
sigset_t blocksigs = __sighandmask[sig];
|
||||
|
@ -513,9 +519,6 @@ static int __sig_crash_sig(struct NtExceptionPointers *ep, int *code) {
|
|||
static void __sig_unmaskable(struct NtExceptionPointers *ep, int code, int sig,
|
||||
struct CosmoTib *tib) {
|
||||
|
||||
// increment the signal count for getrusage()
|
||||
atomic_fetch_add_explicit(&__sig.count, 1, memory_order_relaxed);
|
||||
|
||||
// log vital crash information reliably for --strace before doing much
|
||||
// we don't print this without the flag since raw numbers scare people
|
||||
// this needs at least one page of stack memory in order to get logged
|
||||
|
|
|
@ -479,13 +479,6 @@ static int __sigaction(int sig, const struct sigaction *act,
|
|||
* spawned your process, happened to call `setrlimit()`. Doing this is
|
||||
* a wonderful idea.
|
||||
*
|
||||
* Using signals might make your C runtime slower. Upon successfully
|
||||
* installing its first signal handling function, sigaction() will set
|
||||
* the global variable `__interruptible` to true, to let everything else
|
||||
* know that signals are in play. That way code which would otherwise be
|
||||
* frequently calling sigprocmask() out of an abundance of caution, will
|
||||
* no longer need to pay its outrageous cost.
|
||||
*
|
||||
* Signal handlers should avoid clobbering global variables like `errno`
|
||||
* because most signals are asynchronous, i.e. the signal handler might
|
||||
* be called at any assembly instruction. If something like a `SIGCHLD`
|
||||
|
@ -506,13 +499,6 @@ int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact) {
|
|||
rc = einval();
|
||||
} else {
|
||||
rc = __sigaction(sig, act, oldact);
|
||||
if (!rc && act && (uintptr_t)act->sa_handler >= kSigactionMinRva) {
|
||||
static bool once;
|
||||
if (!once) {
|
||||
__interruptible = true;
|
||||
once = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
STRACE("sigaction(%G, %s, [%s]) → %d% m", sig, DescribeSigaction(0, act),
|
||||
DescribeSigaction(rc, oldact), rc);
|
||||
|
|
|
@ -27,12 +27,6 @@
|
|||
//
|
||||
// swapcontext(x, y);
|
||||
//
|
||||
// This function goes 14x slower if sigaction() has ever been used to
|
||||
// install a signal handling function. If you don't care about signal
|
||||
// safety and just want fast fibers, then you may override the global
|
||||
// variable `__interruptible` to disable the sigprocmask() calls, for
|
||||
// pure userspace context switching.
|
||||
//
|
||||
// @return 0 on success, or -1 w/ errno
|
||||
// @returnstwice
|
||||
.ftrace1
|
||||
|
|
|
@ -24,8 +24,6 @@
|
|||
int __tailcontext(const ucontext_t *);
|
||||
|
||||
static int __contextmask(const sigset_t *opt_set, sigset_t *opt_out_oldset) {
|
||||
if (!__interruptible) return 0;
|
||||
// signal handling functions might exist
|
||||
// now context switching needs to go 14x slower
|
||||
return sigprocmask(SIG_SETMASK, opt_set, opt_out_oldset);
|
||||
}
|
||||
|
@ -33,12 +31,6 @@ static int __contextmask(const sigset_t *opt_set, sigset_t *opt_out_oldset) {
|
|||
/**
|
||||
* Sets machine context.
|
||||
*
|
||||
* This function goes 14x slower if sigaction() has ever been used to
|
||||
* install a signal handling function. If you don't care about signal
|
||||
* safety and just want fast fibers, then you may override the global
|
||||
* variable `__interruptible` to disable the sigprocmask() calls, for
|
||||
* pure userspace context switching.
|
||||
*
|
||||
* @return -1 on error w/ errno, otherwise won't return unless sent back
|
||||
* @see swapcontext()
|
||||
* @see makecontext()
|
||||
|
|
|
@ -23,11 +23,6 @@
|
|||
*/
|
||||
int __threaded;
|
||||
|
||||
/**
|
||||
* Set to true if sigaction() has installed signal handlers.
|
||||
*/
|
||||
bool __interruptible;
|
||||
|
||||
#ifdef __x86_64__
|
||||
bool __tls_enabled;
|
||||
#endif
|
||||
|
|
|
@ -72,7 +72,6 @@ extern int __argc;
|
|||
extern char **__argv;
|
||||
extern char **__envp;
|
||||
extern unsigned long *__auxv;
|
||||
extern bool __interruptible;
|
||||
extern intptr_t __oldstack;
|
||||
extern uint64_t __nosync;
|
||||
extern int __strace;
|
||||
|
|
|
@ -54,7 +54,6 @@ TEST(getcontext, test) {
|
|||
TEST(getcontext, canReadAndWriteSignalMask) {
|
||||
sigset_t ss, old;
|
||||
volatile int n = 0;
|
||||
__interruptible = true;
|
||||
sigemptyset(&ss);
|
||||
sigaddset(&ss, SIGUSR1);
|
||||
sigprocmask(SIG_SETMASK, &ss, &old);
|
||||
|
@ -72,8 +71,7 @@ TEST(getcontext, canReadAndWriteSignalMask) {
|
|||
}
|
||||
|
||||
void SetGetContext(void) {
|
||||
static int a;
|
||||
a = 0;
|
||||
int a = 0;
|
||||
getcontext(&context);
|
||||
if (!a) {
|
||||
a = 1;
|
||||
|
@ -82,9 +80,6 @@ void SetGetContext(void) {
|
|||
}
|
||||
|
||||
BENCH(getcontext, bench) {
|
||||
__interruptible = false;
|
||||
EZBENCH2("getsetcontext nosig", donothing, SetGetContext());
|
||||
__interruptible = true;
|
||||
EZBENCH2("getsetcontext", donothing, SetGetContext());
|
||||
}
|
||||
|
||||
|
@ -99,10 +94,6 @@ BENCH(swapcontext, bench) {
|
|||
}
|
||||
} else {
|
||||
ready = true;
|
||||
__interruptible = false;
|
||||
EZBENCH2("swapcontextx2 nosig", donothing, swapcontext(&loop, &main));
|
||||
// kprintf("dollar\n");
|
||||
__interruptible = true;
|
||||
EZBENCH2("swapcontextx2", donothing, swapcontext(&loop, &main));
|
||||
// kprintf("dollar\n");
|
||||
}
|
||||
|
|
|
@ -20,10 +20,12 @@
|
|||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/subprocess.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
@ -67,9 +69,19 @@ void *Worker(void *arg) {
|
|||
}
|
||||
|
||||
TEST(raise, threaded) {
|
||||
SPAWN(fork);
|
||||
signal(SIGILL, SIG_DFL);
|
||||
pthread_t worker;
|
||||
ASSERT_EQ(0, pthread_create(&worker, 0, Worker, 0));
|
||||
ASSERT_EQ(0, pthread_join(worker, 0));
|
||||
pthread_exit(0);
|
||||
EXITS(0);
|
||||
}
|
||||
|
||||
void OnRaise(int sig) {
|
||||
}
|
||||
|
||||
BENCH(raise, bench) {
|
||||
signal(SIGUSR1, OnRaise);
|
||||
EZBENCH2("raise", donothing, raise(SIGUSR1));
|
||||
}
|
||||
|
|
|
@ -116,3 +116,8 @@ TEST(poll, interrupt) {
|
|||
ASSERT_TRUE(gotsig);
|
||||
ASSERT_TRUE(didit);
|
||||
}
|
||||
|
||||
TEST(raise, zero) {
|
||||
ASSERT_SYS(0, 0, raise(0));
|
||||
ASSERT_SYS(EINVAL, -1, raise(-1));
|
||||
}
|
||||
|
|
|
@ -64,7 +64,6 @@ void check_args(long x0, long x1, long x2, long x3, long x4, long x5, double f0,
|
|||
|
||||
TEST(makecontext, args) {
|
||||
char stack[1024];
|
||||
__interruptible = false;
|
||||
getcontext(&uc);
|
||||
uc.uc_link = &goback;
|
||||
uc.uc_stack.ss_sp = stack;
|
||||
|
|
Loading…
Add table
Reference in a new issue