Skip to content

Commit

Permalink
Clock: add timer module
Browse files Browse the repository at this point in the history
This module can be used to know when a certain amount has passed since the
timer startted. Useful for encoding timeouts, schedule actions periodically and
similar.

Signed-off-by: Pau Ruiz Safont <[email protected]>
  • Loading branch information
psafont committed Jun 21, 2024
1 parent e52990c commit 8f49e1f
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 2 deletions.
1 change: 1 addition & 0 deletions clock.opam
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ depends: [
"ocaml" {>= "4.12"}
"alcotest" {with-test}
"astring"
"mtime"
"ptime"
"odoc" {with-doc}
]
Expand Down
1 change: 1 addition & 0 deletions dune-project
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
(ocaml (>= 4.12))
(alcotest :with-test)
astring
mtime
ptime
)
)
Expand Down
2 changes: 1 addition & 1 deletion ocaml/libs/clock/date.ml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
*)
*)

let months =
[|
Expand Down
4 changes: 3 additions & 1 deletion ocaml/libs/clock/dune
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
(library
(name clock)
(public_name clock)
(modules date)
(modules date timer)
(libraries
astring
mtime
mtime.clock.os
ptime
ptime.clock.os
)
Expand Down
53 changes: 53 additions & 0 deletions ocaml/libs/clock/timer.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
type t = {start: Ptime.t; elapsed: Mtime_clock.counter; timeout: Mtime.Span.t}

type remaining = Spare of Mtime.Span.t | Excess of Mtime.Span.t

let is_shorter a ~than:b = Mtime.Span.compare a b < 0

let is_longer a ~than:b = Mtime.Span.compare a b > 0

let start ~timeout =
{start= Ptime_clock.now (); elapsed= Mtime_clock.counter (); timeout}

let timeout {timeout; _} = timeout

let elapsed t = Mtime_clock.count t.elapsed

let remaining t =
let elapsed = Mtime_clock.count t.elapsed in
let difference = Mtime.Span.abs_diff t.timeout elapsed in
if Mtime.Span.compare t.timeout elapsed > 0 then
Spare difference
else
Excess difference

let expired t = match remaining t with Spare _ -> false | Excess _ -> true

let deadline_of t =
Mtime.Span.to_uint64_ns t.timeout
|> Int64.to_float
|> Ptime.Span.of_float_s
|> Option.get
|> Ptime.(Span.add Ptime.(to_span t.start))
|> Ptime.Span.to_float_s

let shorten_by dur t =
let timeout =
if is_longer dur ~than:t.timeout then
Mtime.Span.zero
else
Mtime.Span.abs_diff dur t.timeout
in
{t with timeout}

let extend_by dur t =
let timeout = Mtime.Span.add dur t.timeout in
{t with timeout}

(* Conversion functions *)
let span_to_s span =
Mtime.Span.to_uint64_ns span |> Int64.to_float |> fun ns -> ns /. 1e9

let s_to_span s =
let micros_of = Float.to_int (s *. 1_000_000.) in
Mtime.Span.(micros_of * us)
59 changes: 59 additions & 0 deletions ocaml/libs/clock/timer.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
(** This module is useful for knowing that a set amount of time has passed
since a particular moment in time. For example, to know when pasta is
cooked al dente. *)
type t

type remaining = Spare of Mtime.Span.t | Excess of Mtime.Span.t

val start : timeout:Mtime.Span.t -> t
(** [start ~timeout] starts a timer that expires after [timeout] has passed.
The wait is done in monotonic time, not in POSIX time. *)

val timeout : t -> Mtime.Span.t
(** [timeout timer] returns the amount of time after which the timer expires,
from the moment it was started. *)

val expired : t -> bool
(** [expired timer] returns whether [timer] has reached the timeout it was
set to. *)

val elapsed : t -> Mtime.Span.t
(** [elapsed timer] returns the amount of time elapsed since [timer] was
started. *)

val remaining : t -> remaining
(** [remaining timer] returns the amount of spare time is left until it
expires, or the amount of excess time since it expired. *)

val deadline_of : t -> float
(** [deadline_of timer] returns the posix timestamp when the timer expires.
This is an approximation as the timer doesn't take leap seconds into
account when waiting. The use of this function is discouraged and it's
only provided for backwards-compatible reasons. *)

val shorten_by : Mtime.Span.t -> t -> t
(** [shorten_by amount timer] creates a new timer with the timeout of [timer]
shortened by [amount]. The starting time doesn't change. *)

val extend_by : Mtime.Span.t -> t -> t
(** [extend_by amount timer] creates a new timer with the timeout of [timer]
delayed by [amount]. The starting time doesn't change. *)

(** Mtime.Span helpers *)

val is_shorter : Mtime.Span.t -> than:Mtime.Span.t -> bool
(** [is_shorter dur ~than] returns whether [dur] lasts less than [than]. *)

val is_longer : Mtime.Span.t -> than:Mtime.Span.t -> bool
(** [is_longer dur ~than] returns whether [dur] lasts more than [than]. *)

val span_to_s : Mtime.Span.t -> float
(** [span_to_s span] converts a time span into seconds, represented by a float.
When the span is longer than ~54 years it becomes unprecise, avoid whenever
possible, this is unavoidable when using Thread.wait functions and related.
*)

val s_to_span : float -> Mtime.Span.t
(** [s_to_span] convert a float representing seconds to a timespan, retains
microsecond precision. Avoid whenever possible, some RPC function already
use this so it needs to be available. *)

0 comments on commit 8f49e1f

Please sign in to comment.