@@ -27,12 +27,6 @@ module Mutex = struct
2727 r
2828end
2929
30- let rec split_c c str =
31- try
32- let i = String. index str c in
33- String. sub str 0 i :: (split_c c (String. sub str (i+ 1 ) (String. length str - i - 1 )))
34- with Not_found -> [str]
35-
3630type frame = {
3731 process : string ;
3832 filename : string ;
@@ -52,7 +46,9 @@ let to_string_hum xs =
5246 Buffer. add_string results (Printf. sprintf " %d/%d %s %s file %s, line %d" i xs' x.process (if first_line then " Raised at" else " Called from" ) x.filename x.line);
5347 Buffer. add_string results " \n " ;
5448 loop false (i + 1 ) xs in
55- loop true 1 xs
49+ match xs with
50+ | [] -> Printf. sprintf " %s: Thread %d has no backtrace table" ! my_name Thread. (id (self () ))
51+ | _ -> loop true 1 xs
5652
5753type table = {
5854 backtraces : t array ;
@@ -68,11 +64,11 @@ let max_backtraces = 100
6864
6965let frame_of_string process x =
7066 try
71- begin match split_c '"' x with
67+ begin match String. split_on_char '"' x with
7268 | [ _; filename; rest ] ->
73- begin match split_c ',' rest with
69+ begin match String. split_on_char ',' rest with
7470 | [ _; line_n; _ ] ->
75- begin match split_c ' ' line_n with
71+ begin match String. split_on_char ' ' line_n with
7672 | _ :: _ :: n :: _ ->
7773 { process; filename; line = int_of_string n }
7874 | _ ->
@@ -88,7 +84,7 @@ let frame_of_string process x =
8884
8985let get_backtrace_401 () =
9086 Printexc. get_backtrace ()
91- |> split_c '\n'
87+ |> String. split_on_char '\n'
9288 |> List. filter (fun x -> x <> " " )
9389 |> List. map (frame_of_string ! my_name)
9490
@@ -147,65 +143,110 @@ let remove t exn =
147143 bt :: acc
148144 ) [] |> remove_dups |> List. concat
149145
150- let per_thread_backtraces = Hashtbl. create 37
151- let per_thread_backtraces_m = Mutex. create ()
152146
153- let with_lock f x =
154- Mutex. lock per_thread_backtraces_m;
155- try
156- let result = f x in
157- Mutex. unlock per_thread_backtraces_m;
158- result
159- with e ->
160- Mutex. unlock per_thread_backtraces_m;
161- raise e
162-
163- let with_backtraces f =
164- let id = Thread. (id (self () )) in
165- let tbl = with_lock
166- (fun () ->
167- let tbl =
168- if Hashtbl. mem per_thread_backtraces id
169- then Hashtbl. find per_thread_backtraces id
170- else make () in
171- (* If we nest these functions we add multiple bindings
172- to the same mutable table which is ok *)
173- Hashtbl. add per_thread_backtraces id tbl;
174- tbl
175- ) () in
176- try
177- let result = f () in
178- with_lock (Hashtbl. remove per_thread_backtraces) id;
179- `Ok result
180- with e ->
181- let bt = get tbl e in
182- with_lock (Hashtbl. remove per_thread_backtraces) id;
183- `Error (e, bt)
147+ module IntMap = Map. Make (Int )
148+
149+ module ThreadLocalTable = struct
150+ (* The map values behave like stacks here, with shadowing as in Hashtbl.
151+ A Hashtbl is not used here, in order to avoid taking the lock in `find`. *)
152+ type 'a t = {mutable tbl : 'a list IntMap .t ; m : Mutex .t }
153+
154+ let make () =
155+ let tbl = IntMap. empty in
156+ let m = Mutex. create () in
157+ {tbl; m}
158+
159+ let add t v =
160+ let id = Thread. (id (self () )) in
161+ Mutex. execute t.m (fun () ->
162+ t.tbl < -
163+ IntMap. update id
164+ (function Some v' -> Some (v :: v') | None -> Some [v])
165+ t.tbl
166+ )
167+
168+ let remove t =
169+ let id = Thread. (id (self () )) in
170+ Mutex. execute t.m (fun () ->
171+ t.tbl < -
172+ IntMap. update id
173+ (function
174+ | Some [_] ->
175+ None
176+ | Some (_hd :: tl ) ->
177+ Some tl
178+ | Some [] | None ->
179+ None
180+ )
181+ t.tbl
182+ )
183+
184+ let find t =
185+ let id = Thread. (id (self () )) in
186+ IntMap. find_opt id t.tbl
187+ |> Option. fold ~none: None ~some: (function v :: _ -> Some v | [] -> None )
188+ end
189+
190+ let per_thread_backtraces = ThreadLocalTable. make ()
191+
192+ let ( let@ ) f x = f x
184193
185- let with_table f default =
186- let id = Thread. (id (self () )) in
187- match with_lock (fun () ->
188- if Hashtbl. mem per_thread_backtraces id
189- then Some (Hashtbl. find per_thread_backtraces id)
190- else None
191- ) () with
192- | None -> default ()
193- | Some tbl -> f tbl
194+ let try_result f = try Ok (f () ) with exn -> Error exn
195+
196+ let with_backtraces_common f with_table =
197+ let tbl =
198+ let tbl =
199+ match ThreadLocalTable. find per_thread_backtraces with
200+ | Some tbl -> tbl
201+ | None -> make ()
202+ in
203+ (* If we nest these functions we add multiple bindings
204+ to the same mutable table which is ok *)
205+ ThreadLocalTable. add per_thread_backtraces tbl;
206+ tbl
207+ in
208+ let finally () = ThreadLocalTable. remove per_thread_backtraces in
209+ Fun. protect ~finally (fun () -> with_table tbl (try_result f))
210+
211+ module V1 = struct
212+ let with_backtraces f =
213+ let with_table tbl = function
214+ | Ok ok -> `Ok ok
215+ | Error e -> `Error (e, get tbl e)
216+ in
217+ with_backtraces_common f with_table
218+ end
219+
220+ module V2 = struct
221+ let with_backtraces ~finally f =
222+ let with_table tbl result =
223+ result
224+ |> Result. map_error (function e -> (e, get tbl e))
225+ |> finally
226+ in
227+ with_backtraces_common f with_table
228+ end
194229
195- let is_important exn = with_table ( fun tbl -> is_important tbl exn ) ( fun () -> () )
230+ let with_backtraces = V1. with_backtraces
196231
197- let add exn bt = with_table (fun tbl -> add tbl exn bt) (fun () -> () )
232+ let is_important exn =
233+ ThreadLocalTable. find per_thread_backtraces
234+ |> Option. iter (fun tbl -> is_important tbl exn )
198235
199- let warning () =
200- [ { process = ! my_name;
201- filename = Printf. sprintf " (Thread %d has no backtrace table. Was with_backtraces called?" Thread. (id (self () ));
202- line = 0 } ]
236+ let add exn bt =
237+ ThreadLocalTable. find per_thread_backtraces
238+ |> Option. iter (fun tbl -> add tbl exn bt)
203239
204- let remove exn = with_table (fun tbl -> remove tbl exn ) warning
240+ let remove exn =
241+ ThreadLocalTable. find per_thread_backtraces
242+ |> Option. fold ~some: (fun tbl -> remove tbl exn ) ~none: empty
205243
206- let get exn = with_table (fun tbl -> get tbl exn ) warning
244+ let get exn =
245+ ThreadLocalTable. find per_thread_backtraces
246+ |> Option. fold ~some: (fun tbl -> get tbl exn ) ~none: empty
207247
208248let reraise old newexn =
249+ is_important old;
209250 add newexn (remove old);
210251 raise newexn
211252
0 commit comments