Landin library reference discuss source

hosted/events/signals.ldn

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)