1-- Waiting for descriptors to be ready: POSIX's `poll`, its records and
2-- the events it reports.
3
4import platform/libc
5import platform/linux
6
7events_value: type = distinct u16
8
9--- Poll events, POSIX's `poll` bits as a set: a value combines with `|`,
10--- `&` and `~` and compares with `==` and `<>`. `poll_input`,
11--- `poll_output` and `poll_urgent` are what an entry can ask about;
12--- `poll_error`, `poll_hung_up` and `poll_invalid` are only ever
13--- reported. The bits are the same on Linux,
14--- Darwin and FreeBSD.
15public poll_events: type = events_value
16
17--- No event.
18public poll_no_events: poll_events = events_value(0)
19
20--- Data to read, or the end of a file.
21public poll_input: poll_events = events_value(1)
22
23--- Out-of-band data to read.
24public poll_urgent: poll_events = events_value(2)
25
26--- Room to write.
27public poll_output: poll_events = events_value(4)
28
29--- An error on the file.
30public poll_error: poll_events = events_value(8)
31
32--- The peer has gone.
33public poll_hung_up: poll_events = events_value(16)
34
35--- A descriptor that is not open.
36public poll_invalid: poll_events = events_value(32)
37
38-- C's `struct pollfd`, the same on all three systems, so a slice of
39-- entries is what the system reads and fills: `short` fields, whose bits
40-- these are.
41poll_record: type = struct
42 descriptor: descriptor
43 events: poll_events
44 returned: poll_events
45end poll_record
46
47--- One descriptor for `poll` to watch and the events asked about, made by
48--- `watch`, and after the call what happened, which `happened` reads.
49public poll_entry: type = poll_record
50
51--- An entry watching `watched` for the events `wanted` asks about; its
52--- `poll_error`, `poll_hung_up` and `poll_invalid` are reported whether
53--- asked or not.
54public link(symbol: "_landin_posix_watch") watch:
55 (watched: descriptor, wanted: poll_events)
56 -> (entry: poll_entry) =
57 asked: poll_events = poll_input | poll_urgent | poll_output
58 entry = (descriptor: watched, events: wanted & asked,
59 returned: poll_no_events)
60end watch
61
62--- What happened to an entry's descriptor in the last `poll`; nothing,
63--- before one.
64public link(symbol: "_landin_posix_happened") happened:
65 (entry: poll_entry) -> (events: poll_events) =
66 events = entry.returned
67end happened
68
69--- Wait until an event happens to one of `entries`' descriptors, or
70--- `milliseconds` pass, waiting indefinitely when it is negative and not
71--- at all when it is zero, POSIX's `poll`: how many entries had an event,
72--- each of which `happened` reads, zero when the time passed first, or
73--- the errno, `eintr` when a signal came first.
74public link(symbol: "_landin_posix_poll") poll:
75 (entries: []mut poll_entry, milliseconds: i32)
76 -> (ready: usize) ! errno =
77 ready = try system_poll(entries, milliseconds)
78end poll
79
80-- The system's own calls beneath the facility above: Linux's system
81-- calls through platform/linux, and the C library through
82-- platform/libc on Darwin and FreeBSD, whose system interface it is.
83
84fixed if compiler.os == linux then
85 -- arm64 and RV64 have no `poll` call, so every architecture makes
86 -- `ppoll`, with the milliseconds as its time limit.
87 system_poll: (entries: []mut poll_entry, milliseconds: i32)
88 -> (ready: usize) ! errno =
89 mut spare: linux.poll_record = zeroed
90 records: ptr mut linux.poll_record =
91 if lenof entries > 0 then ptr(usize(addr entries[0]))
92 else addr spare end if
93 mut limit: linux.timespec = zeroed
94 limit.seconds = i64(milliseconds / 1000)
95 limit.nanoseconds = i64(milliseconds % 1000) * 1000000
96 bound: linux.poll_limit = if milliseconds < 0 then linux.no_limit
97 else addr limit end if
98 result: i32 = linux.ppoll(records, lenof entries, bound)
99 fail errno_of(-result) when result < 0
100 ready = usize(result)
101 end system_poll
102elsif compiler.os == darwin or compiler.os == freebsd then
103 system_poll: (entries: []mut poll_entry, milliseconds: i32)
104 -> (ready: usize) ! errno =
105 mut spare: libc.poll_record = zeroed
106 records: ptr mut libc.poll_record =
107 if lenof entries > 0 then ptr(usize(addr entries[0]))
108 else addr spare end if
109 result: i32 = libc.poll(records, libc.poll_count(lenof entries),
110 milliseconds)
111 fail failure() when result < 0
112 ready = usize(result)
113 end system_poll
114end if