Source file clock.ml

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)