Page
Library
Module
Module type
Parameter
Class
Class type
Source
Picos_std_finallySourceSyntax for avoiding resource leaks for Picos.
A resource is something that is acquired and must be released after it is no longer needed.
We open both this library and a few other libraries
open Picos_io
open Picos_std_finally
open Picos_std_structured
open Picos_std_syncfor the examples.
let@ resource = template in scope is equivalent to template (fun resource -> scope).
ℹ️ You can use this binding operator with any template function that has a type of the form ('r -> 'a) -> 'a.
finally release acquire scope calls acquire () to obtain a resource, calls scope resource, and then calls release resource after the scope exits.
Either contains a resource or is empty as the resource has been transferred, dropped, or has been borrowed temporarily.
drop instance releases the resource, if any, contained by the instance.
borrow instance scope borrows the resource stored in the instance, calls scope resource, and then returns the resource to the instance after scope exits.
transfer source transfers the resource stored in the source instance into a new target instance, calls scope target. Then, if scope returns normally, awaits until the target instance becomes empty. In case scope raises an exception or the fiber is canceled, the target instance will be dropped.
move instance scope is equivalent to transfer instance (fun instance -> borrow instance scope).
Here is a sketch of a server that recursively forks a fiber to accept and handle a client:
let recursive_server server_fd =
Flock.join_after @@ fun () ->
(* recursive server *)
let rec accept () =
let@ client_fd =
finally Unix.close @@ fun () ->
Unix.accept ~cloexec:true server_fd
|> fst
in
(* fork to accept other clients *)
Flock.fork accept;
(* handle this client... omitted *)
()
in
Flock.fork acceptThere is also a way to move instantiated resources to allow forking fibers to handle clients without leaks.
Here is a sketch of a server that accepts in a loop and forks fibers to handle clients:
let looping_server server_fd =
Flock.join_after @@ fun () ->
(* loop to accept clients *)
while true do
let@ client_fd =
instantiate Unix.close @@ fun () ->
Unix.accept ~cloexec:true server_fd
|> fst
in
(* fork to handle this client *)
Flock.fork @@ fun () ->
let@ client_fd = move client_fd in
(* handle client... omitted *)
()
doneYou can move an instantiated resource between any two fibers and borrow it before moving it. For example, you can create a resource in a child fiber, use it there, and then move it to the parent fiber:
let move_from_child_to_parent () =
Flock.join_after @@ fun () ->
(* for communicating a resource *)
let shared_ivar = Ivar.create () in
(* fork a child that creates a resource *)
Flock.fork begin fun () ->
let pretend_release () = ()
and pretend_acquire () = () in
(* allocate a resource *)
let@ instance =
instantiate pretend_release pretend_acquire
in
begin
(* borrow the resource *)
let@ resource = borrow instance in
(* use the resource... omitted *)
()
end;
(* send the resource to the parent *)
Ivar.fill shared_ivar instance
end;
(* await for a resource from the child and own it *)
let@ resource = Ivar.read shared_ivar |> move in
(* use the resource... omitted *)
()The above uses an Ivar to communicate the movable resource from the child fiber to the parent fiber. Any concurrency safe mechanism could be used.