1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
open Eio.Std
type 'time ty = [`Mock | 'time Eio.Time.clock_ty]
module type S = sig
type time
type t = time ty r
val make : unit -> t
val advance : t -> unit
val try_advance : t -> bool
val set_time : t -> time -> unit
end
module type TIME = sig
type t
val zero : t
val compare : t -> t -> int
val pp : t Fmt.t
end
module Make(T : TIME) : S with type time := T.t = struct
type t = T.t ty r
module Key = struct
type t = < >
let compare = compare
end
module Job = struct
type t = {
time : T.t;
resolver : unit Promise.u;
}
let compare a b = T.compare a.time b.time
end
module Q = Psq.Make(Key)(Job)
module Impl = struct
type time = T.t
type t = {
mutable now : T.t;
mutable q : Q.t;
}
let make () =
{
now = T.zero;
q = Q.empty;
}
let now t = t.now
let sleep_until t time =
if T.compare time t.now <= 0 then Fiber.yield ()
else (
let p, r = Promise.create () in
let k = object end in
t.q <- Q.add k { time; resolver = r } t.q;
try
Promise.await p
with Eio.Cancel.Cancelled _ as ex ->
t.q <- Q.remove k t.q;
raise ex
)
let set_time t time =
let rec drain () =
match Q.min t.q with
| Some (_, v) when T.compare v.time time <= 0 ->
Promise.resolve v.resolver ();
t.q <- Option.get (Q.rest t.q);
drain ()
| _ -> ()
in
drain ();
t.now <- time;
traceln "mock time is now %a" T.pp t.now
let try_advance t =
match Q.min t.q with
| None -> false
| Some (_, v) -> set_time t v.time; true
type (_, _, _) Eio.Resource.pi += Raw : ('t, 't -> t, T.t ty) Eio.Resource.pi
let raw (Eio.Resource.T (t, ops)) = Eio.Resource.get ops Raw t
end
let handler =
Eio.Resource.handler (
H (Impl.Raw, Fun.id) ::
Eio.Resource.bindings (Eio.Time.Pi.clock (module Impl));
)
let make () =
Eio.Resource.T (Impl.make (), handler)
let set_time t v = Impl.set_time (Impl.raw t) v
let try_advance t = Impl.try_advance (Impl.raw t)
let advance t =
if not (try_advance t) then
invalid_arg "No further events scheduled on mock clock"
end
module Old_time = struct
type t = float
let compare = Float.compare
let pp f x = Fmt.pf f "%g" x
let zero = 0.0
end
module Mono_time = struct
type t = Mtime.t
let compare = Mtime.compare
let zero = Mtime.of_uint64_ns 0L
let pp f t =
let s = Int64.to_float (Mtime.to_uint64_ns t) /. 1e9 in
Fmt.pf f "%g" s
end
module Mono = Make(Mono_time)
include Make(Old_time)
|