Skip to content

Commit fe129fe

Browse files
authored
refactor(lev): use Stdune.Time in the bindings (#14325)
Replace Lev's float-based timestamp API with Stdune.Time and Time.Span, and add stdune as a dependency of the lev library. Update the OCaml and C binding layers for loop time, sleep, periodic watchers, timers, and stat polling, and adjust the lev tests to use the new time types. Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
1 parent 83fd77c commit fe129fe

7 files changed

Lines changed: 84 additions & 65 deletions

File tree

src/lev/src/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
(library
1919
(name lev)
2020
(synopsis "libev bindings")
21-
(libraries unix)
21+
(libraries stdune unix)
2222
(foreign_archives ev)
2323
(foreign_stubs
2424
(language c)

src/lev/src/lev.ml

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
open Stdune
12
module List = ListLabels
23

34
external ev_version : unit -> int * int = "lev_version"
@@ -91,14 +92,7 @@ module Backend = struct
9192
external recommended : unit -> Set.t = "lev_backend_recommended"
9293
end
9394

94-
module Timestamp = struct
95-
type t = float
96-
97-
external sleep : t -> unit = "lev_sleep"
98-
99-
let to_float x = x
100-
let of_float x = x
101-
end
95+
external sleep : Time.Span.t -> unit = "lev_sleep"
10296

10397
module Loop = struct
10498
module Flag = struct
@@ -174,7 +168,7 @@ module Loop = struct
174168

175169
let create ?(flags = flags) () = create flags
176170

177-
external now : t -> Timestamp.t = "lev_ev_now"
171+
external now : t -> Time.t = "lev_ev_now"
178172
external destroy : t -> unit = "lev_loop_destroy"
179173
external now_update : t -> unit = "lev_loop_now_update"
180174
external run : t -> int -> bool = "lev_ev_run"
@@ -338,21 +332,21 @@ module Periodic = struct
338332

339333
type kind =
340334
| Regular of
341-
{ offset : Timestamp.t
342-
; interval : Timestamp.t option
335+
{ offset : Time.t
336+
; interval : Time.Span.t option
343337
}
344-
| Custom of (t -> now:Timestamp.t -> Timestamp.t)
338+
| Custom of (t -> now:Time.t -> Time.t)
345339

346340
external create_regular
347341
: (t -> unit -> unit)
348-
-> float
349-
-> float
342+
-> Time.t
343+
-> Time.Span.t
350344
-> t
351345
= "lev_periodic_create_regular"
352346

353347
external create_custom
354348
: (t -> unit -> unit)
355-
-> (t -> now:Timestamp.t -> Timestamp.t)
349+
-> (t -> now:Time.t -> Time.t)
356350
-> t
357351
= "lev_periodic_create_custom"
358352

@@ -363,7 +357,7 @@ module Periodic = struct
363357
| Regular { offset; interval } ->
364358
let interval =
365359
match interval with
366-
| None -> 0.
360+
| None -> Time.Span.zero
367361
| Some f -> f
368362
in
369363
create_regular f offset interval
@@ -380,11 +374,16 @@ module Timer = struct
380374
type nonrec t = t
381375
end)
382376

383-
external create : (t -> unit -> unit) -> float -> float -> t = "lev_timer_create"
377+
external create
378+
: (t -> unit -> unit)
379+
-> Time.Span.t
380+
-> Time.Span.t
381+
-> t
382+
= "lev_timer_create"
384383

385-
let create ?(repeat = 0.) ~after f = create (wrap_callback f) after repeat
384+
let create ?(repeat = Time.Span.zero) ~after f = create (wrap_callback f) after repeat
386385

387-
external remaining : t -> Loop.t -> Timestamp.t = "lev_timer_remaining"
386+
external remaining : t -> Loop.t -> Time.Span.t = "lev_timer_remaining"
388387
external stop : t -> Loop.t -> unit = "lev_timer_stop"
389388
external start : t -> Loop.t -> unit = "lev_timer_start"
390389
external again : t -> Loop.t -> unit = "lev_timer_again"
@@ -470,10 +469,13 @@ module Stat = struct
470469
external destroy : t -> unit = "lev_stat_destroy"
471470
external stop : t -> Loop.t -> unit = "lev_stat_stop"
472471
external start : t -> Loop.t -> unit = "lev_stat_start"
473-
external create : (t -> unit -> unit) -> string -> Timestamp.t -> t = "lev_stat_create"
472+
external create : (t -> unit -> unit) -> string -> Time.Span.t -> t = "lev_stat_create"
474473
external stat : t -> Unix.stats = "lev_stat_stat"
475474

476-
let create_unix ?(interval = 0.) ~path f = create (wrap_callback f) path interval
475+
let create_unix ?(interval = Time.Span.zero) ~path f =
476+
create (wrap_callback f) path interval
477+
;;
478+
477479
let create = if Sys.win32 then Error `Unimplemented else Ok create_unix
478480
end
479481

src/lev/src/lev.mli

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
open Stdune
2+
13
(** Thin wrapper around Marc Lehmann's libev library.
24
35
libev is small and performant, but this comes at a cost of a few rough edges
@@ -57,13 +59,7 @@ module Backend : sig
5759
val recommended : unit -> Set.t
5860
end
5961

60-
module Timestamp : sig
61-
type t
62-
63-
val sleep : t -> unit
64-
val of_float : float -> t
65-
val to_float : t -> float
66-
end
62+
val sleep : Time.Span.t -> unit
6763

6864
module Loop : sig
6965
module Flag : sig
@@ -95,7 +91,7 @@ module Loop : sig
9591

9692
type t
9793

98-
val now : t -> Timestamp.t
94+
val now : t -> Stdune.Time.t
9995

10096
(** The default event loop is the only one that can handle child watchers. *)
10197
val default : ?flags:Flag.Set.t -> unit -> t
@@ -160,10 +156,10 @@ module Periodic : sig
160156

161157
type kind =
162158
| Regular of
163-
{ offset : Timestamp.t
164-
; interval : Timestamp.t option
159+
{ offset : Time.t
160+
; interval : Time.Span.t option
165161
}
166-
| Custom of (t -> now:Timestamp.t -> Timestamp.t)
162+
| Custom of (t -> now:Time.t -> Time.t)
167163

168164
val create : (t -> unit) -> kind -> t
169165
end
@@ -202,8 +198,8 @@ end
202198
module Timer : sig
203199
include Watcher
204200

205-
val create : ?repeat:float -> after:float -> (t -> unit) -> t
206-
val remaining : t -> Loop.t -> Timestamp.t
201+
val create : ?repeat:Time.Span.t -> after:Time.Span.t -> (t -> unit) -> t
202+
val remaining : t -> Loop.t -> Time.Span.t
207203
val again : t -> Loop.t -> unit
208204
end
209205

@@ -214,7 +210,7 @@ module Stat : sig
214210
val stat : t -> Unix.stats
215211

216212
val create
217-
: ( ?interval:Timestamp.t -> path:string -> (t -> unit) -> t
213+
: ( ?interval:Time.Span.t -> path:string -> (t -> unit) -> t
218214
, [ `Unimplemented ] )
219215
result
220216
end

src/lev/src/lev_stubs.c

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,24 @@ DEF_BACKEND_SET(supported, ev_supported_backends)
9595
DEF_BACKEND_SET(recommended, ev_recommended_backends)
9696
DEF_BACKEND_SET(embeddable, ev_embeddable_backends)
9797

98+
static const ev_tstamp lev_ns_per_second = 1000000000.0;
99+
100+
static ev_tstamp lev_time_to_seconds(value v_time) {
101+
return ((ev_tstamp)Long_val(v_time)) / lev_ns_per_second;
102+
}
103+
104+
static ev_tstamp lev_span_to_seconds(value v_span) {
105+
return ((ev_tstamp)Long_val(v_span)) / lev_ns_per_second;
106+
}
107+
108+
static value lev_time_of_seconds(ev_tstamp seconds) {
109+
return Val_long((intnat)(seconds * lev_ns_per_second));
110+
}
111+
112+
static value lev_span_of_seconds(ev_tstamp seconds) {
113+
return Val_long((intnat)(seconds * lev_ns_per_second));
114+
}
115+
98116
#define DEF_STOP(__name) \
99117
CAMLprim value lev_##__name##_stop(value v_w, value v_ev) { \
100118
CAMLparam2(v_w, v_ev); \
@@ -277,13 +295,13 @@ CAMLprim value lev_ev_now(value v_ev) {
277295
CAMLparam1(v_ev);
278296
struct ev_loop *loop = (struct ev_loop *)Nativeint_val(v_ev);
279297
ev_tstamp now = ev_now(loop);
280-
CAMLreturn(caml_copy_double(now));
298+
CAMLreturn(lev_time_of_seconds(now));
281299
}
282300

283301
CAMLprim value lev_sleep(value v_ts) {
284302
CAMLparam1(v_ts);
285303
caml_release_runtime_system();
286-
ev_sleep(Double_val(v_ts));
304+
ev_sleep(lev_span_to_seconds(v_ts));
287305
caml_acquire_runtime_system();
288306
CAMLreturn(Val_unit);
289307
}
@@ -324,8 +342,8 @@ static ev_tstamp lev_periodic_reschedule_cb(ev_periodic *w, ev_tstamp now) {
324342
CAMLparam0();
325343
CAMLlocal1(v_stamp);
326344
struct periodic_cbs *cbs = (struct periodic_cbs *)w->data;
327-
v_stamp = caml_callback(cbs->reschedule, caml_copy_double(now));
328-
double result = Double_val(v_stamp);
345+
v_stamp = caml_callback(cbs->reschedule, lev_time_of_seconds(now));
346+
ev_tstamp result = lev_time_to_seconds(v_stamp);
329347
CAMLdrop;
330348
return result;
331349
}
@@ -393,8 +411,8 @@ CAMLprim value lev_timer_create(value v_cb, value v_after, value v_repeat) {
393411
CAMLparam3(v_cb, v_after, v_repeat);
394412
CAMLlocal2(v_timer, v_cb_applied);
395413
ev_timer *timer = caml_stat_alloc(sizeof(ev_timer));
396-
ev_timer_init(timer, Cb_for(ev_timer), Double_val(v_after),
397-
Double_val(v_repeat));
414+
ev_timer_init(timer, Cb_for(ev_timer), lev_span_to_seconds(v_after),
415+
lev_span_to_seconds(v_repeat));
398416
v_timer = caml_alloc_custom(&watcher_ops, sizeof(struct ev_timer *), 0, 1);
399417
Ev_timer_val(v_timer) = timer;
400418
v_cb_applied = caml_callback(v_cb, v_timer);
@@ -407,7 +425,7 @@ CAMLprim value lev_timer_remaining(value v_timer, value v_ev) {
407425
CAMLparam2(v_timer, v_ev);
408426
ev_timer *timer = Ev_timer_val(v_timer);
409427
struct ev_loop *ev = (struct ev_loop *)Nativeint_val(v_ev);
410-
CAMLreturn(caml_copy_double(ev_timer_remaining(ev, timer)));
428+
CAMLreturn(lev_span_of_seconds(ev_timer_remaining(ev, timer)));
411429
}
412430

413431
CAMLprim value lev_timer_again(value v_timer, value v_ev) {
@@ -423,8 +441,8 @@ CAMLprim value lev_periodic_create_regular(value v_cb, value v_offset,
423441
CAMLparam3(v_cb, v_offset, v_interval);
424442
CAMLlocal2(v_periodic, v_cb_applied);
425443
ev_periodic *periodic = caml_stat_alloc(sizeof(ev_periodic));
426-
ev_periodic_init(periodic, Cb_for(ev_periodic), Double_val(v_offset),
427-
Double_val(v_interval), NULL);
444+
ev_periodic_init(periodic, Cb_for(ev_periodic), lev_time_to_seconds(v_offset),
445+
lev_span_to_seconds(v_interval), NULL);
428446
v_periodic =
429447
caml_alloc_custom(&watcher_ops, sizeof(struct ev_periodic *), 0, 1);
430448
Ev_periodic_val(v_periodic) = periodic;
@@ -663,7 +681,7 @@ CAMLprim value lev_stat_create(value v_cb, value v_path, value v_interval) {
663681
ev_stat *w = caml_stat_alloc(sizeof(ev_stat));
664682
struct lev_stat_data *data = caml_stat_alloc(sizeof(struct lev_stat_data));
665683
const char *path = caml_stat_strdup(String_val(v_path));
666-
ev_stat_init(w, lev_stat_cb, path, Double_val(v_interval));
684+
ev_stat_init(w, lev_stat_cb, path, lev_span_to_seconds(v_interval));
667685
v_w = caml_alloc_custom(&watcher_ops, sizeof(struct ev_stat *), 0, 1);
668686
Ev_val(ev_stat, v_w) = w;
669687
v_cb_applied = caml_callback(v_cb, v_w);

src/lev/test/dune

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
(libraries
55
lev
66
unix
7+
stdune
78
;; This is because of the (implicit_transitive_deps false)
89
;; in dune-project
910
ppx_expect.config
@@ -20,6 +21,7 @@
2021
(libraries
2122
lev
2223
unix
24+
stdune
2325
;; This is because of the (implicit_transitive_deps false)
2426
;; in dune-project
2527
ppx_expect.config

src/lev/test/lev_tests.ml

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1+
open Stdune
12
open Printf
23
open Lev
34
module List = ListLabels
45

5-
let immediately = 0.00001
6-
76
let%expect_test "version" =
87
let major, minor = ev_version () in
98
printf "version (%d, %d)\n" major minor;
@@ -15,14 +14,17 @@ let%expect_test "default" =
1514
[%expect {||}]
1615
;;
1716

17+
let span_of_secs = Time.Span.of_secs
18+
let immediately = span_of_secs 0.00001
19+
1820
let%expect_test "now" =
1921
let loop = Loop.default () in
20-
let (_ : Timestamp.t) = Loop.now loop in
22+
let (_ : Time.t) = Loop.now loop in
2123
[%expect {||}]
2224
;;
2325

2426
let%expect_test "sleep" =
25-
Timestamp.sleep (Timestamp.of_float 0.1);
27+
Lev.sleep (span_of_secs 0.1);
2628
[%expect {||}]
2729
;;
2830

@@ -51,7 +53,7 @@ let%expect_test "supported backends include the active backend" =
5153
let%expect_test "timer" =
5254
let loop = Loop.create () in
5355
let timer =
54-
Timer.create ~after:0.001 (fun timer ->
56+
Timer.create ~after:(span_of_secs 0.001) (fun timer ->
5557
print_endline "fired timer";
5658
Timer.stop timer loop)
5759
in
@@ -64,9 +66,9 @@ let%expect_test "timer" =
6466

6567
let%expect_test "timer remaining is boxed correctly" =
6668
let loop = Loop.create () in
67-
let timer = Timer.create ~after:1.0 (fun _ -> ()) in
69+
let timer = Timer.create ~after:(span_of_secs 1.0) (fun _ -> ()) in
6870
Timer.start timer loop;
69-
let remaining = Timestamp.to_float (Timer.remaining timer loop) in
71+
let remaining = Time.Span.to_secs (Timer.remaining timer loop) in
7072
printf "remaining positive = %b\n" (remaining > 0.);
7173
Timer.stop timer loop;
7274
[%expect {| remaining positive = true |}]
@@ -86,7 +88,9 @@ let%expect_test "timer - not repeats" =
8688

8789
let%expect_test "timer - cancellation with stop" =
8890
let loop = Loop.create () in
89-
let timer = Timer.create ~after:6.5 (fun _ -> print_endline "fired timer") in
91+
let timer =
92+
Timer.create ~after:(span_of_secs 6.5) (fun _ -> print_endline "fired timer")
93+
in
9094
Timer.start timer loop;
9195
ignore (Lev.Loop.run loop Nowait);
9296
Timer.stop timer loop;
@@ -98,7 +102,7 @@ let%expect_test "periodic timer" =
98102
let loop = Loop.create () in
99103
let timer =
100104
let count = ref 3 in
101-
Timer.create ~after:0. ~repeat:0.02 (fun timer ->
105+
Timer.create ~after:Time.Span.zero ~repeat:(span_of_secs 0.02) (fun timer ->
102106
if !count = 0
103107
then (
104108
let () = print_endline "stopping timer" in
@@ -133,7 +137,7 @@ let%expect_test "periodic - regular" =
133137
(fun p ->
134138
print_endline "periodic fired";
135139
Periodic.stop p loop)
136-
(Regular { offset = Timestamp.of_float 0.1; interval = None })
140+
(Regular { offset = Time.add (Loop.now loop) (span_of_secs 0.1); interval = None })
137141
in
138142
Periodic.start periodic loop;
139143
Loop.run_until_done loop;
@@ -147,10 +151,7 @@ let%expect_test "periodic - custom" =
147151
(fun p ->
148152
print_endline "periodic fired";
149153
Periodic.stop p loop)
150-
(Custom
151-
(fun _ ~now ->
152-
let now = Timestamp.to_float now in
153-
Timestamp.of_float (now +. 0.2)))
154+
(Custom (fun _ ~now -> Time.add now (span_of_secs 0.2)))
154155
in
155156
Periodic.start periodic loop;
156157
Loop.run_until_done loop;
@@ -276,13 +277,13 @@ let%expect_test "timer/consecutive again" =
276277
let loop = Loop.create () in
277278
let now = Unix.time () in
278279
let timer =
279-
Timer.create ~after:1.0 (fun t ->
280+
Timer.create ~after:(span_of_secs 1.0) (fun t ->
280281
printf "timer fired after %f\n" (Unix.time () -. now);
281282
Timer.stop t loop)
282283
in
283284
let control =
284285
let count = ref 3 in
285-
Timer.create ~after:immediately ~repeat:0.2 (fun t ->
286+
Timer.create ~after:immediately ~repeat:(span_of_secs 0.2) (fun t ->
286287
if !count = 0
287288
then Timer.stop t loop
288289
else (

0 commit comments

Comments
 (0)