Source file eliom_notif.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# 1 "src/lib/eliom_notif.server.ml"
open Lwt
module type S = sig
type identity
type key
type server_notif
type client_notif
val init : unit -> unit Lwt.t
val deinit : unit -> unit
val listen : key -> unit
val unlisten : key -> unit
module Ext : sig
val unlisten
: ?sitedata:Eliom_common.sitedata
-> ([< `Client_process], [< `Data]) Eliom_state.Ext.state
-> key
-> unit
end
val notify : ?notfor:[`Me | `Id of identity] -> key -> server_notif -> unit
val client_ev : unit -> (key * client_notif) Eliom_react.Down.t
val clean : unit -> unit
end
module type ARG = sig
type identity
type key
type server_notif
type client_notif
val prepare : identity -> server_notif -> client_notif option Lwt.t
val equal_key : key -> key -> bool
val equal_identity : identity -> identity -> bool
val get_identity : unit -> identity Lwt.t
val max_resource : int
val max_identity_per_resource : int
end
module Make (A : ARG) :
S
with type identity = A.identity
and type key = A.key
and type server_notif = A.server_notif
and type client_notif = A.client_notif = struct
type key = A.key
type identity = A.identity
type server_notif = A.server_notif
type client_notif = A.client_notif
type notification_data = A.key * A.client_notif
type notification_react =
notification_data Eliom_react.Down.t
* (?step:React.step -> notification_data -> unit)
module Notif_hashtbl = Hashtbl.Make (struct
type t = A.key
let equal = A.equal_key
let hash = Hashtbl.hash
end)
module Weak_tbl = Weak.Make (struct
type t = (A.identity * notification_react) option
let equal a b =
match a, b with
| None, None -> true
| Some (a, b), Some (c, d) -> A.equal_identity a c && b == d
| _ -> false
let hash = Hashtbl.hash
end)
module I = struct
let tbl = Notif_hashtbl.create A.max_resource
let remove_if_empty wt key =
if Weak_tbl.count wt = 0 then Notif_hashtbl.remove tbl key
let remove v key =
try
let wt = Notif_hashtbl.find tbl key in
Weak_tbl.remove wt v; remove_if_empty wt key
with Not_found -> ()
let add v key =
let wt =
try Notif_hashtbl.find tbl key
with Not_found ->
let wt = Weak_tbl.create A.max_identity_per_resource in
Notif_hashtbl.add tbl key wt;
wt
in
if not (Weak_tbl.mem wt v) then Weak_tbl.add wt v
let iter =
let iter (f : Weak_tbl.data -> unit Lwt.t) wt : unit =
Weak_tbl.iter (fun data -> Lwt.async (fun () -> f data)) wt
in
fun f key ->
try
let wt = Notif_hashtbl.find tbl key in
let g data =
match data with
| None ->
Weak_tbl.remove wt data; remove_if_empty wt key; Lwt.return_unit
| Some v -> f v
in
iter g wt
with Not_found -> ()
end
let identity_r
: (A.identity * notification_react) option Eliom_reference.Volatile.eref
=
Eliom_reference.Volatile.eref ~scope:Eliom_common.default_process_scope None
let notif_e : notification_react Eliom_reference.Volatile.eref =
Eliom_reference.Volatile.eref_from_fun
~scope:Eliom_common.default_process_scope (fun () ->
let e, send_e = React.E.create () in
let client_ev =
Eliom_react.Down.of_react
~size:100 ~scope:Eliom_common.default_process_scope e
in
client_ev, send_e)
let set_identity identity =
let notif_e = Eliom_reference.Volatile.get notif_e in
Eliom_reference.Volatile.set identity_r (Some (identity, notif_e))
let set_current_identity () =
A.get_identity () >>= fun identity -> set_identity identity; Lwt.return_unit
let init : unit -> unit Lwt.t = fun () -> set_current_identity ()
let deinit () = Eliom_reference.Volatile.set identity_r None
let listen (key : A.key) =
let identity = Eliom_reference.Volatile.get identity_r in
I.add identity key
let unlisten (id : A.key) =
let identity = Eliom_reference.Volatile.get identity_r in
I.remove identity id
module Ext = struct
let unlisten ?sitedata:_ state (key : A.key) =
let uc = Eliom_reference.Volatile.Ext.get state identity_r in
I.remove uc key
end
let notify ?notfor key content =
let f (identity, ((_, send_e) as notif)) =
let blocked =
match notfor with
| Some `Me ->
let notif_e = Eliom_reference.Volatile.get notif_e in
notif == notif_e
| Some (`Id id) -> identity = id
| None -> false
in
if blocked
then Lwt.return_unit
else
A.prepare identity content >>= fun content ->
match content with
| Some content ->
send_e (key, content);
Lwt.return_unit
| None -> Lwt.return_unit
in
I.iter f key
let client_ev () =
let ev, _ = Eliom_reference.Volatile.get notif_e in
ev
let clean () =
let f key weak_tbl =
if Weak_tbl.count weak_tbl = 0 then Notif_hashtbl.remove I.tbl key
in
Notif_hashtbl.iter f I.tbl
end
module type ARG_SIMPLE = sig
type identity
type key
type notification
val get_identity : unit -> identity Lwt.t
end
module Make_Simple (A : ARG_SIMPLE) = Make (struct
type identity = A.identity
type key = A.key
type server_notif = A.notification
type client_notif = A.notification
let prepare _ n = Lwt.return_some n
let equal_key = ( = )
let equal_identity = ( = )
let get_identity = A.get_identity
let max_resource = 1000
let max_identity_per_resource = 10
end)