Skip to content

Commit

Permalink
Introduce the Thread.Exit exception (#10951)
Browse files Browse the repository at this point in the history
This provides an alternative to Thread.exit for terminating the current
thread.

In 5.00, Thread.exit is deprecated, and raising Thread.Exit is the standard
way to terminate threads prematurely.
  • Loading branch information
xavierleroy committed Jan 27, 2022
1 parent db915bd commit 3ff3fcc
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 13 deletions.
5 changes: 5 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ OCaml 4.14.0
WSADuplicateSocket on sockets instead of DuplicateHandle.
(Antonin Décimo, review by Xavier Leroy and Nicolás Ojeda Bär)

- #10951: Introduce the Thread.Exit exception as an alternative way to
terminate threads prematurely. This alternative way will become
the standard way in 5.00.
(Xavier Leroy, review by Florian Angeletti)

### Tools:

- #3959, #7202, #10476: ocaml, in script mode, directive errors
Expand Down
11 changes: 9 additions & 2 deletions otherlibs/systhreads/thread.ml
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,25 @@ let uncaught_exception_handler = ref default_uncaught_exception_handler

let set_uncaught_exception_handler fn = uncaught_exception_handler := fn

exception Exit

let create fn arg =
thread_new
(fun () ->
try
fn arg;
ignore (Sys.opaque_identity (check_memprof_cb ()))
with exn ->
with
| Exit ->
ignore (Sys.opaque_identity (check_memprof_cb ()))
| exn ->
let raw_backtrace = Printexc.get_raw_backtrace () in
flush stdout; flush stderr;
try
!uncaught_exception_handler exn
with exn' ->
with
| Exit -> ()
| exn' ->
Printf.eprintf
"Thread %d killed on uncaught exception %s\n"
(id (self ())) (Printexc.to_string exn);
Expand Down
16 changes: 14 additions & 2 deletions otherlibs/systhreads/thread.mli
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ val create : ('a -> 'b) -> 'a -> t
The application of [Thread.create]
returns the handle of the newly created thread.
The new thread terminates when the application [funct arg]
returns, either normally or by raising an uncaught exception.
In the latter case, the exception is printed on standard error,
returns, either normally or by raising the {!Thread.Exit} exception
or by raising any other uncaught exception.
In the last case, the uncaught exception is printed on standard error,
but not propagated back to the parent thread. Similarly, the
result of the application [funct arg] is discarded and not
directly accessible to the parent thread. *)
Expand All @@ -41,6 +42,17 @@ val id : t -> int
is an integer that identifies uniquely the thread.
It can be used to build data structures indexed by threads. *)

exception Exit
(** Exception that can be raised by user code to initiate termination
of the current thread.
Compared to calling the {!Thread.exit} function, raising the
{!Thread.Exit} exception will trigger {!Fun.finally} finalizers
and catch-all exception handlers.
It is the recommended way to terminate threads prematurely.
@since 4.14.0
*)

val exit : unit -> unit
(** Terminate prematurely the currently executing thread. *)

Expand Down
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 47, characters 8-14
Called from Thread.create.(fun) in file "thread.ml", line 49, characters 8-14
9 changes: 6 additions & 3 deletions testsuite/tests/lib-threads/uncaught_exception_handler.ml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ let () = Printexc.record_backtrace true
exception UncaughtHandlerExn
exception CallbackExn

let handler exn =
let handler final_exn 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
raise final_exn

let fn () = Printexc.raise_with_backtrace
CallbackExn
Expand All @@ -32,6 +32,9 @@ let fn () = Printexc.raise_with_backtrace
let _ =
let th = Thread.create fn () in
Thread.join th;
Thread.set_uncaught_exception_handler handler;
Thread.set_uncaught_exception_handler (handler UncaughtHandlerExn);
let th = Thread.create fn () in
Thread.join th;
Thread.set_uncaught_exception_handler (handler Thread.Exit);
let th = Thread.create fn () in
Thread.join th
13 changes: 8 additions & 5 deletions testsuite/tests/lib-threads/uncaught_exception_handler.reference
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
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
Called from Thread.create.(fun) in file "thread.ml", line 49, 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
Called from Thread.create.(fun) in file "thread.ml", line 49, 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
Called from Thread.create.(fun) in file "thread.ml", line 49, 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
Raised at Uncaught_exception_handler.handler in file "uncaught_exception_handler.ml", line 26, characters 2-17
Called from Thread.create.(fun) in file "thread.ml", line 58, characters 10-41
[thread 3] 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 49, characters 8-14

0 comments on commit 3ff3fcc

Please sign in to comment.