1-- The event provider's `signal.signals` (D449, D477, D482): a watched
2-- signal is taken from its default disposition, blocked and read through
3-- a signalfd on Linux and ignored and reported by kqueue's
4-- EVFILT_SIGNAL on Darwin and FreeBSD, and every child a program starts
5-- gets its default back (D479). An arrival the loop reports is kept
6-- until a fibre reads it with `next_signal`, which waits as a receive
7-- does, taking turns; two arrivals of one signal before it is read are
8-- one event (D488).
9
10import core/io
11import core/io/signal
12import platform/posix
13
14-- Where a signal sits in `signal_numbers` and the arrivals.
15slot: (which: signal.kind) -> (index: usize) =
16 index = match which
17 signal.interrupt: 0
18 signal.terminate: 1
19 signal.hangup: 2
20 signal.child: 3
21 signal.user1: 4
22 signal.user2: 5
23 end match
24end slot
25
26-- The signal at `index`, `slot`'s inverse.
27kind_at: (index: usize) -> (which: signal.kind) =
28 which = if index == 0 then signal.interrupt
29 elsif index == 1 then signal.terminate
30 elsif index == 2 then signal.hangup
31 elsif index == 3 then signal.child
32 elsif index == 4 then signal.user1
33 else signal.user2 end if
34end kind_at
35
36-- Keep an arrival of the system's signal `number`, unless one waits to
37-- be read already or it is no signal this provider watches.
38arrive: (state: ptr mut loop_state, number: i32) -> none =
39 for index in usize(0)..<kinds do
40 if signal_numbers[index] == number then
41 for at in usize(0)..<state.val.queued do
42 return when usize(state.val.arrived[at]) == index
43 end for
44 state.val.arrived[state.val.queued] = u8(index)
45 inc state.val.queued
46 return
47 end if
48 end for
49end arrive
50
51events_watch: (self: host, which: signal.kind) -> none ! io.io_failed =
52 cleared(self)
53 state: ptr mut loop_state = self.state
54 number: i32 = signal_numbers[slot(which)]
55 bit: u64 = u64(1) << u64(number - 1)
56 state.val.stopped = false
57 return when (state.val.watched & bit) <> 0
58 -- Named first, so that no child started from now on inherits what
59 -- taking it does to its disposition.
60 posix.default_in_children(number) else (problem)
61 record(self, problem)
62 fail io.io_failed
63 end
64 take_signal(state, number) else (problem)
65 record(self, problem)
66 fail io.io_failed
67 end
68 state.val.watched = state.val.watched | bit
69end events_watch
70
71events_next_signal: (self: host) -> (received: signal.event)
72 ! io.io_failed =
73 cleared(self)
74 state: ptr mut loop_state = self.state
75 loop do
76 fail io.io_failed when state.val.stopped
77 collect(state)
78 if state.val.queued > 0 then
79 index: usize = usize(state.val.arrived[0])
80 dec state.val.queued
81 for at in usize(0)..<state.val.queued do
82 state.val.arrived[at] = state.val.arrived[at + 1]
83 end for
84 received = (which: kind_at(index),
85 number: u32(signal_numbers[index]))
86 return
87 end if
88 mut entry: waiting = (next: 0, descriptor: -1, output: false,
89 child: 0, signalled: true, deadline: 0,
90 answer: pending)
91 answer: u8 = park(self, addr entry) else (problem)
92 record(self, problem)
93 fail io.io_failed
94 end
95 fail io.io_failed when answer == failed
96 end loop
97end events_next_signal
98
99events_stop_watching: (self: host) -> none ! io.io_failed =
100 cleared(self)
101 state: ptr mut loop_state = self.state
102 mut returned: bool = true
103 give_signals(state) else (problem)
104 record(self, problem)
105 returned = false
106 end
107 state.val.watched = 0
108 state.val.queued = 0
109 state.val.stopped = true
110 answer_signals(state, failed)
111 fail io.io_failed when not returned
112end events_stop_watching
113
114host is signal.signals (watch: events_watch, next_signal: events_next_signal,
115 stop_watching: events_stop_watching)