Skip to content

Commit 35feb3a

Browse files
authored
Merge pull request #338 from talex5/clocks3
Add Time.Mono for monotonic clocks
2 parents 11011dd + ce4b392 commit 35feb3a

File tree

18 files changed

+299
-113
lines changed

18 files changed

+299
-113
lines changed

lib_eio/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
(name eio)
33
(public_name eio)
44
(flags (:standard -open Eio__core -open Eio__core.Private))
5-
(libraries eio__core cstruct lwt-dllist fmt bigstringaf optint))
5+
(libraries eio__core cstruct lwt-dllist fmt bigstringaf optint mtime))

lib_eio/eio.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ module Stdenv = struct
3737
net : Net.t;
3838
domain_mgr : Domain_manager.t;
3939
clock : Time.clock;
40+
mono_clock : Time.Mono.t;
4041
fs : Fs.dir Path.t;
4142
cwd : Fs.dir Path.t;
4243
secure_random : Flow.source;
@@ -49,6 +50,7 @@ module Stdenv = struct
4950
let net (t : <net : #Net.t; ..>) = t#net
5051
let domain_mgr (t : <domain_mgr : #Domain_manager.t; ..>) = t#domain_mgr
5152
let clock (t : <clock : #Time.clock; ..>) = t#clock
53+
let mono_clock (t : <mono_clock : #Time.Mono.t; ..>) = t#mono_clock
5254
let secure_random (t: <secure_random : #Flow.source; ..>) = t#secure_random
5355
let fs (t : <fs : #Fs.dir Path.t; ..>) = t#fs
5456
let cwd (t : <cwd : #Fs.dir Path.t; ..>) = t#cwd

lib_eio/eio.mli

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ module Stdenv : sig
179179
net : Net.t;
180180
domain_mgr : Domain_manager.t;
181181
clock : Time.clock;
182+
mono_clock : Time.Mono.t;
182183
fs : Fs.dir Path.t;
183184
cwd : Fs.dir Path.t;
184185
secure_random : Flow.source;
@@ -232,7 +233,10 @@ module Stdenv : sig
232233
*)
233234

234235
val clock : <clock : #Time.clock as 'a; ..> -> 'a
235-
(** [clock t] is the system clock. *)
236+
(** [clock t] is the system clock (used to get the current time and date). *)
237+
238+
val mono_clock : <mono_clock : #Time.Mono.t as 'a; ..> -> 'a
239+
(** [mono_clock t] is a monotonic clock (used for measuring intervals). *)
236240

237241
(** {1 Randomness} *)
238242

lib_eio/mock/clock.ml

Lines changed: 98 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,111 @@
11
open Eio.Std
22

3-
type t = <
4-
Eio.Time.clock;
5-
advance : unit;
6-
set_time : float -> unit;
7-
>
8-
9-
module Key = struct
10-
type t = < >
11-
let compare = compare
12-
end
3+
module type S = sig
4+
type time
5+
6+
type t = <
7+
time Eio.Time.clock_base;
8+
advance : unit;
9+
set_time : time -> unit;
10+
>
1311

14-
module Job = struct
15-
type t = {
16-
time : float;
17-
resolver : unit Promise.u;
18-
}
12+
val make : unit -> t
13+
val advance : t -> unit
14+
val set_time : t -> time -> unit
15+
end
1916

20-
let compare a b = Float.compare a.time b.time
17+
module type TIME = sig
18+
type t
19+
val zero : t
20+
val compare : t -> t -> int
21+
val pp : t Fmt.t
2122
end
2223

23-
module Q = Psq.Make(Key)(Job)
24+
module Make(T : TIME) : S with type time := T.t = struct
25+
type t = <
26+
T.t Eio.Time.clock_base;
27+
advance : unit;
28+
set_time : T.t -> unit;
29+
>
30+
31+
module Key = struct
32+
type t = < >
33+
let compare = compare
34+
end
35+
36+
module Job = struct
37+
type t = {
38+
time : T.t;
39+
resolver : unit Promise.u;
40+
}
41+
42+
let compare a b = T.compare a.time b.time
43+
end
44+
45+
module Q = Psq.Make(Key)(Job)
46+
47+
let make () =
48+
object (self)
49+
inherit [T.t] Eio.Time.clock_base
2450

25-
let make () =
26-
object (self)
27-
inherit Eio.Time.clock
51+
val mutable now = T.zero
52+
val mutable q = Q.empty
2853

29-
val mutable now = 0.0
30-
val mutable q = Q.empty
54+
method now = now
3155

32-
method now = now
56+
method sleep_until time =
57+
if T.compare time now <= 0 then Fiber.yield ()
58+
else (
59+
let p, r = Promise.create () in
60+
let k = object end in
61+
q <- Q.add k { time; resolver = r } q;
62+
try
63+
Promise.await p
64+
with Eio.Cancel.Cancelled _ as ex ->
65+
q <- Q.remove k q;
66+
raise ex
67+
)
3368

34-
method sleep_until time =
35-
if time <= now then Fiber.yield ()
36-
else (
37-
let p, r = Promise.create () in
38-
let k = object end in
39-
q <- Q.add k { time; resolver = r } q;
40-
try
41-
Promise.await p
42-
with Eio.Cancel.Cancelled _ as ex ->
43-
q <- Q.remove k q;
44-
raise ex
45-
)
69+
method set_time time =
70+
let rec drain () =
71+
match Q.min q with
72+
| Some (_, v) when T.compare v.time time <= 0 ->
73+
Promise.resolve v.resolver ();
74+
q <- Option.get (Q.rest q);
75+
drain ()
76+
| _ -> ()
77+
in
78+
drain ();
79+
now <- time;
80+
traceln "mock time is now %a" T.pp now
4681

47-
method set_time time =
48-
let rec drain () =
82+
method advance =
4983
match Q.min q with
50-
| Some (_, v) when v.time <= time ->
51-
Promise.resolve v.resolver ();
52-
q <- Option.get (Q.rest q);
53-
drain ()
54-
| _ -> ()
55-
in
56-
drain ();
57-
now <- time;
58-
traceln "mock time is now %g" now
59-
60-
method advance =
61-
match Q.min q with
62-
| None -> invalid_arg "No further events scheduled on mock clock"
63-
| Some (_, v) -> self#set_time v.time
64-
end
84+
| None -> invalid_arg "No further events scheduled on mock clock"
85+
| Some (_, v) -> self#set_time v.time
86+
end
87+
88+
let set_time (t:t) time = t#set_time time
89+
let advance (t:t) = t#advance
90+
end
91+
92+
module Old_time = struct
93+
type t = float
94+
let compare = Float.compare
95+
let pp f x = Fmt.pf f "%g" x
96+
let zero = 0.0
97+
end
98+
99+
module Mono_time = struct
100+
type t = Mtime.t
101+
let compare = Mtime.compare
102+
let zero = Mtime.of_uint64_ns 0L
103+
104+
let pp f t =
105+
let s = Int64.to_float (Mtime.to_uint64_ns t) /. 1e9 in
106+
Fmt.pf f "%g" s
107+
end
108+
109+
module Mono = Make(Mono_time)
65110

66-
let set_time (t:t) time = t#set_time time
67-
let advance (t:t) = t#advance
111+
include Make(Old_time)

lib_eio/mock/clock.mli

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
1-
type t = <
2-
Eio.Time.clock;
3-
advance : unit;
4-
set_time : float -> unit;
5-
>
1+
module type S = sig
2+
type time
63

7-
val make : unit -> t
8-
(** [make ()] is a new clock.
4+
type t = <
5+
time Eio.Time.clock_base;
6+
advance : unit;
7+
set_time : time -> unit;
8+
>
99

10-
The time is initially set to 0.0 and doesn't change except when you call {!advance} or {!set_time}. *)
10+
val make : unit -> t
11+
(** [make ()] is a new clock.
1112
12-
val advance : t -> unit
13-
(** [advance t] sets the time to the next scheduled event (adding any due fibers to the run queue).
14-
@raise Invalid_argument if nothing is scheduled. *)
13+
The time is initially set to 0.0 and doesn't change except when you call {!advance} or {!set_time}. *)
1514

16-
val set_time : t -> float -> unit
17-
(** [set_time t time] sets the time to [time] (adding any due fibers to the run queue). *)
15+
val advance : t -> unit
16+
(** [advance t] sets the time to the next scheduled event (adding any due fibers to the run queue).
17+
@raise Invalid_argument if nothing is scheduled. *)
18+
19+
val set_time : t -> time -> unit
20+
(** [set_time t time] sets the time to [time] (adding any due fibers to the run queue). *)
21+
end
22+
23+
include S with type time := float
24+
25+
module Mono : S with type time := Mtime.t

lib_eio/time.ml

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,95 @@
11
exception Timeout
22

3+
class virtual ['a] clock_base = object
4+
method virtual now : 'a
5+
method virtual sleep_until : 'a -> unit
6+
end
7+
38
class virtual clock = object
4-
method virtual now : float
5-
method virtual sleep_until : float -> unit
9+
inherit [float] clock_base
610
end
711

8-
let now (t : #clock) = t#now
12+
let now (t : _ #clock_base) = t#now
913

10-
let sleep_until (t : #clock) time = t#sleep_until time
14+
let sleep_until (t : _ #clock_base) time = t#sleep_until time
1115

1216
let sleep t d = sleep_until t (now t +. d)
1317

18+
module Mono = struct
19+
class virtual t = object
20+
inherit [Mtime.t] clock_base
21+
end
22+
23+
let now = now
24+
let sleep_until = sleep_until
25+
26+
let sleep_span t span =
27+
match Mtime.add_span (now t) span with
28+
| Some time -> sleep_until t time
29+
| None -> Fiber.await_cancel ()
30+
31+
(* Converting floats via int64 is tricky when things overflow or go negative.
32+
Since we don't need to wait for more than 100 years, limit it to this: *)
33+
let too_many_ns = 0x8000000000000000.
34+
35+
let span_of_s s =
36+
if s >= 0.0 then (
37+
let ns = s *. 1e9 in
38+
if ns >= too_many_ns then Mtime.Span.max_span
39+
else Mtime.Span.of_uint64_ns (Int64.of_float ns)
40+
) else Mtime.Span.zero (* Also happens for NaN and negative infinity *)
41+
42+
let sleep (t : #t) s =
43+
sleep_span t (span_of_s s)
44+
end
45+
1446
let with_timeout t d = Fiber.first (fun () -> sleep t d; Error `Timeout)
1547
let with_timeout_exn t d = Fiber.first (fun () -> sleep t d; raise Timeout)
1648

1749
module Timeout = struct
1850
type t =
19-
| Timeout of clock * float
51+
| Timeout of Mono.t * Mtime.Span.t
52+
| Deprecated of clock * float
2053
| Unlimited
2154

2255
let none = Unlimited
23-
let of_s clock time = Timeout ((clock :> clock), time)
56+
let v clock time = Timeout ((clock :> Mono.t), time)
57+
58+
let seconds clock time =
59+
v clock (Mono.span_of_s time)
60+
61+
let of_s clock time =
62+
Deprecated ((clock :> clock), time)
2463

2564
let run t fn =
2665
match t with
2766
| Unlimited -> fn ()
2867
| Timeout (clock, d) ->
68+
Fiber.first (fun () -> Mono.sleep_span clock d; Error `Timeout) fn
69+
| Deprecated (clock, d) ->
2970
Fiber.first (fun () -> sleep clock d; Error `Timeout) fn
3071

3172
let run_exn t fn =
3273
match t with
3374
| Unlimited -> fn ()
3475
| Timeout (clock, d) ->
76+
Fiber.first (fun () -> Mono.sleep_span clock d; raise Timeout) fn
77+
| Deprecated (clock, d) ->
3578
Fiber.first (fun () -> sleep clock d; raise Timeout) fn
3679

80+
let pp_duration f d =
81+
if d >= 0.001 && d < 0.1 then
82+
Fmt.pf f "%.2gms" (d *. 1000.)
83+
else if d < 120. then
84+
Fmt.pf f "%.2gs" d
85+
else
86+
Fmt.pf f "%.2gm" (d /. 60.)
87+
3788
let pp f = function
3889
| Unlimited -> Fmt.string f "(no timeout)"
3990
| Timeout (_clock, d) ->
40-
if d >= 0.001 && d < 0.1 then
41-
Fmt.pf f "%.2gms" (d *. 1000.)
42-
else if d < 120. then
43-
Fmt.pf f "%.2gs" d
44-
else
45-
Fmt.pf f "%.2gm" (d /. 60.)
91+
let d = Mtime.Span.to_s d in
92+
pp_duration f d
93+
| Deprecated (_clock, d) ->
94+
pp_duration f d
4695
end

0 commit comments

Comments
 (0)