Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add set_uncaught_exception_handler to systhreads #10469

Merged
merged 7 commits into from
Aug 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ Working version
to emulate Unix.socketpair (only available on Windows 1803+)
(Antonin Décimo, review by David Allsopp)

- #10469: Add Thread.set_uncaught_exception_handler and
Thread.default_uncaught_exception_handler.
(Enguerrand Decorne, review by David Allsopp)

### Tools:

### Manual and documentation:
Expand Down
22 changes: 20 additions & 2 deletions otherlibs/systhreads/thread.ml
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,33 @@ external exit_stub : unit -> unit = "caml_thread_exit"

let[@inline never] check_memprof_cb () = ref ()

let default_uncaught_exception_handler = thread_uncaught_exception

let uncaught_exception_handler = ref default_uncaught_exception_handler

let set_uncaught_exception_handler fn = uncaught_exception_handler := fn

let create fn arg =
thread_new
(fun () ->
try
fn arg;
ignore (Sys.opaque_identity (check_memprof_cb ()))
with exn ->
flush stdout; flush stderr;
thread_uncaught_exception exn)
let raw_backtrace = Printexc.get_raw_backtrace () in
flush stdout; flush stderr;
try
!uncaught_exception_handler exn
with exn' ->
Printf.eprintf
"Thread %d killed on uncaught exception %s\n"
(id (self ())) (Printexc.to_string exn);
Printexc.print_raw_backtrace stderr raw_backtrace;
Printf.eprintf
"Thread %d uncaught exception handler raised %s\n"
(id (self ())) (Printexc.to_string exn');
Printexc.print_backtrace stdout;
flush stderr)

let exit () =
ignore (Sys.opaque_identity (check_memprof_cb ()));
Expand Down
13 changes: 13 additions & 0 deletions otherlibs/systhreads/thread.mli
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,16 @@ val wait_signal : int list -> int
Signal handlers attached to the signals in [sigs] will not
be invoked. The signals [sigs] are expected to be blocked before
calling [wait_signal]. *)

(** {1 Uncaught exceptions} *)

val default_uncaught_exception_handler : exn -> unit
(** [Thread.default_uncaught_exception_handler] will print the thread's id,
exception and backtrace (if available). *)

val set_uncaught_exception_handler : (exn -> unit) -> unit
(** [Thread.set_uncaught_exception_handler fn] registers [fn] as the handler
for uncaught exceptions.

If the newly set uncaught exception handler raise an exception,
{!default_uncaught_exception_handler} will be called. *)
2 changes: 1 addition & 1 deletion testsuite/tests/backtrace/callstack.reference
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ Raised by primitive operation at Callstack.f0 in file "callstack.ml", line 11, c
Called from Callstack.f1 in file "callstack.ml", line 12, characters 27-32
Called from Callstack.f2 in file "callstack.ml", line 13, characters 27-32
Called from Callstack.f3 in file "callstack.ml", line 14, characters 27-32
Called from Thread.create.(fun) in file "thread.ml", line 41, characters 8-14
Called from Thread.create.(fun) in file "thread.ml", line 47, characters 8-14
37 changes: 37 additions & 0 deletions testsuite/tests/lib-threads/uncaught_exception_handler.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
(* TEST

flags = "-g"
ocamlrunparam += ",b=1"

* hassysthreads
include systhreads
** bytecode
** native

*)

(* Testing if uncaught exception handlers are behaving properly *)

let () = Printexc.record_backtrace true

exception UncaughtHandlerExn
exception CallbackExn

let handler exn =
let id = Thread.self () |> Thread.id in
let msg = Printexc.to_string exn in
Printf.eprintf "[thread %d] caught %s\n" id msg;
Printexc.print_backtrace stderr;
flush stderr;
raise UncaughtHandlerExn

let fn () = Printexc.raise_with_backtrace
CallbackExn
(Printexc.get_raw_backtrace ())

let _ =
let th = Thread.create fn () in
Thread.join th;
Thread.set_uncaught_exception_handler handler;
let th = Thread.create fn () in
Thread.join th
12 changes: 12 additions & 0 deletions testsuite/tests/lib-threads/uncaught_exception_handler.reference
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Thread 1 killed on uncaught exception Uncaught_exception_handler.CallbackExn
Raised at Uncaught_exception_handler.fn in file "uncaught_exception_handler.ml", line 28, characters 12-113
Called from Thread.create.(fun) in file "thread.ml", line 47, characters 8-14
[thread 2] caught Uncaught_exception_handler.CallbackExn
Raised at Uncaught_exception_handler.fn in file "uncaught_exception_handler.ml", line 28, characters 12-113
Called from Thread.create.(fun) in file "thread.ml", line 47, characters 8-14
Thread 2 killed on uncaught exception Uncaught_exception_handler.CallbackExn
Raised at Uncaught_exception_handler.fn in file "uncaught_exception_handler.ml", line 28, characters 12-113
Called from Thread.create.(fun) in file "thread.ml", line 47, characters 8-14
Thread 2 uncaught exception handler raised Uncaught_exception_handler.UncaughtHandlerExn
Raised at Uncaught_exception_handler.handler in file "uncaught_exception_handler.ml", line 26, characters 2-26
Called from Thread.create.(fun) in file "thread.ml", line 53, characters 10-41