Skip to content

Commit 5928b4b

Browse files
Make exception filtering optional
1 parent d3c9698 commit 5928b4b

17 files changed

+106
-47
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
====== Additions ======
2020

21+
* Lwt.set_exception_filter for enabling/disabling system-exception catching (#964)
2122
* Lwt.reraise an exception raising function which preserves backtraces, recommended for use in Lwt.catch (#963)
2223
* Expose Lwt_io.delete_recursively for deleting a directory and its content recursively. (#984, Antonin Décimo)
2324

src/core/lwt.ml

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -710,12 +710,18 @@ struct
710710
end
711711
open Basic_helpers
712712

713-
714-
(* Small helper function to avoid catching ocaml-runtime exceptions *)
715-
let is_not_ocaml_runtime_exception = function
713+
(* Small helpers to avoid catching ocaml-runtime exceptions *)
714+
type exception_filter = exn -> bool
715+
let catch_all_filter = fun _ -> true
716+
let catch_not_runtime_filter = function
716717
| Out_of_memory -> false
717718
| Stack_overflow -> false
718719
| _ -> true
720+
let exception_filter =
721+
(* Default value: the legacy behaviour to avoid breaking programs *)
722+
ref catch_all_filter
723+
let set_exception_filter f = exception_filter := f
724+
let filter_exception e = !exception_filter e
719725

720726
module Sequence_associated_storage :
721727
sig
@@ -796,7 +802,7 @@ struct
796802
let result = f () in
797803
current_storage := saved_storage;
798804
result
799-
with exn when is_not_ocaml_runtime_exception exn ->
805+
with exn when !exception_filter exn ->
800806
current_storage := saved_storage;
801807
raise exn
802808
end
@@ -1134,7 +1140,7 @@ struct
11341140
be reject later, it is not the responsibility of this function to pass
11351141
the exception to [!async_exception_hook]. *)
11361142
try f v
1137-
with exn when is_not_ocaml_runtime_exception exn ->
1143+
with exn when !exception_filter exn ->
11381144
!async_exception_hook exn
11391145

11401146

@@ -1834,7 +1840,7 @@ struct
18341840

18351841
let p' =
18361842
try f v with exn
1837-
when is_not_ocaml_runtime_exception exn -> fail exn
1843+
when !exception_filter exn -> fail exn
18381844
in
18391845
let Internal p' = to_internal_promise p' in
18401846
(* Run the user's function [f]. *)
@@ -1900,7 +1906,7 @@ struct
19001906

19011907
let p' =
19021908
try f v
1903-
with exn when is_not_ocaml_runtime_exception exn ->
1909+
with exn when !exception_filter exn ->
19041910
fail (add_loc exn) in
19051911
let Internal p' = to_internal_promise p' in
19061912

@@ -1957,7 +1963,7 @@ struct
19571963

19581964
let p''_result =
19591965
try Fulfilled (f v) with exn
1960-
when is_not_ocaml_runtime_exception exn -> Rejected exn
1966+
when !exception_filter exn -> Rejected exn
19611967
in
19621968

19631969
let State_may_now_be_pending_proxy p'' = may_now_be_proxy p'' in
@@ -1987,7 +1993,7 @@ struct
19871993
to_public_promise
19881994
{state =
19891995
try Fulfilled (f v)
1990-
with exn when is_not_ocaml_runtime_exception exn -> Rejected exn})
1996+
with exn when !exception_filter exn -> Rejected exn})
19911997
~if_deferred:(fun () ->
19921998
let (p'', callback) =
19931999
create_result_promise_and_callback_if_deferred () in
@@ -2006,7 +2012,7 @@ struct
20062012
let catch f h =
20072013
let p =
20082014
try f ()
2009-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
2015+
with exn when !exception_filter exn -> fail exn
20102016
in
20112017
let Internal p = to_internal_promise p in
20122018
let p = underlying p in
@@ -2031,7 +2037,7 @@ struct
20312037

20322038
let p' =
20332039
try h exn
2034-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
2040+
with exn when !exception_filter exn -> fail exn
20352041
in
20362042
let Internal p' = to_internal_promise p' in
20372043

@@ -2067,7 +2073,7 @@ struct
20672073
let backtrace_catch add_loc f h =
20682074
let p =
20692075
try f ()
2070-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
2076+
with exn when !exception_filter exn -> fail exn
20712077
in
20722078
let Internal p = to_internal_promise p in
20732079
let p = underlying p in
@@ -2092,7 +2098,7 @@ struct
20922098

20932099
let p' =
20942100
try h exn
2095-
with exn when is_not_ocaml_runtime_exception exn ->
2101+
with exn when !exception_filter exn ->
20962102
fail (add_loc exn)
20972103
in
20982104
let Internal p' = to_internal_promise p' in
@@ -2129,7 +2135,7 @@ struct
21292135
let try_bind f f' h =
21302136
let p =
21312137
try f ()
2132-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
2138+
with exn when !exception_filter exn -> fail exn
21332139
in
21342140
let Internal p = to_internal_promise p in
21352141
let p = underlying p in
@@ -2146,7 +2152,7 @@ struct
21462152

21472153
let p' =
21482154
try f' v
2149-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
2155+
with exn when !exception_filter exn -> fail exn
21502156
in
21512157
let Internal p' = to_internal_promise p' in
21522158

@@ -2162,7 +2168,7 @@ struct
21622168

21632169
let p' =
21642170
try h exn
2165-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
2171+
with exn when !exception_filter exn -> fail exn
21662172
in
21672173
let Internal p' = to_internal_promise p' in
21682174

@@ -2204,7 +2210,7 @@ struct
22042210
let backtrace_try_bind add_loc f f' h =
22052211
let p =
22062212
try f ()
2207-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
2213+
with exn when !exception_filter exn -> fail exn
22082214
in
22092215
let Internal p = to_internal_promise p in
22102216
let p = underlying p in
@@ -2221,7 +2227,7 @@ struct
22212227

22222228
let p' =
22232229
try f' v
2224-
with exn when is_not_ocaml_runtime_exception exn ->
2230+
with exn when !exception_filter exn ->
22252231
fail (add_loc exn)
22262232
in
22272233
let Internal p' = to_internal_promise p' in
@@ -2238,7 +2244,7 @@ struct
22382244

22392245
let p' =
22402246
try h exn
2241-
with exn when is_not_ocaml_runtime_exception exn ->
2247+
with exn when !exception_filter exn ->
22422248
fail (add_loc exn)
22432249
in
22442250
let Internal p' = to_internal_promise p' in
@@ -2493,7 +2499,7 @@ struct
24932499
let dont_wait f h =
24942500
let p =
24952501
try f ()
2496-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
2502+
with exn when !exception_filter exn -> fail exn
24972503
in
24982504
let Internal p = to_internal_promise p in
24992505

@@ -2516,7 +2522,7 @@ struct
25162522
let async f =
25172523
let p =
25182524
try f ()
2519-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
2525+
with exn when !exception_filter exn -> fail exn
25202526
in
25212527
let Internal p = to_internal_promise p in
25222528

@@ -3119,39 +3125,39 @@ struct
31193125

31203126

31213127
let apply f x =
3122-
try f x with exn when is_not_ocaml_runtime_exception exn -> fail exn
3128+
try f x with exn when !exception_filter exn -> fail exn
31233129

31243130
let wrap f =
31253131
try return (f ())
3126-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
3132+
with exn when !exception_filter exn -> fail exn
31273133

31283134
let wrap1 f x1 =
31293135
try return (f x1)
3130-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
3136+
with exn when !exception_filter exn -> fail exn
31313137

31323138
let wrap2 f x1 x2 =
31333139
try return (f x1 x2)
3134-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
3140+
with exn when !exception_filter exn -> fail exn
31353141

31363142
let wrap3 f x1 x2 x3 =
31373143
try return (f x1 x2 x3)
3138-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
3144+
with exn when !exception_filter exn -> fail exn
31393145

31403146
let wrap4 f x1 x2 x3 x4 =
31413147
try return (f x1 x2 x3 x4)
3142-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
3148+
with exn when !exception_filter exn -> fail exn
31433149

31443150
let wrap5 f x1 x2 x3 x4 x5 =
31453151
try return (f x1 x2 x3 x4 x5)
3146-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
3152+
with exn when !exception_filter exn -> fail exn
31473153

31483154
let wrap6 f x1 x2 x3 x4 x5 x6 =
31493155
try return (f x1 x2 x3 x4 x5 x6)
3150-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
3156+
with exn when !exception_filter exn -> fail exn
31513157

31523158
let wrap7 f x1 x2 x3 x4 x5 x6 x7 =
31533159
try return (f x1 x2 x3 x4 x5 x6 x7)
3154-
with exn when is_not_ocaml_runtime_exception exn -> fail exn
3160+
with exn when !exception_filter exn -> fail exn
31553161

31563162

31573163

src/core/lwt.mli

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1999,15 +1999,43 @@ val ignore_result : _ t -> unit
19991999
resolved, completing any associated side effects along the way. In fact,
20002000
the function that does {e that} is ordinary {!Lwt.bind}. *)
20012001

2002+
(** {4 Runtime exception filters}
2003+
2004+
Depending on the kind of programs that you write, you may need to treat
2005+
exceptions thrown by the OCaml runtime (namely [Out_of_memory] and
2006+
[Stack_overflow] differently. This is because (a) these exceptions are not
2007+
reproducible (in that they are thrown at different points of your program
2008+
depending on the machine that your program runs on) and (b) recovering
2009+
from these errors may be impossible.
2010+
2011+
The helpers below allow you to change the way that Lwt handles the two OCaml
2012+
runtime exceptions [Out_of_memory] and [Stack_overflow]. *)
2013+
2014+
(** An [exception_filter] is a value which indicates to Lwt what exceptions to
2015+
catch and what exceptions to let bubble up all the way out of the main loop
2016+
immediately. *)
2017+
type exception_filter
2018+
2019+
(** [catch_all_filter] is the default filter. With it the all the exceptions
2020+
(including [Out_of_memory] and [Stack_overflow]) are caught and transformed
2021+
into rejected promises. *)
2022+
val catch_all_filter : exception_filter
2023+
2024+
(** [catch_not_runtime_filter] is a filter which lets the OCaml runtime
2025+
exceptions ([Out_of_memory] and [Stack_overflow]) go through all the Lwt
2026+
abstractions and bubble all the way out of the call to [Lwt_main.run]. *)
2027+
val catch_not_runtime_filter : exception_filter
2028+
2029+
(** [set_exception_filter] sets the given exception filter globally. *)
2030+
val set_exception_filter : exception_filter -> unit
2031+
20022032

20032033

20042034
(**/**)
20052035

20062036
val poll : 'a t -> 'a option
20072037
val apply : ('a -> 'b t) -> 'a -> 'b t
20082038

2009-
val is_not_ocaml_runtime_exception : exn -> bool
2010-
20112039
val backtrace_bind :
20122040
(exn -> exn) -> 'a t -> ('a -> 'b t) -> 'b t
20132041
val backtrace_catch :
@@ -2020,3 +2048,5 @@ val backtrace_try_bind :
20202048
val abandon_wakeups : unit -> unit
20212049

20222050
val debug_state_is : 'a state -> 'a t -> bool t
2051+
2052+
val filter_exception : exn -> bool

src/core/lwt_seq.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ let rec unfold f u () =
270270
match f u with
271271
| None -> return_nil
272272
| Some (x, u') -> Lwt.return (Cons (x, unfold f u'))
273-
| exception exc when Lwt.is_not_ocaml_runtime_exception exc -> Lwt.fail exc
273+
| exception exc when Lwt.filter_exception exc -> Lwt.fail exc
274274

275275
let rec unfold_lwt f u () =
276276
let* x = f u in
@@ -305,7 +305,7 @@ let rec of_seq seq () =
305305
| Seq.Nil -> return_nil
306306
| Seq.Cons (x, next) ->
307307
Lwt.return (Cons (x, (of_seq next)))
308-
| exception exn when Lwt.is_not_ocaml_runtime_exception exn -> Lwt.fail exn
308+
| exception exn when Lwt.filter_exception exn -> Lwt.fail exn
309309

310310
let rec of_seq_lwt (seq: 'a Lwt.t Seq.t): 'a t = fun () ->
311311
match seq () with
@@ -321,4 +321,4 @@ let of_seq_lwt (seq: 'a Lwt.t Seq.t): 'a t = fun () ->
321321
let+ x = x in
322322
let next = of_seq_lwt next in
323323
Cons (x, next)
324-
| exception exc when Lwt.is_not_ocaml_runtime_exception exc -> Lwt.fail exc
324+
| exception exc when Lwt.filter_exception exc -> Lwt.fail exc

src/react/lwt_react.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ module E = struct
9898
Lwt_stream.iter
9999
(fun v ->
100100
try push v
101-
with exn when Lwt.is_not_ocaml_runtime_exception exn ->
101+
with exn when Lwt.filter_exception exn ->
102102
!Lwt.async_exception_hook exn)
103103
stream in
104104
with_finaliser (cancel_thread t) event

src/unix/lwt_io.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ let make :
537537
perform_io,
538538
fun pos cmd ->
539539
try seek pos cmd
540-
with e when Lwt.is_not_ocaml_runtime_exception e -> Lwt.fail e
540+
with e when Lwt.filter_exception e -> Lwt.fail e
541541
);
542542
} and wrapper = {
543543
state = Idle;

src/unix/lwt_main.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ let run p =
107107
| result ->
108108
finished ();
109109
result
110-
| exception exn when Lwt.is_not_ocaml_runtime_exception exn ->
110+
| exception exn when Lwt.filter_exception exn ->
111111
finished ();
112112
raise exn
113113

src/unix/lwt_preemptive.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ let detach f args =
182182
let task () =
183183
try
184184
result := Result.Ok (f args)
185-
with exn when Lwt.is_not_ocaml_runtime_exception exn ->
185+
with exn when Lwt.filter_exception exn ->
186186
result := Result.Error exn
187187
in
188188
get_worker () >>= fun worker ->

src/unix/lwt_timeout.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ let rec loop () =
7272
(*XXX Should probably report any exception *)
7373
try
7474
x.action ()
75-
with e when Lwt.is_not_ocaml_runtime_exception e ->
75+
with e when Lwt.filter_exception e ->
7676
!handle_exn e
7777
done;
7878
curr := (!curr + 1) mod (Array.length !buckets);

0 commit comments

Comments
 (0)