Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
ctypes_closure_properties.ml1 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(* * Copyright (c) 2013 Jeremy Yallop. * * This file is distributed under the terms of the MIT License. * See the file LICENSE for details. *) module type MUTEX = sig type t val create : unit -> t val lock : t -> unit val try_lock : t -> bool val unlock : t -> unit end module HashPhysical = Hashtbl.Make (struct type t = Obj.t let hash = Hashtbl.hash let equal = (==) end) module Make (Mutex : MUTEX) = struct (* Map integer identifiers to functions. *) let function_by_id : (int, Obj.t) Hashtbl.t = Hashtbl.create 10 (* Map functions (not closures) to identifiers. *) let id_by_function : int HashPhysical.t = HashPhysical.create 10 (* A single mutex guards both tables *) let tables_lock = Mutex.create () (* (The caller must hold tables_lock) *) let store_non_closure_function fn boxed_fn id = try (* Return the existing identifier, if any. *) HashPhysical.find id_by_function fn with Not_found -> (* Add entries to both tables *) HashPhysical.add id_by_function fn id; Hashtbl.add function_by_id id boxed_fn; id let fresh () = Oo.id (object end) let finalise key = (* GC can be triggered while the lock is already held, in which case we abandon the attempt and re-install the finaliser. *) let rec cleanup fn = begin if Mutex.try_lock tables_lock then begin Hashtbl.remove function_by_id key; Mutex.unlock tables_lock; end else Gc.finalise cleanup fn; end in cleanup let try_finalise f x = match Gc.finalise f x with | () -> true | exception Invalid_argument _ -> false let record closure boxed_closure : int = let key = fresh () in (* For closures we add an entry to function_by_id and a finaliser that removes the entry. *) if try_finalise (finalise key) closure then begin Mutex.lock tables_lock; Hashtbl.add function_by_id key boxed_closure; Mutex.unlock tables_lock; key end else begin (* For non-closures we add entries to function_by_id and id_by_function. *) Mutex.lock tables_lock; let id = store_non_closure_function closure boxed_closure key in Mutex.unlock tables_lock; id end let retrieve id = begin Mutex.lock tables_lock; match Hashtbl.find function_by_id id with | exception Not_found -> Mutex.unlock tables_lock; raise Not_found | f -> Mutex.unlock tables_lock; f end end