Skip to content

Commit 736fdb7

Browse files
committed
Implement raise() with getcontext() / setcontext()
1 parent dd83db9 commit 736fdb7

File tree

12 files changed

+73
-97
lines changed

12 files changed

+73
-97
lines changed

libc/calls/getcontext.S

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,6 @@
2020

2121
// Gets machine state.
2222
//
23-
// This function goes 14x slower if sigaction() has ever been used to
24-
// install a signal handling function. If you don't care about signal
25-
// safety and just want fast fibers, then you may override the global
26-
// variable `__interruptible` to disable the sigprocmask() calls, for
27-
// pure userspace context switching.
28-
//
2923
// @return 0 on success, or -1 w/ errno
3024
// @see makecontext()
3125
// @see swapcontext()

libc/calls/raise.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "libc/intrin/strace.internal.h"
2424
#include "libc/runtime/syslib.internal.h"
2525
#include "libc/sysv/consts/sicode.h"
26+
#include "libc/sysv/errfuns.h"
2627

2728
/**
2829
* Sends signal to self.
@@ -35,15 +36,20 @@
3536
*
3637
* @param sig can be SIGALRM, SIGINT, SIGTERM, SIGKILL, etc.
3738
* @return 0 on success, or nonzero on failure
39+
* @raise EINVAL if `sig` is invalid
3840
* @asyncsignalsafe
3941
*/
4042
int raise(int sig) {
4143
int rc;
4244
if (IsXnuSilicon()) {
4345
rc = __syslib->__raise(sig);
4446
} else if (IsWindows()) {
45-
__sig_raise(sig, SI_TKILL);
46-
rc = 0;
47+
if (0 <= sig && sig <= 64) {
48+
__sig_raise(sig, SI_TKILL);
49+
rc = 0;
50+
} else {
51+
rc = einval();
52+
}
4753
} else {
4854
rc = sys_tkill(gettid(), sig, 0);
4955
}

libc/calls/sig.c

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "libc/intrin/atomic.h"
3535
#include "libc/intrin/bsf.h"
3636
#include "libc/intrin/describebacktrace.internal.h"
37+
#include "libc/intrin/kprintf.h"
3738
#include "libc/intrin/popcnt.h"
3839
#include "libc/intrin/strace.internal.h"
3940
#include "libc/intrin/weaken.h"
@@ -169,53 +170,59 @@ static textwindows bool __sig_start(struct PosixThread *pt, int sig,
169170
}
170171

171172
static textwindows sigaction_f __sig_handler(unsigned rva) {
173+
atomic_fetch_add_explicit(&__sig.count, 1, memory_order_relaxed);
172174
return (sigaction_f)(__executable_start + rva);
173175
}
174176

175-
textwindows int __sig_raise(int sig, int sic) {
177+
textwindows int __sig_raise(volatile int sig, int sic) {
176178

177-
// create machine context object
178-
struct PosixThread *pt = _pthread_self();
179-
ucontext_t ctx = {.uc_sigmask = pt->tib->tib_sigmask};
180-
struct NtContext nc;
181-
nc.ContextFlags = kNtContextFull;
182-
GetThreadContext(GetCurrentThread(), &nc);
183-
_ntcontext2linux(&ctx, &nc);
179+
// bitset of kinds of handlers called
180+
volatile int handler_was_called = 0;
184181

185-
// process signal(s)
186-
int handler_was_called = 0;
187-
do {
188-
// start the signal
189-
unsigned rva, flags;
190-
if (__sig_start(pt, sig, &rva, &flags)) {
191-
if (flags & SA_RESETHAND) {
192-
STRACE("resetting %G handler", sig);
193-
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
194-
}
182+
// loop over pending signals
183+
ucontext_t ctx;
184+
getcontext(&ctx);
185+
if (!sig) {
186+
if ((sig = __sig_get(ctx.uc_sigmask))) {
187+
sic = SI_KERNEL;
188+
} else {
189+
return handler_was_called;
190+
}
191+
}
195192

196-
// update the signal mask in preparation for signal handller
197-
sigset_t blocksigs = __sighandmask[sig];
198-
if (!(flags & SA_NODEFER)) blocksigs |= 1ull << (sig - 1);
199-
ctx.uc_sigmask = atomic_fetch_or_explicit(
200-
&pt->tib->tib_sigmask, blocksigs, memory_order_acquire);
201-
202-
// call the user's signal handler
203-
char ssbuf[2][128];
204-
siginfo_t si = {.si_signo = sig, .si_code = sic};
205-
STRACE("__sig_raise(%G, %t) mask %s → %s", sig, __sig_handler(rva),
206-
(DescribeSigset)(ssbuf[0], 0, &ctx.uc_sigmask),
207-
(DescribeSigset)(ssbuf[1], 0, (sigset_t *)&pt->tib->tib_sigmask));
208-
__sig_handler(rva)(sig, &si, &ctx);
209-
(void)ssbuf;
210-
211-
// restore the original signal mask
212-
atomic_store_explicit(&pt->tib->tib_sigmask, ctx.uc_sigmask,
213-
memory_order_release);
214-
handler_was_called |= (flags & SA_RESTART) ? 2 : 1;
193+
// process signal(s)
194+
unsigned rva, flags;
195+
struct PosixThread *pt = _pthread_self();
196+
if (__sig_start(pt, sig, &rva, &flags)) {
197+
if (flags & SA_RESETHAND) {
198+
STRACE("resetting %G handler", sig);
199+
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
215200
}
216-
sic = SI_KERNEL;
217-
} while ((sig = __sig_get(ctx.uc_sigmask)));
218-
return handler_was_called;
201+
202+
// update the signal mask in preparation for signal handller
203+
sigset_t blocksigs = __sighandmask[sig];
204+
if (!(flags & SA_NODEFER)) blocksigs |= 1ull << (sig - 1);
205+
ctx.uc_sigmask = atomic_fetch_or_explicit(&pt->tib->tib_sigmask, blocksigs,
206+
memory_order_acquire);
207+
208+
// call the user's signal handler
209+
char ssbuf[2][128];
210+
siginfo_t si = {.si_signo = sig, .si_code = sic};
211+
STRACE("__sig_raise(%G, %t) mask %s → %s", sig, __sig_handler(rva),
212+
(DescribeSigset)(ssbuf[0], 0, &ctx.uc_sigmask),
213+
(DescribeSigset)(ssbuf[1], 0, (sigset_t *)&pt->tib->tib_sigmask));
214+
__sig_handler(rva)(sig, &si, &ctx);
215+
(void)ssbuf;
216+
217+
// record this handler
218+
handler_was_called |= (flags & SA_RESTART) ? 2 : 1;
219+
}
220+
221+
// restore sigmask
222+
// loop back to top
223+
// jump where handler says
224+
sig = 0;
225+
return setcontext(&ctx);
219226
}
220227

221228
textwindows int __sig_relay(int sig, int sic, sigset_t waitmask) {
@@ -258,7 +265,6 @@ static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
258265
struct CosmoTib *tib = __get_tls();
259266
struct PosixThread *pt = (struct PosixThread *)tib->tib_pthread;
260267
for (;;) {
261-
atomic_fetch_add_explicit(&__sig.count, 1, memory_order_relaxed);
262268

263269
// update the signal mask in preparation for signal handller
264270
sigset_t blocksigs = __sighandmask[sig];
@@ -513,9 +519,6 @@ static int __sig_crash_sig(struct NtExceptionPointers *ep, int *code) {
513519
static void __sig_unmaskable(struct NtExceptionPointers *ep, int code, int sig,
514520
struct CosmoTib *tib) {
515521

516-
// increment the signal count for getrusage()
517-
atomic_fetch_add_explicit(&__sig.count, 1, memory_order_relaxed);
518-
519522
// log vital crash information reliably for --strace before doing much
520523
// we don't print this without the flag since raw numbers scare people
521524
// this needs at least one page of stack memory in order to get logged

libc/calls/sigaction.c

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -479,13 +479,6 @@ static int __sigaction(int sig, const struct sigaction *act,
479479
* spawned your process, happened to call `setrlimit()`. Doing this is
480480
* a wonderful idea.
481481
*
482-
* Using signals might make your C runtime slower. Upon successfully
483-
* installing its first signal handling function, sigaction() will set
484-
* the global variable `__interruptible` to true, to let everything else
485-
* know that signals are in play. That way code which would otherwise be
486-
* frequently calling sigprocmask() out of an abundance of caution, will
487-
* no longer need to pay its outrageous cost.
488-
*
489482
* Signal handlers should avoid clobbering global variables like `errno`
490483
* because most signals are asynchronous, i.e. the signal handler might
491484
* 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) {
506499
rc = einval();
507500
} else {
508501
rc = __sigaction(sig, act, oldact);
509-
if (!rc && act && (uintptr_t)act->sa_handler >= kSigactionMinRva) {
510-
static bool once;
511-
if (!once) {
512-
__interruptible = true;
513-
once = true;
514-
}
515-
}
516502
}
517503
STRACE("sigaction(%G, %s, [%s]) → %d% m", sig, DescribeSigaction(0, act),
518504
DescribeSigaction(rc, oldact), rc);

libc/calls/swapcontext.S

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,6 @@
2727
//
2828
// swapcontext(x, y);
2929
//
30-
// This function goes 14x slower if sigaction() has ever been used to
31-
// install a signal handling function. If you don't care about signal
32-
// safety and just want fast fibers, then you may override the global
33-
// variable `__interruptible` to disable the sigprocmask() calls, for
34-
// pure userspace context switching.
35-
//
3630
// @return 0 on success, or -1 w/ errno
3731
// @returnstwice
3832
.ftrace1

libc/calls/ucontext.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,13 @@
2424
int __tailcontext(const ucontext_t *);
2525

2626
static int __contextmask(const sigset_t *opt_set, sigset_t *opt_out_oldset) {
27-
if (!__interruptible) return 0;
28-
// signal handling functions might exist
2927
// now context switching needs to go 14x slower
3028
return sigprocmask(SIG_SETMASK, opt_set, opt_out_oldset);
3129
}
3230

3331
/**
3432
* Sets machine context.
3533
*
36-
* This function goes 14x slower if sigaction() has ever been used to
37-
* install a signal handling function. If you don't care about signal
38-
* safety and just want fast fibers, then you may override the global
39-
* variable `__interruptible` to disable the sigprocmask() calls, for
40-
* pure userspace context switching.
41-
*
4234
* @return -1 on error w/ errno, otherwise won't return unless sent back
4335
* @see swapcontext()
4436
* @see makecontext()

libc/nexgen32e/threaded.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@
2323
*/
2424
int __threaded;
2525

26-
/**
27-
* Set to true if sigaction() has installed signal handlers.
28-
*/
29-
bool __interruptible;
30-
3126
#ifdef __x86_64__
3227
bool __tls_enabled;
3328
#endif

libc/runtime/runtime.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ extern int __argc;
7272
extern char **__argv;
7373
extern char **__envp;
7474
extern unsigned long *__auxv;
75-
extern bool __interruptible;
7675
extern intptr_t __oldstack;
7776
extern uint64_t __nosync;
7877
extern int __strace;

test/libc/calls/getcontext_test.c

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ TEST(getcontext, test) {
5454
TEST(getcontext, canReadAndWriteSignalMask) {
5555
sigset_t ss, old;
5656
volatile int n = 0;
57-
__interruptible = true;
5857
sigemptyset(&ss);
5958
sigaddset(&ss, SIGUSR1);
6059
sigprocmask(SIG_SETMASK, &ss, &old);
@@ -72,8 +71,7 @@ TEST(getcontext, canReadAndWriteSignalMask) {
7271
}
7372

7473
void SetGetContext(void) {
75-
static int a;
76-
a = 0;
74+
int a = 0;
7775
getcontext(&context);
7876
if (!a) {
7977
a = 1;
@@ -82,9 +80,6 @@ void SetGetContext(void) {
8280
}
8381

8482
BENCH(getcontext, bench) {
85-
__interruptible = false;
86-
EZBENCH2("getsetcontext nosig", donothing, SetGetContext());
87-
__interruptible = true;
8883
EZBENCH2("getsetcontext", donothing, SetGetContext());
8984
}
9085

@@ -99,10 +94,6 @@ BENCH(swapcontext, bench) {
9994
}
10095
} else {
10196
ready = true;
102-
__interruptible = false;
103-
EZBENCH2("swapcontextx2 nosig", donothing, swapcontext(&loop, &main));
104-
// kprintf("dollar\n");
105-
__interruptible = true;
10697
EZBENCH2("swapcontextx2", donothing, swapcontext(&loop, &main));
10798
// kprintf("dollar\n");
10899
}

test/libc/calls/raise_test.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
#include "libc/calls/struct/sigaction.h"
2121
#include "libc/calls/struct/siginfo.h"
2222
#include "libc/dce.h"
23+
#include "libc/intrin/kprintf.h"
2324
#include "libc/runtime/runtime.h"
2425
#include "libc/sysv/consts/sa.h"
2526
#include "libc/sysv/consts/sicode.h"
2627
#include "libc/sysv/consts/sig.h"
28+
#include "libc/testlib/ezbench.h"
2729
#include "libc/testlib/subprocess.h"
2830
#include "libc/testlib/testlib.h"
2931
#include "libc/thread/thread.h"
@@ -67,9 +69,19 @@ void *Worker(void *arg) {
6769
}
6870

6971
TEST(raise, threaded) {
72+
SPAWN(fork);
7073
signal(SIGILL, SIG_DFL);
7174
pthread_t worker;
7275
ASSERT_EQ(0, pthread_create(&worker, 0, Worker, 0));
7376
ASSERT_EQ(0, pthread_join(worker, 0));
7477
pthread_exit(0);
78+
EXITS(0);
79+
}
80+
81+
void OnRaise(int sig) {
82+
}
83+
84+
BENCH(raise, bench) {
85+
signal(SIGUSR1, OnRaise);
86+
EZBENCH2("raise", donothing, raise(SIGUSR1));
7587
}

0 commit comments

Comments
 (0)