package ocsigenserver
A full-featured and extensible Web server
Install
Dune Dependency
Authors
Maintainers
Sources
6.0.0.tar.gz
md5=001e22ec2da3ab08840f934a8f005859
sha512=59f36fdf0a640117aa562d1d9ef96b7146843d9b72d71d01366640521405550074e03267fb388c5e685542781fc4bce763818a36cf05c0e033fae5e51c2f1496
doc/src/ocsigenserver.baselib/ocsigen_cache.ml.html
Source file ocsigen_cache.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 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
(* Ocsigen * Copyright (C) 2009 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** Cache. @author Vincent Balat @author Raphaël Proust (adding timers) *) let ( >>= ) = Lwt.bind module Dlist : sig type 'a t type 'a node val create : ?timer:float -> int -> 'a t val add : 'a -> 'a t -> 'a option val newest : 'a t -> 'a node option val oldest : 'a t -> 'a node option val remove : 'a node -> unit (** Removes an element from its list. If the element is not in a list, it does nothing. If it is in a list, it calls the finaliser, then removes the element. If the finaliser fails with an exception, the element is removed and the exception is raised again. *) val up : 'a node -> unit (** Removes the element from its list without finalising, then adds it as newest. *) val size : 'a t -> int val maxsize : 'a t -> int val get_timer : 'a t -> float option (** returns the timer of the Dlist *) val value : 'a node -> 'a val list_of : 'a node -> 'a t option val remove_n_oldest : 'a t -> int -> 'a list (** remove the n oldest values ; returns the list of removed values *) val fold : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b (** fold over the elements from the cache starting from the newest to the oldest *) val fold_back : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b (** fold over the elements from the cache starting from the oldest to the newest *) val lwt_fold : ('b -> 'a -> 'b Lwt.t) -> 'b -> 'a t -> 'b Lwt.t (** lwt version of fold *) val lwt_fold_back : ('b -> 'a -> 'b Lwt.t) -> 'b -> 'a t -> 'b Lwt.t (** lwt version of fold_back *) val move : 'a node -> 'a t -> 'a option (** Move a node from one dlist to another one, without finalizing. If one value is removed from the destination list (because its maximum size is reached), it is returned (after finalisation). *) val set_maxsize : 'a t -> int -> 'a list (** change the maximum size ; returns the list of removed values, if any. *) val add_finaliser_before : ('a node -> unit) -> 'a t -> unit (** record a function to be called automatically on a piece of data just before it disappears from the list (either by explicit removal or because the maximum size is exceeded) *) val set_finaliser_before : ('a node -> unit) -> 'a t -> unit (** replace all finalizers by a new one. Be very careful while using this. *) val get_finaliser_before : 'a t -> 'a node -> unit (** returns the finalizers. *) val add_finaliser_after : ('a node -> unit) -> 'a t -> unit (** record a function to be called automatically on a piece of data just after it disappears from the list (either by explicit removal or because the maximum size is exceeded) *) val set_finaliser_after : ('a node -> unit) -> 'a t -> unit (** replace all finalizers by a new one. Be very careful while using this. *) val get_finaliser_after : 'a t -> 'a node -> unit (** returns the finalizers. *) end = struct type 'a node = { mutable value : 'a ; mutable succ : 'a node option ; (* the node added just after *) mutable prev : 'a node option ; (* the node added just before *) mutable mylist : 'a t option ; (* the list to which it belongs *) mutable collection : float option (* the timestamp for removal *) } (* Doubly-linked list with maximum size. The field [oldest] is the first element that must be removed if the list becomes too long. *) and 'a t = { mutable newest : 'a node option (* None = empty *) ; mutable oldest : 'a node option ; mutable size : int ; mutable maxsize : int ; mutable finaliser_before : 'a node -> unit ; mutable finaliser_after : 'a node -> unit ; (* *) time_bound : time_bound option } and time_bound = {(* *) timer : float; mutable collector : unit Lwt.t option} (* Checks (by BY): let compute_length c = let rec aux i = function | Some {prev=p} -> aux (i + 1) p | None -> i in aux 0 c.newest let correct_node n = (match n.succ with | None -> true | Some n' -> n'.prev == Some n) && (match n.prev with | None -> true | Some n' -> n'.succ == Some n) (* Check that a list is correct. To be completed 1. by adding a check on nodes, 2. by verifying that newest can be reached from oldest and respectively *) let correct_list l = (l.size <= l.maxsize) && (compute_length l = l.size) && (match l.oldest with | None -> true | Some n -> n.prev = None) && (match l.newest with | None -> true | Some n -> n.succ = None) *) let create ?timer size = { newest = None ; oldest = None ; size = 0 ; maxsize = size ; finaliser_before = (fun _ -> ()) ; finaliser_after = (fun _ -> ()) ; time_bound = (match timer with | None -> None | Some t -> Some {timer = t; collector = None}) } (* Remove an element from its list - don't finalise *) let remove' node l = (* assertion (node.mylist = Some l' with l' == l); *) let oldest = match l.oldest with Some n when node == n -> node.succ | _ -> l.oldest in let newest = match l.newest with Some n when node == n -> node.prev | _ -> l.newest in (match node.succ with None -> () | Some s -> s.prev <- node.prev); (match node.prev with None -> () | Some s -> s.succ <- node.succ); l.oldest <- oldest; l.newest <- newest; node.mylist <- None; l.size <- l.size - 1 (* Remove an element from its list - and finalise *) let remove node = match node.mylist with | None -> () | Some l as a -> (try l.finaliser_before node; assert (node.mylist == a); remove' node l with e -> remove' node l; raise e); l.finaliser_after node (* These next functions are for the collecting thread *) (* computing the timestamp for a node *) let collect_timer = function | {time_bound = Some {timer = t; _}; _} -> Some (t +. Unix.gettimeofday ()) | {time_bound = None; _} -> None (* do collect. We first check if the node is still in the list and then if * its collection hasn't been rescheduled ! *) let collect dl n = match n.mylist with | Some l when l == dl -> ( match n.collection with | None -> assert false | Some c -> if c < Unix.gettimeofday () then remove n else ()) | None | Some _ -> () let sleep_until = function (*/!\ COOPERATES*) | None -> assert false (* collection is set to None and collector to Some *) | Some t -> let duration = t -. Unix.gettimeofday () in if duration <= 0. then Lwt.return () else Lwt_unix.sleep duration (* a function to set the collector. *) let rec update_collector r = match r.time_bound with | None (* Not time bounded dlist *) | Some {collector = Some _; _} -> () (* Already collecting *) | Some ({collector = None; _} as t) -> ( match r.oldest with | None -> () (* Empty dlist *) | Some n -> t.collector <- Some ( sleep_until n.collection >>= fun () -> collect r n; t.collector <- None; update_collector r; Lwt.return () )) (* Add a node that do not belong to any list to a list. The fields [succ] and [prev] are overridden. If the list is too long, the function returns the oldest value. The node added becomes the element [list] of the list *) (* do not finalise *) (* not exported *) let add_node node r = assert (node.mylist = None); node.mylist <- Some r; let res = match r.newest with | None -> node.succ <- None; node.prev <- None; r.newest <- Some node; r.oldest <- r.newest; r.size <- 1; None | Some rl -> node.succ <- None; node.prev <- r.newest; rl.succ <- Some node; r.newest <- Some node; r.size <- r.size + 1; if r.size > r.maxsize then r.oldest else None in node.collection <- collect_timer r; update_collector r; res let add x l = let create_one a = {value = a; succ = None; prev = None; mylist = None; collection = None} in (* create_one not exported *) match add_node (create_one x) l with | None -> None | Some v -> remove v; Some v.value let newest a = a.newest let oldest a = a.oldest let size c = c.size let maxsize c = c.maxsize let get_timer c = match c.time_bound with None -> None | Some tb -> Some tb.timer let value n = n.value let list_of n = n.mylist let up node = match node.mylist with | None -> () | Some l -> ( match l.newest with | Some n when node == n -> () | _ -> remove' node l; ignore (add_node node l)) (* assertion: = None *) (* we must not change the physical address => use add_node *) let rec remove_n_oldest l n = (* remove the n oldest values (or less if the list is not long enough) ; returns the list of removed values *) if n <= 0 then [] else match l.oldest with | None -> [] | Some node -> let v = node.value in remove node; (* and finalise! *) v :: remove_n_oldest l (n - 1) (* Move a node from one dlist to another one, without finalizing *) let move node l = (match node.mylist with None -> () | Some l -> remove' node l); match add_node node l with None -> None | Some v -> remove v; Some v.value (* fold over the elements from the newest to the oldest *) let lwt_fold f accu {newest; _} = match newest with | None -> Lwt.return accu | Some newest -> let rec fold accu node = f accu node.value >>= fun accu -> match node.prev with | None -> Lwt.return accu | Some new_node when new_node == newest -> Lwt.return accu | Some new_node -> fold accu new_node in fold accu newest (* fold over the elements from the oldest to the newest *) let lwt_fold_back f accu {oldest; _} = match oldest with | None -> Lwt.return accu | Some oldest -> let rec fold accu node = f accu node.value >>= fun accu -> match node.succ with | None -> Lwt.return accu | Some new_node when new_node == oldest -> Lwt.return accu | Some new_node -> fold accu new_node in fold accu oldest (* fold over the elements from the newest to the oldest *) let fold f accu {newest; _} = match newest with | None -> accu | Some newest -> let rec fold accu node = let accu = f accu node.value in match node.prev with | None -> accu | Some new_node when new_node == newest -> accu | Some new_node -> fold accu new_node in fold accu newest (* fold over the elements from the oldest to the newest *) let fold_back f accu {oldest; _} = match oldest with | None -> accu | Some oldest -> let rec fold accu node = let accu = f accu node.value in match node.succ with | None -> accu | Some new_node when new_node == oldest -> accu | Some new_node -> fold accu new_node in fold accu oldest let set_maxsize l m = let size = l.size in if m >= size then ( l.maxsize <- m; []) else if m <= 0 then failwith "Dlist.set_maxsize" else let ll = remove_n_oldest l (size - m) in l.maxsize <- m; ll let set_finaliser_before f l = l.finaliser_before <- f let get_finaliser_before l = l.finaliser_before let add_finaliser_before f l = let oldf = l.finaliser_before in l.finaliser_before <- (fun n -> oldf n; f n) let set_finaliser_after f l = l.finaliser_after <- f let get_finaliser_after l = l.finaliser_after let add_finaliser_after f l = let oldf = l.finaliser_after in l.finaliser_after <- (fun n -> oldf n; f n) end module Weak = Weak.Make (struct type t = unit -> unit let hash = Hashtbl.hash let equal = ( == ) end) let clear_all = Weak.create 17 module Make = functor (A : sig type key type value end) -> struct module H = Hashtbl.Make (struct type t = A.key let equal a a' = a = a' let hash = Hashtbl.hash end) type t = { mutable pointers : A.key Dlist.t ; mutable table : (A.value * A.key Dlist.node) H.t ; finder : A.key -> A.value Lwt.t ; clear : unit -> unit (* This function clears the cache. It is put inside the cache structure so that it is garbage-collected only when the cache is no longer referenced, as the functions themselves are put inside a weak hash table *) } let mk ?timer size = let ((l, t) as a) = Dlist.create ?timer size, H.create size in Dlist.set_finaliser_after (fun n -> H.remove t (Dlist.value n)) l; a let rec create f ?timer size = let rec cache = let l, t = mk ?timer size in {pointers = l; table = t; finder = f; clear = f_clear} and f_clear () = clear cache in Weak.add clear_all f_clear; cache and clear cache = let size = Dlist.maxsize cache.pointers in let timer = Dlist.get_timer cache.pointers in let l, t = mk ?timer size in cache.pointers <- l; cache.table <- t (* not exported *) let poke cache node = assert ( match Dlist.list_of node with | None -> false | Some l -> cache.pointers == l); Dlist.up node let find_in_cache cache k = let v, node = H.find cache.table k in poke cache node; v let remove cache k = try let _v, node = H.find cache.table k in assert ( match Dlist.list_of node with | None -> false | Some l -> cache.pointers == l); Dlist.remove node with Not_found -> () (* Add in a cache, under the hypothesis that the value is not already in the cache *) let add_no_remove cache k v = ignore (Dlist.add k cache.pointers); match Dlist.newest cache.pointers with | None -> assert false | Some n -> H.add cache.table k (v, n) let add cache k v = remove cache k; add_no_remove cache k v let size c = Dlist.size c.pointers let find cache k = try Lwt.return (find_in_cache cache k) with Not_found -> cache.finder k >>= fun r -> (try (* it may have been added during cache.finder *) ignore (find_in_cache cache k) with Not_found -> add_no_remove cache k r); Lwt.return r class cache f ?timer size_c = let c = create f ?timer size_c in object method clear () = clear c method find = find c method add = add c method size = size c method find_in_cache = find_in_cache c method remove = remove c end end let clear_all_caches () = Weak.iter (fun f -> f ()) clear_all
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>