Skip to content

Commit

Permalink
signals: improve waitForEvents
Browse files Browse the repository at this point in the history
Signed-off-by: leongross <[email protected]>
  • Loading branch information
leongross committed Oct 8, 2024
1 parent 4946cc5 commit 98cb692
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 8 deletions.
54 changes: 52 additions & 2 deletions src/runtime/runtime_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ func usleep(usec uint) int
//export pause
func pause() int32

// int sigsuspend(const sigset_t *mask);
//
//export sigsuspend
func sigsuspend(sigset_t unsafe.Pointer) int32

// void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
// Note: off_t is defined as int64 because:
// - musl (used on Linux) always defines it as int64
Expand Down Expand Up @@ -341,6 +346,12 @@ var hasSignals uint32
// signals into this value.
var receivedSignals uint32

// Bitmap keeping track of enabled signals.
var activeSignals uint32

// Bitmap keeping track of masked/disabled signals.
var maskedSignals uint32

//go:linkname signal_enable os/signal.signal_enable
func signal_enable(s uint32) {
if s >= 32 {
Expand All @@ -349,6 +360,10 @@ func signal_enable(s uint32) {
runtimePanicAt(returnAddress(0), "unsupported signal number")
}
atomic.StoreUint32(&hasSignals, 1)

// update the enabled signals bitmap
val := atomic.LoadUint32(&activeSignals)
atomic.CompareAndSwapUint32(&receivedSignals, val, val|(1<<s))
// It's easier to implement this function in C.
tinygo_signal_enable(s)
}
Expand Down Expand Up @@ -396,6 +411,15 @@ func tinygo_signal_ignore(s uint32)
//export tinygo_signal_disable
func tinygo_signal_disable(s uint32)

//export tinygo_mask_signals
func tinygo_mask_signals(mask uint32) unsafe.Pointer

//export tinygo_unmask_signals
func tinygo_unmask_signals(mask uint32)

//export tinygo_sigsuspend
func tinygo_sigsuspend(mask uint32)

// void tinygo_signal_handler(int sig);
//
//export tinygo_signal_handler
Expand Down Expand Up @@ -424,7 +448,7 @@ func signal_recv() uint32 {
return val
}

// Atomically find a signal that previously occured and send it into the
// Atomically find a signal that previously occurred and send it into the
// signalChan channel. Return true if at least one signal was delivered this
// way, false otherwise.
func checkSignals() bool {
Expand Down Expand Up @@ -467,6 +491,24 @@ func checkSignals() bool {
}
}

func sigSuspend() {
signals := atomic.LoadUint32(&activeSignals)
tinygo_sigsuspend(signals)
}

// mask a set of signals defined by the activeSignals bitmap (32bit)
// returns the C style sigset_t pointer.
func maskActiveSignals() unsafe.Pointer {
signals := atomic.LoadUint32(&activeSignals)
return tinygo_mask_signals(signals)
}

// unmask a set of signals defined by the activeSignals bitmap (32bit)
func unmaskActiveSignals() {
signals := atomic.LoadUint32(&activeSignals)
tinygo_unmask_signals(signals)
}

func waitForEvents() {
if atomic.LoadUint32(&hasSignals) != 0 {
// TODO: there is a race condition here. If a signal arrives between
Expand All @@ -479,9 +521,17 @@ func waitForEvents() {
// - unmask all active signals
// For a longer explanation of the problem, see:
// https://www.cipht.net/2023/11/30/perils-of-pause.html

// When a signal arrives while masked by the process, it remains pending
// until the process unmasks it.
maskActiveSignals()

checkSignals()
pause()
sigSuspend()

checkSignals()
unmaskActiveSignals()

} else {
// The program doesn't use signals, so this is a deadlock.
runtimePanic("deadlocked: no event source")
Expand Down
59 changes: 53 additions & 6 deletions src/runtime/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,67 @@
void tinygo_signal_handler(int sig);

// Enable a signal from the runtime.
void tinygo_signal_enable(uint32_t sig) {
struct sigaction act = { 0 };
void tinygo_signal_enable(uint32_t sig)
{
struct sigaction act = {0};
act.sa_handler = &tinygo_signal_handler;
sigaction(sig, &act, NULL);
}

void tinygo_signal_ignore(uint32_t sig) {
struct sigaction act = { 0 };
void tinygo_signal_ignore(uint32_t sig)
{
struct sigaction act = {0};
act.sa_handler = SIG_IGN;
sigaction(sig, &act, NULL);
}

void tinygo_signal_disable(uint32_t sig) {
struct sigaction act = { 0 };
void tinygo_signal_disable(uint32_t sig)
{
struct sigaction act = {0};
act.sa_handler = SIG_DFL;
sigaction(sig, &act, NULL);
}

// https://www.cipht.net/2023/11/30/perils-of-pause.html#text-3
void *tinygo_mask_signals(uint32_t mask)
{
sigset_t set, prev;
sigemptyset(&set);
for (int i = 0; i < 32; i++)
{
if (mask & (1 << i))
{
sigaddset(&set, i);
}
}
sigprocmask(SIG_BLOCK, &set, &prev);
return (void *)(&set);
}

void tinygo_unmask_signals(uint32_t mask)
{
sigset_t set, prev;
sigemptyset(&set);
for (int i = 0; i < 32; i++)
{
if (mask & (1 << i))
{
sigaddset(&set, i);
}
}
sigprocmask(SIG_UNBLOCK, &set, &prev);
}

void tinygo_sigSuspend(uint32_t mask)
{
sigset_t set;
sigemptyset(&set);
for (int i = 0; i < 32; i++)
{
if (mask & (1 << i))
{
sigaddset(&set, i);
}
}
sigsuspend(&set);
}

0 comments on commit 98cb692

Please sign in to comment.