Module Eio.Switch

Grouping fibers and other resources so they can be turned off together.

Many resources in Eio (such as fibers and file handles) require a switch to be provided when they are created. The resource cannot outlive its switch.

If a function wants to create such resources, and was not passed a switch as an argument, it will need to create a switch using run. This doesn't return until all resources attached to it have been freed, preventing the function from leaking resources.

Any function creating resources that outlive it needs to be given a switch by its caller.

Each switch includes its own Cancel.t context. Calling fail cancels all fibers attached to the switch and, once they have exited, reports the error.

Note: this concept is known as a "nursery" or "bundle" in some other systems.

Example:

Switch.run (fun sw ->
   let flow = Dir.open_in ~sw dir "myfile.txt" in
   ...
);
(* [flow] will have been closed by this point *)
type t

A switch contains a group of fibers and other resources (such as open file handles).

Switch creation

val run : ?name:string -> (t -> 'a) -> 'a

run fn runs fn with a fresh switch (initially on).

When fn finishes, run waits for all fibers registered with the switch to finish, and then releases all attached resources.

If fail is called, run will re-raise the exception (after everything is cleaned up). If fn raises an exception, it is passed to fail.

  • parameter name

    Used to name the switch when tracing.

val run_protected : ?name:string -> (t -> 'a) -> 'a

run_protected fn is like run but ignores cancellation requests from the parent context.

Cancellation and failure

val check : t -> unit

check t checks that t is still on.

val get_error : t -> exn option

get_error t is like check t except that it returns the exception instead of raising it. If t is finished, this returns (rather than raising) the Invalid_argument exception too.

val fail : ?bt:Stdlib.Printexc.raw_backtrace -> t -> exn -> unit

fail t ex adds ex to t's set of failures and ensures that the switch's cancellation context is cancelled, to encourage all fibers to exit as soon as possible.

fail returns immediately, without waiting for the shutdown actions to complete. The exception will be raised later by run, and run's caller is responsible for handling it. Exn.combine is used to avoid duplicate or unnecessary exceptions.

  • parameter bt

    A backtrace to attach to ex

Cleaning up resources

It is possible to attach clean-up hooks to a switch. Once all fibers within the switch have finished, these hooks are called. For example, when a file is opened it will register a release hook to close it.

Functions that create such resources will take a switch argument and call these functions for you. You usually don't need to call these directly.

val on_release : t -> (unit -> unit) -> unit

on_release t fn registers fn to be called once t's main function has returned and all fibers have finished.

If fn raises an exception, it is passed to fail.

Release handlers are run in LIFO order, in series.

Note that fn is called within a Cancel.protect, since aborting clean-up actions is usually a bad idea and the switch may have been cancelled by the time it runs. You cannot attach new resources to a switch once the cancel hooks start to run.

This function is thread-safe (but not signal-safe). If the switch finishes before fn can be registered, it raises Invalid_argument and runs fn immediately instead.

type hook

A handle for removing a clean-up callback.

val null_hook : hook

A dummy hook. try_remove_hook null_hook = false.

val on_release_cancellable : t -> (unit -> unit) -> hook

Like on_release, but the handler can be removed later.

For example, opening a file will call on_release_cancellable to ensure the file is closed later. However, if the file is manually closed before that, it will use remove_hook to remove the hook, which is no longer needed.

This function is thread-safe (but not signal-safe).

val try_remove_hook : hook -> bool

try_remove_hook h removes a previously-added hook. Returns true if the hook was successfully removed, or false if another domain ran it or removed it first.

This function is thread-safe (but not signal-safe).

val remove_hook : hook -> unit

remove_hook h is ignore (try_remove_hook h).

For multi-domain code, consider using try_remove_hook instead so that you can handle the case of trying to close a resource just as another domain is closing it or finishing the switch.

Debugging

val dump : t Fmt.t

Dump out details of the switch's state for debugging.