Landin library reference discuss source

platform/posix/time.ldn

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