Landin library reference source

core/io/time.ldn

1--  Time: a monotonic clock and sleeping on it, and a wall clock, two
2--  concepts beside the others and outside `world`, so a provider without
3--  a clock, and a firmware image that never asks for the time, carries
4--  none of them.  Time is counted in nanoseconds in a `u64`: no floating
5--  point, the same arithmetic on every target, and room for 584 years.
6
7duration_value: type = distinct u64
8instant_value: type = distinct u64
9
10--- A span of time, a whole number of nanoseconds from zero to 2^64 - 1,
11--- about 584 years.  `u64(span)` is the count and `io.duration(n)` makes
12--- one, as `nanoseconds` does; `milliseconds` and the others make one from
13--- a coarser count and trap when it would not fit.  Two spans compare with
14--- `==`, `<>`, `<`, `<=`, `>` and `>=`, as their counts do; the type has
15--- no arithmetic of its own, so an instant is never added to an instant.
16public duration: type = duration_value
17
18--- A reading of a monotonic clock: nanoseconds since a start the provider
19--- fixes, which is the system's boot on Linux.  Only the difference
20--- between two readings of one provider means anything, `elapsed`; the
21--- count is `u64(at)`.  Two instants compare with `==`, `<>`, `<`, `<=`,
22--- `>` and `>=`, as their counts do.
23public instant: type = instant_value
24
25--- A reading of the wall clock: UTC seconds since 1970-01-01T00:00:00,
26--- negative before it, and the nanoseconds after them, from 0 to
27--- 999,999,999.  Leap seconds are not counted, as POSIX counts none.
28--- Calendars, time zones and formatting a date are not this library's.
29public wall_time: type = struct
30    seconds: i64
31    nanoseconds: u32
32end wall_time
33
34--- A monotonic clock and sleeping on it.  `now` never goes back: a later
35--- reading of one provider is never less than an earlier one, whatever
36--- the wall clock does.  `sleep` returns once at least `span` has passed
37--- on this clock, measured from when it was called; it may return later,
38--- as late as the system schedules it, and never sooner.
39public clock: type = concept (provider: type)
40    --- The clock's reading now.
41    now: (self: provider) -> (at: instant)
42    --- Wait until at least `span` has passed on this clock.
43    sleep: (self: provider, span: duration) -> none
44end clock
45
46--- The wall clock: what time it is in UTC, as the system keeps it.  It is
47--- a concept of its own because a firmware image may count time without
48--- knowing the date, and because the system may set this clock, so two
49--- readings may go back; time a task with `clock`, never with this.
50public wall_clock: type = concept (provider: type)
51    --- The wall clock's reading now.
52    wall_now: (self: provider) -> (at: wall_time)
53end wall_clock
54
55--- The reading of a monotonic clock now, through `clock` evidence.
56public now: (provider: type is clock, host: provider) -> (at: instant) =
57    at = provider.now(host)
58end now
59
60--- Wait until at least `span` has passed on the provider's monotonic
61--- clock.  A sleep a signal interrupts on a hosted system sleeps on for
62--- what is left; a sleep of zero returns at once.
63public sleep: (provider: type is clock, host: provider, span: duration)
64              -> none =
65    provider.sleep(host, span)
66end sleep
67
68--- The wall clock's reading now, through `wall_clock` evidence.
69public wall_now: (provider: type is wall_clock, host: provider)
70                 -> (at: wall_time) =
71    at = provider.wall_now(host)
72end wall_now
73
74--- A span of `count` nanoseconds.
75public nanoseconds: (count: u64) -> (span: duration) =
76    span = duration_value(count)
77end nanoseconds
78
79--- A span of `count` microseconds; it traps beyond 584 years.
80public microseconds: (count: u64) -> (span: duration) =
81    span = duration_value(count * 1000)
82end microseconds
83
84--- A span of `count` milliseconds; it traps beyond 584 years.
85public milliseconds: (count: u64) -> (span: duration) =
86    span = duration_value(count * 1000000)
87end milliseconds
88
89--- A span of `count` seconds; it traps beyond 584 years.
90public seconds: (count: u64) -> (span: duration) =
91    span = duration_value(count * 1000000000)
92end seconds
93
94--- The span from `earlier` to `later`, two readings of one clock taken in
95--- that order.  It traps when `later` is the earlier of the two, which
96--- two readings of one monotonic clock taken in order never are.
97public elapsed: (earlier: instant, later: instant) -> (span: duration) =
98    span = duration_value(u64(later) - u64(earlier))
99end elapsed
100
101--- The instant `span` after `at`, a deadline; it traps past the clock's
102--- last instant, 584 years after its start.
103public later: (at: instant, span: duration) -> (result: instant) =
104    result = instant_value(u64(at) + u64(span))
105end later
106
107--- The whole milliseconds in `span`, rounded down.
108public in_milliseconds: (span: duration) -> (count: u64) =
109    count = u64(span) / 1000000
110end in_milliseconds
111
112--  A clock a test sets and advances, beside the memory world.
113
114--- Caller-owned state of a manual clock: what its monotonic clock reads,
115--- in nanoseconds, what its wall clock reads, and how often each was read
116--- and slept on and how long.  Declare it `zeroed` and give it to
117--- `clock_over`, which fills it; it must outlive the clock's handle.  Its
118--- fields may be read, and the readings set, while the handle is in use:
119--- set `monotonic` only forward, as a monotonic clock never goes back, and
120--- `wall_seconds` either way, as a system's wall clock may be set.
121public clock_space: type = struct
122    monotonic: u64
123    wall_seconds: i64
124    wall_nanoseconds: u32
125    readings: usize
126    sleeps: usize
127    slept: u64
128end clock_space
129
130manual_value: type = struct
131    state: ptr mut clock_space
132end manual_value
133
134--- A clock that moves only when it is told: `advance` moves it, and
135--- `sleep` advances it by exactly the span asked and returns at once, so a
136--- test of code that sleeps or times itself takes no time and gives the
137--- same answer every run.  Both of its clocks move together.  A provider
138--- of `clock` and `wall_clock`; copies share the state.
139public manual_clock: type = manual_value
140
141--- Fill `state` for a manual clock whose monotonic clock reads `start` and
142--- whose wall clock reads `wall`, and return its handle.
143public clock_over: (inout state: clock_space, start: instant,
144                    wall: wall_time) -> (host: manual_clock from state) =
145    state = (monotonic: u64(start), wall_seconds: wall.seconds,
146             wall_nanoseconds: wall.nanoseconds, readings: 0, sleeps: 0,
147             slept: 0)
148    host = (state: addr state)
149end clock_over
150
151--- Move a manual clock's monotonic and wall clocks forward by `span`; it
152--- traps past the monotonic clock's last instant.
153public advance: (host: manual_clock, span: duration) -> none =
154    state: ptr mut clock_space = host.state
155    state.val.monotonic = state.val.monotonic + u64(span)
156    whole: u64 = u64(span) / 1000000000
157    mut part: u32 = state.val.wall_nanoseconds
158                    + u32(u64(span) - whole * 1000000000)
159    mut carried: i64 = i64(whole)
160    if part >= 1000000000 then
161        part -= 1000000000
162        inc carried
163    end if
164    state.val.wall_seconds = state.val.wall_seconds + carried
165    state.val.wall_nanoseconds = part
166end advance
167
168manual_now: (self: manual_clock) -> (at: instant) =
169    inc self.state.val.readings
170    at = instant_value(self.state.val.monotonic)
171end manual_now
172
173manual_sleep: (self: manual_clock, span: duration) -> none =
174    inc self.state.val.sleeps
175    self.state.val.slept = self.state.val.slept + u64(span)
176    advance(self, span)
177end manual_sleep
178
179manual_wall_now: (self: manual_clock) -> (at: wall_time) =
180    inc self.state.val.readings
181    at = (seconds: self.state.val.wall_seconds,
182          nanoseconds: self.state.val.wall_nanoseconds)
183end manual_wall_now
184
185manual_clock is clock (now: manual_now, sleep: manual_sleep)
186manual_clock is wall_clock (wall_now: manual_wall_now)