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
[@@@warning "-32-34-37-69"]
type kind = Interval | Timeout
type timer_entry = {
id : string;
kind : kind;
interval_s : float;
mutable deadline : float;
}
type state = {mutable timers : timer_entry list; mutable fired : string list}
type t = {
set_interval : id:string -> float -> unit;
set_timeout : id:string -> float -> unit;
clear : string -> unit;
drain_fired : unit -> string list;
active_ids : unit -> string list;
}
let key : t Capability.key = Capability.create ~name:"Timer"
let set v = Capability.set key v
let get () = Capability.get key
let require () = Capability.require key
let create_state () = {timers = []; fired = []}
let register (s : state) =
let cap : t =
{
set_interval =
(fun ~id interval_s ->
let clock = Clock.require () in
let now = clock.now () in
s.timers <- List.filter (fun e -> e.id <> id) s.timers ;
let entry =
{id; kind = Interval; interval_s; deadline = now +. interval_s}
in
s.timers <- entry :: s.timers);
set_timeout =
(fun ~id delay_s ->
let clock = Clock.require () in
let now = clock.now () in
s.timers <- List.filter (fun e -> e.id <> id) s.timers ;
let entry =
{
id;
kind = Timeout;
interval_s = delay_s;
deadline = now +. delay_s;
}
in
s.timers <- entry :: s.timers);
clear = (fun id -> s.timers <- List.filter (fun e -> e.id <> id) s.timers);
drain_fired =
(fun () ->
let f = s.fired in
s.fired <- [] ;
f);
active_ids = (fun () -> List.map (fun e -> e.id) s.timers);
}
in
set cap
let tick (s : state) =
let clock = Clock.require () in
let now = clock.now () in
let fired = ref [] in
let remaining = ref [] in
List.iter
(fun (entry : timer_entry) ->
if now >= entry.deadline then begin
fired := entry.id :: !fired ;
match entry.kind with
| Interval ->
let next = entry.deadline +. entry.interval_s in
let next =
if next <= now then
let missed =
Float.to_int ((now -. entry.deadline) /. entry.interval_s)
in
entry.deadline +. (float_of_int (missed + 1) *. entry.interval_s)
else next
in
entry.deadline <- next ;
remaining := entry :: !remaining
| Timeout ->
()
end
else remaining := entry :: !remaining)
s.timers ;
s.timers <- List.rev !remaining ;
s.fired <- List.rev_append !fired s.fired
let clear_all (s : state) =
s.timers <- [] ;
s.fired <- []