aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/runtime/signal.c
blob: a462518c13a63949f804ab00b3be0198b08a319a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//go:build none

// Ignore the //go:build above. This file is manually included on Linux and
// MacOS to provide os/signal support.

#include <stdint.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>

// Signal handler in the runtime.
void tinygo_signal_handler(int sig);

// Enable a signal from the runtime.
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 };
    act.sa_handler = SIG_IGN;
    sigaction(sig, &act, NULL);
}

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

// Implement waitForEvents and sleep with signals.
// Warning: sigprocmask is not defined in a multithreaded program so will need
// to be replaced with something else once we implement threading on POSIX.

// Signals active before a call to tinygo_wfi_mask.
static sigset_t active_signals;

static void tinygo_set_signals(sigset_t *mask, uint32_t signals) {
    sigemptyset(mask);
    for (int i=0; i<32; i++) {
        if ((signals & (1<<i)) != 0) {
            sigaddset(mask, i);
        }
    }
}

// Mask the given signals.
// This function must always restore the previous signals using
// tinygo_wfi_unmask, to create a critical section.
void tinygo_wfi_mask(uint32_t active) {
    sigset_t mask;
    tinygo_set_signals(&mask, active);

    sigprocmask(SIG_BLOCK, &mask, &active_signals);
}

// Wait until a signal becomes pending (or is already pending), and return the
// signal.
#if !defined(__APPLE__)
int tinygo_wfi_sleep(uint32_t active, uint64_t timeout) {
    sigset_t active_set;
    tinygo_set_signals(&active_set, active);

    struct timespec ts = {0};
    ts.tv_sec  = timeout / 1000000000;
    ts.tv_nsec = timeout % 1000000000;

    int result = sigtimedwait(&active_set, NULL, &ts);
    return result;
}
#endif

// Wait until any of the active signals becomes pending (or returns immediately
// if one is already pending).
int tinygo_wfi_wait(uint32_t active) {
    sigset_t active_set;
    tinygo_set_signals(&active_set, active);

    int sig = 0;
    sigwait(&active_set, &sig);
    return sig;
}

// Restore previous signal mask.
void tinygo_wfi_unmask(void) {
    sigprocmask(SIG_SETMASK, &active_signals, NULL);
}