Module Async_kernel.Monitor
The part of the Execution_context that determines what to do when there is an unhandled exception.
Every Async computation runs within the context of some monitor, which, when the computation is running, is referred to as the "current" monitor. Monitors are arranged in a tree -- when a new monitor is created, it is a child of the current monitor.
If a computation raises an unhandled exception, the behavior depends on whether the current monitor is "detached" or "attached". If the monitor has been "detached", via one of the detach* functions, then whoever detached it is responsible for dealing with the exception. If the monitor is still attached, then the exception bubbles to monitor's parent. If an exception bubbles to the initial monitor, i.e., the root of the monitor tree, that prints an unhandled-exception message and calls exit 1.
Note about the toplevel monitor
It is important to note that in the toplevel monitor, exceptions will only be caught in the Async part of a computation. For example, in:
upon (f ()) g if f raises, the exception will not go to a monitor; it will go to the next caml exception handler on the stack. Any exceptions raised by g will be caught by the scheduler and propagated to the toplevel monitor. Because of this it is advised to always use Scheduler.schedule or Scheduler.within. For example:
Scheduler.within (fun () -> upon (f ()) g) This code will catch an exception in either f or g, and propagate it to the monitor.
This is only relevant to the toplevel monitor because if you create another monitor and you wish to run code within it you have no choice but to use Scheduler.within. try_with creates its own monitor and uses Scheduler.within, so it does not have this problem.
module Deferred = Async_kernel__.Deferred1type t= Async_kernel__.Monitor0.t
val sexp_of_t : t -> Ppx_sexp_conv_lib.Sexp.t
type 'a with_optional_monitor_name= ?here:Core_kernel.Source_code_position.t -> ?info:Core_kernel.Info.t -> ?name:string -> 'a
val create : (unit -> t) with_optional_monitor_namecreate ()returns a new monitor whose parent is the current monitor.
val name : t -> Core_kernel.Info.tname treturns the name of the monitor, or a unique id if no name was supplied tocreate.
val parent : t -> t optionval depth : t -> intval current : unit -> tcurrent ()returns the current monitor.
val detach : t -> unitdetach tdetachestso that errors raised totare not passed tot's parent monitor. If those errors aren't handled in some other way, then they will effectively be ignored. One should usually usedetach_and_iter_errorsso that errors are not ignored.
val detach_and_iter_errors : t -> f:(exn -> unit) -> unitdetach_and_iter_errors t ~fdetachestand passes tofall subsequent errors that reacht, stopping iteration iffraises an exception. An exception raised byfis sent to the monitor in effect whendetach_and_iter_errorswas called.
val detach_and_get_next_error : t -> exn Deferred.tdetach_and_get_next_error tdetachestand returns a deferred that becomes determined with the next error that reachest(possibly never).
val detach_and_get_error_stream : t -> exn Tail.Stream.tdetach_and_get_error_stream tdetachestand returns a stream of all subsequent errors that reacht.Stream.iter (detach_and_get_error_stream t) ~fis equivalent todetach_and_iter_errors t ~f.
val get_next_error : t -> exn Deferred.tget_next_error treturns a deferred that becomes determined the next timetgets an error, if ever. Callingget_next_error tdoes not detacht, and if no other call has detachedt, then errors will still bubble up the monitor tree.
val extract_exn : exn -> exnextract_exn exnextracts the exn from an error exn that comes from a monitor. If it is not supplied such an error exn, it returns the exn itself. It removes the backtrace from the error (see discussion intry_with).
val has_seen_error : t -> boolhas_seen_error treturns true iff the monitor has ever seen an error.
val send_exn : t -> ?backtrace:[ `Get | `This of Core_kernel.Backtrace.t ] -> exn -> unitsend_exn t exn ?backtracesends the exceptionexnas an error to be handled by monitort. By default, the error will not contain a backtrace. However, the caller can supply one using`This, or use`Getto request thatsend_exnobtain one usingBacktrace.Exn.most_recent ().
val try_with : (?extract_exn:bool -> ?run:[ `Now | `Schedule ] -> ?rest:[ `Log | `Raise | `Call of exn -> unit ] -> (unit -> 'a Deferred.t) -> ('a, exn) Core_kernel.Result.t Deferred.t) with_optional_monitor_nametry_with frunsf ()in a monitor and returns the result asOk xifffinishes normally, or returnsError eif there is an exception. It either runsfnow, ifrun = `Now, or schedules a job to runf, ifrun = `Schedule. Once a result is returned, subsequent exceptions raised to the monitor are handled according torest:`Log: Logged to a global error log (cannot raise).`Raise: Reraised to the monitor oftry_with's caller.`Call f: Passed tofwithin the context of the caller oftry_with's monitor.
The
nameargument is used to give a name to the monitor the computation will be running in. This name will appear when printing errors.try_withrunsf ()in a new monitortthat has no parent. This works becausetry_withcallsdetach_and_get_error_stream tand explicitly handles all errors sent tot. No errors would ever implicitly propagate tot's parent, althoughtry_withwill explicitly send them tot's parent withrest = `Raise.If
extract_exn = true, then in anError exnresult, theexnwill be the actual exception raised by the computation. Ifextract_exn = false, then theexnwill include additional information, like the monitor and backtrace.
val try_with_or_error : (?extract_exn:bool -> (unit -> 'a Deferred.t) -> 'a Core_kernel.Or_error.t Deferred.t) with_optional_monitor_nametry_with_or_erroris liketry_withbut returns'a Or_error.t Deferred.tinstead of('a,exn) Result.t Deferred.t. More precisely:try_with_or_error f ?extract_exn = try_with f ?extract_exn ~run:`Now ~rest:`Log >>| Or_error.of_exn_result~run:`Nowis different fromtry_with's default,~run:`Schedule. Based on experience, we think~run:`Nowis a better behavior.
val try_with_join_or_error : (?extract_exn:bool -> (unit -> 'a Core_kernel.Or_error.t Deferred.t) -> 'a Core_kernel.Or_error.t Deferred.t) with_optional_monitor_nametry_with_join_or_error f = try_with_or_error f >>| Or_error.join.
val handle_errors : ((unit -> 'a Deferred.t) -> (exn -> unit) -> 'a Deferred.t) with_optional_monitor_namehandle_errors ?name f handlerrunsf ()inside a new monitor with the optionally supplied name, and callshandler erroron every error raised to that monitor. Any error raised byhandlergoes to the monitor in effect whenhandle_errorswas called.Errors that are raised after
f ()becomes determined will still be sent tohandler, i.e., the new monitor lives as long as jobs created byflive.
val catch_stream : ((unit -> unit) -> exn Tail.Stream.t) with_optional_monitor_namecatch_stream ?name frunsf ()inside a new monitormand returns the stream of errors raised tom.
val catch : ((unit -> unit) -> exn Deferred.t) with_optional_monitor_namecatch ?name frunsf ()inside a new monitormand returns the first error raised tom.
val catch_error : ((unit -> unit) -> Core_kernel.Error.t Deferred.t) with_optional_monitor_namecatch_error ?name frunsf ()inside of a new monitormand returns the first error raised tom.
val protect : (?extract_exn:bool -> ?run:[ `Now | `Schedule ] -> (unit -> 'a Deferred.t) -> finally:(unit -> unit Deferred.t) -> 'a Deferred.t) with_optional_monitor_nameprotect f ~finallyrunsf ()and thenfinallyregardless of the success or failure off. It re-raises any exception thrown byfor returns whateverfreturned.The
nameargument is used to give a name to the monitor the computation will be running in. This name will appear when printing the errors.
val main : tThis is the initial monitor and is the root of the monitor tree. Unhandled exceptions are raised to this monitor.
module Expert : sig ... endmodule Exported_for_scheduler : sig ... end