Landin library reference source

hosted/io/time.ldn

1--  The hosted provider's clocks and entropy, written once over
2--  platform/posix: `io.clock` is the monotonic clock and sleeping on it,
3--  `io.wall_clock` the realtime clock, and `io.entropy` the system's own
4--  random bytes.
5
6import core/io
7import platform/posix
8
9--  A second in nanoseconds.
10second: u64 = 1000000000
11
12--  The longest one request to sleep asks for, about eleven days.
13longest: u64 = 1000000 * second
14
15--  The clock `clock` read now.  Every hosted system has both clocks, and
16--  reading one fails only when the process is given a bad address, which
17--  no call here gives, so a failure ends the program.
18read: (clock: posix.clock_kind) -> (time: posix.time_value) =
19    time = posix.clock_time(clock) else (problem)
20        _ = problem
21        compiler.unreachable()
22    end
23end read
24
25--  The monotonic clock in nanoseconds since the system started, which no
26--  hosted system counts as negative.
27reading: () -> (at: u64) =
28    time: posix.time_value = read(posix.monotonic_clock)
29    at = u64(time.seconds) * second + u64(time.nanoseconds)
30end reading
31
32system_now: (self: system) -> (at: io.instant) =
33    _ = self.failure
34    at = io.instant(reading())
35end system_now
36
37--  The sleep is measured on the clock `now` reads, whatever clock the
38--  system sleeps on: the deadline is taken first, and the system is asked
39--  again for what is left until the clock has reached it, so a sleep a
40--  signal interrupts, or the system ends early, sleeps on for the rest.
41--  A deadline past the clock's last instant is that instant.  A system
42--  that refused every sleep would leave this loop reading the clock until
43--  the deadline, which is still never early; none of them does.
44system_sleep: (self: system, span: io.duration) -> none =
45    _ = self.failure
46    start: u64 = reading()
47    room: u64 = (0 -% 1) - start
48    deadline: u64 = if u64(span) > room then 0 -% 1
49                    else start + u64(span) end if
50    loop do
51        now: u64 = reading()
52        return when now >= deadline
53        --  No more than a million seconds at once, within what every
54        --  system's sleep takes; the loop sleeps again for the rest.
55        mut left: u64 = deadline - now
56        if left > longest then left = longest end if
57        whole: u64 = left / second
58        posix.sleep((seconds: i64(whole),
59                     nanoseconds: u32(left - whole * second))) else (problem)
60            --  Interrupted, or refused: the clock says what is left.
61            _ = problem
62        end
63    end loop
64end system_sleep
65
66system_wall_now: (self: system) -> (at: io.wall_time) =
67    _ = self.failure
68    time: posix.time_value = read(posix.realtime_clock)
69    at = (seconds: time.seconds, nanoseconds: time.nanoseconds)
70end system_wall_now
71
72--  The system gives at most so much at once, and a signal may interrupt it
73--  before it gives anything; both are asked again for the rest.
74system_fill_random: (self: system, into: []mut u8) -> none ! io.io_failed =
75    self.failure.val = 0
76    mut filled: usize = 0
77    while filled < lenof into do
78        got: usize = posix.random_bytes(into[filled..<lenof into]) else (problem)
79            continue when problem == posix.eintr
80            record(self, problem)
81            fail io.io_failed
82        end
83        fail io.io_failed when got == 0 or got > lenof into - filled
84        filled += got
85    end while
86end system_fill_random
87
88system is io.clock (now: system_now, sleep: system_sleep)
89system is io.wall_clock (wall_now: system_wall_now)
90system is io.entropy (fill_random: system_fill_random)