1-- The system's two clocks, the monotonic one and the wall clock, read
2-- as seconds and nanoseconds, and sleeping on the monotonic one.
3
4import platform/libc
5import platform/linux
6import platform/linux/vdso
7
8--- The clock that counts steadily from a point the system fixes, its
9--- start, and is never set: POSIX's `CLOCK_MONOTONIC`. Only the
10--- difference between two of its readings means anything.
11public monotonic_clock: atom
12
13--- The wall clock, UTC seconds and nanoseconds since 1970-01-01 without
14--- leap seconds, as the system keeps it, which may be set and so jump
15--- either way: POSIX's `CLOCK_REALTIME`.
16public realtime_clock: atom
17
18--- Which clock `clock_time` reads: `monotonic_clock` or `realtime_clock`.
19public clock_kind: type = monotonic_clock | realtime_clock
20
21--- A time or a span of time, POSIX's `struct timespec`: whole seconds and
22--- the nanoseconds after them, from 0 to 999,999,999.
23public time_value: type = struct
24 seconds: i64
25 nanoseconds: u32
26end time_value
27
28--- The time the clock `clock` reads now, POSIX's `clock_gettime`, or the
29--- errno. On Linux it is the vDSO's `clock_gettime` when the auxiliary
30--- vector names a vDSO that has one, which answers without entering the
31--- kernel, under `--link=libc` too, and the system call otherwise; on
32--- Darwin and FreeBSD it is the C library's.
33public link(symbol: "_landin_posix_clock_time") clock_time:
34 (clock: clock_kind) -> (time: time_value) ! errno =
35 time = try system_clock_time(clock)
36end clock_time
37
38--- Sleep for `span`, POSIX's `nanosleep`, measured on the monotonic clock
39--- on Linux and on the system's own clock for sleeping elsewhere: or the
40--- errno, `eintr` when a signal came first, which leaves part of the span
41--- unslept, and `einval` for nanoseconds of a second or more. It may
42--- return late, never early, of the clock it sleeps on.
43public link(symbol: "_landin_posix_sleep") sleep:
44 (span: time_value) -> none ! errno =
45 fail einval when span.seconds < 0 or span.nanoseconds >= 1000000000
46 try system_sleep(span)
47end sleep
48
49-- The system's own calls beneath the facility above: Linux's system
50-- calls through platform/linux, and the C library through
51-- platform/libc on Darwin and FreeBSD, whose system interface it is.
52
53fixed if compiler.os == linux then
54 -- The vDSO's `clock_gettime`, found the first time a clock is read:
55 -- zero before then, one when there is none, its address otherwise.
56 -- Process state of a single thread, as the heap's classes are: a C
57 -- thread calling in under --link=libc, the only other thread there
58 -- can be, finds the same vDSO and stores the same answer. Threads of
59 -- the program's own are the threads item's.
60 mut vdso_clock: u64
61
62 no_vdso_clock: u64 = 1
63
64 -- The vDSO's clock function, found once and kept.
65 vdso_clock_address: () -> (address: u64) =
66 address = vdso_clock
67 return when address <> 0
68 (found, address: at) := vdso.symbol(vdso.clock_symbol,
69 vdso.clock_version)
70 address = if found and u64(at) > no_vdso_clock then u64(at)
71 else no_vdso_clock end if
72 vdso_clock = address
73 end vdso_clock_address
74
75 clock_number: (clock: clock_kind) -> (number: i32) =
76 number = match clock
77 monotonic_clock: linux.clock_monotonic
78 realtime_clock: linux.clock_realtime
79 end match
80 end clock_number
81
82 -- The vDSO's function is made a value of its C type from its address,
83 -- as a looked-up symbol is.
84 system_clock_time: (clock: clock_kind) -> (time: time_value) ! errno =
85 mut record: linux.timespec = zeroed
86 mut result: i32 = -1
87 address: u64 = vdso_clock_address()
88 if address <> no_vdso_clock then
89 function: vdso.clock_gettime =
90 vdso.clock_gettime(usize(address))
91 result = function(clock_number(clock), addr record)
92 end if
93 if result <> 0 then
94 result = linux.clock_gettime(clock_number(clock), addr record)
95 fail errno_of(-result) when result < 0
96 end if
97 fail unlisted when record.nanoseconds < 0
98 or record.nanoseconds >= 1000000000
99 time = (seconds: record.seconds, nanoseconds: u32(record.nanoseconds))
100 end system_clock_time
101
102 system_sleep: (span: time_value) -> none ! errno =
103 request: linux.timespec = (seconds: span.seconds,
104 nanoseconds: i64(span.nanoseconds))
105 mut remaining: linux.timespec = zeroed
106 result: i32 = linux.clock_nanosleep(linux.clock_monotonic, 0,
107 addr request, addr remaining)
108 fail errno_of(-result) when result < 0
109 end system_sleep
110elsif compiler.os == darwin or compiler.os == freebsd then
111 system_clock_time: (clock: clock_kind) -> (time: time_value) ! errno =
112 number: i32 = match clock
113 monotonic_clock: libc.clock_monotonic
114 realtime_clock: libc.clock_realtime
115 end match
116 mut record: libc.timespec = zeroed
117 fail failure() when libc.clock_gettime(number, addr record) <> 0
118 fail unlisted when record.nanoseconds < 0
119 or record.nanoseconds >= 1000000000
120 time = (seconds: record.seconds, nanoseconds: u32(record.nanoseconds))
121 end system_clock_time
122
123 system_sleep: (span: time_value) -> none ! errno =
124 request: libc.timespec = (seconds: span.seconds,
125 nanoseconds: i64(span.nanoseconds))
126 mut remaining: libc.timespec = zeroed
127 fail failure() when libc.nanosleep(addr request, addr remaining) <> 0
128 end system_sleep
129end if