package devkit
Development kit - general purpose library
Install
Dune Dependency
Authors
Maintainers
Sources
devkit-1.20240429.tbz
sha256=222f8ac131b1d970dab7eeb2714bfd6b9338b88b1082e6e01c136ae19e7eaef4
sha512=c9e6d93e3d21e5530c0f4d5baca51bf1f0a5d19248f8af7678d0665bb5cdf295d7aaaaa3e50eb2e44b8720e55097cc675af4dc8ec45acf9da39feb3eae1405d5
doc/src/devkit.core/lwt_mark.ml.html
Source file lwt_mark.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
open ExtLib open Prelude let last_logs_max = 10 let enabled = ref false let is_enabled () = !enabled (**) module LastN = struct type 'a t = { queue : 'a Queue.t; mutable avail : int; } let create n = if n < 0 then invalid_arg "LastN.create: n < 0" else { queue = Queue.create (); avail = n } let add x t = Queue.push x t.queue; if t.avail = 0 then ignore (Queue.pop t.queue) else t.avail <- t.avail - 1 let to_list t = List.rev @@ Queue.fold (fun acc x -> x :: acc) [] t.queue end (**) type id = int type kind = | Normal | Background | Status type lazy_string = string Lazy.t type mark = { id : id; kind : kind; name : lazy_string; parent_name : lazy_string; parent_id : id; (** [id] is stored to find parent thread in !marks, but there are no direct links to parent's mark. [parent_{name,id}] don't reflect Lwt scheduling (so background thread's parent is not set to main/unnamed/toplevel); they are used to trace places where threads were born (control flow). *) logs : lazy_string LastN.t; } (**) let string_of_kind = function | Normal -> "normal" | Background -> "background" | Status -> "status" (** [0] is a special value, not used by threads. *) let next_mark_id = ref 1 let marks : (int, mark) Hashtbl.t = Hashtbl.create 7 let create ~name ~parent_id ~parent_name ~kind = { id = (let id = !next_mark_id in next_mark_id := id + 1; id); name; parent_id; parent_name; logs = LastN.create last_logs_max; kind; } let register_mark m = match Hashtbl.find marks m.id with | exception Not_found -> Hashtbl.add marks m.id m | _ -> assert false let unregister_mark m = match Hashtbl.find marks m.id with | _ -> Hashtbl.remove marks m.id | exception Not_found -> assert false let special name = let m = create ~name:(Lazy.from_val name) ~parent_id:0 ~parent_name:(Lazy.from_val "") ~kind:Normal in register_mark m; m (** dummy parent of threads created by parents without mark *) let top_mark = special "<top>" (** dummy parent of threads/statuses which parent has terminated *) let orphan_mark = special "<orphan>" (**) let log_add_line mark msg = let msg = lazy begin let msg = !!msg in if Stre.ends_with msg "\n" then msg else msg ^ "\n" end in LastN.add msg mark.logs let log_to mark msg = if not !enabled then () else log_add_line mark msg let key = Lwt.new_key () let with_mark v f = Lwt.with_value key v f let run_thread on_success on_failure func = match func () with | thr -> Lwt.on_any thr on_success on_failure; thr | exception exn -> on_failure exn; Lwt.fail exn let mark_or_orphan id = try Hashtbl.find marks id with Not_found -> orphan_mark let log_exit mark msg = let parent = mark_or_orphan mark.parent_id in log_to parent begin let {name; id; kind; parent_name; parent_id; logs = _} = mark in lazy begin Printf.sprintf "thread %S (#%i, %s%s) exit %s\n" !!name id (string_of_kind kind) (if parent == orphan_mark then Printf.sprintf ", parent was %s#%i" !!parent_name parent_id else "") !!msg end end (** separate function to ease reasoning about which values are kept in closures (here: only arguments and top-level values, no local bindings from [with_new_mark]) *) let run_with_mark ?dump ?log:(log : Log.logger option) ~mark cont () = register_mark mark; let on_success v = unregister_mark mark; log_exit mark @@ lazy begin "ok" ^ (match dump with None -> "" | Some dump -> ", res: " ^ dump v) end; in let on_failure exn = unregister_mark mark; log_exit mark @@ lazy begin "exn: " ^ Printexc.to_string exn end; begin match log with None -> () | Some log -> log #warn "thread %S failed" !!(mark.name) ~exn end; in run_thread on_success on_failure cont let with_new_mark ?dump ?log ~name ~kind cont = if not !enabled then cont () else let new_mark = let (parent_name, parent_id) = let parent = Option.default top_mark (Lwt.get key) in (parent.name, parent.id) in create ~name ~kind ~parent_name ~parent_id in with_mark (Some new_mark) @@ run_with_mark ?dump ?log ~mark:new_mark cont (**) let name name cont = with_new_mark ~name:(Lazy.from_val name) ~kind:Normal cont let status name ?dump cont = with_new_mark ~name ?dump ~kind:Status cont let status_s name ?dump cont = status (Lazy.from_val name) ?dump cont let async ?log name run_thread = Lwt.async @@ fun () -> with_new_mark ?log ~name:(Lazy.from_val name) ~kind:Background @@ run_thread let log_do msg = let mark = Option.default top_mark (Lwt.get key) in log_add_line mark msg let log_l msg = if not !enabled then () else log_do msg let log_do_strict msg = log_do (Lazy.from_val msg) let log msg = if not !enabled then () else log_do_strict msg let log_f fmt = if not !enabled then Printf.ikfprintf ignore () fmt else Printf.ksprintf log_do_strict fmt (**) let rec parent_of_status parent_id = let parent = mark_or_orphan parent_id in match parent.kind with | Normal | Background -> parent | Status -> parent_of_status parent.parent_id let summary () = let b = Buffer.create 100 in let open Printf in Buffer.add_string b "Lwt_mark status (running threads):\n"; if !enabled then begin let statuses = Hashtbl.create 7 in Hashtbl.iter begin fun _id mark -> match mark.kind with | Normal | Background -> () | Status -> begin let {id = parent_id; _} = parent_of_status mark.parent_id in let sts = try Hashtbl.find statuses parent_id with Not_found -> let s = ref [] in (Hashtbl.add statuses parent_id s; s) in tuck sts mark end end marks; Hashtbl.iter begin fun _id {id; name; parent_id; parent_name; logs; kind} -> bprintf b "%s (#%i, %s%s)\n" !!name id (string_of_kind kind) (if parent_id = 0 then "" else sprintf ", parent: %s#%i" !!parent_name parent_id); let logs = LastN.to_list logs in List.iter (fun line -> Buffer.add_string b " L "; Buffer.add_string b !!line) logs; begin match kind with | Status -> () | Normal | Background -> let sts = match Hashtbl.find statuses id with | sts_acc -> List.rev !sts_acc | exception Not_found -> [] in List.iter (fun status -> bprintf b " S %s#%i\n" !!(status.name) status.id) sts end; Buffer.add_char b '\n' end marks end else bprintf b "<not initialized>\n"; Buffer.contents b (**) let init () = enabled := true; let old_hook = !Log.State.hook in Log.State.hook := fun level facil msg -> (log msg; old_hook level facil msg)
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>