Private.Rcfd
A safe wrapper around Unix.file_descr
.
When FDs are shared between domains, there is the risk that one domain will try to use an FD just as another domain is closing it. Then this can happen:
This would break modularity, since the fibers in domains A and C may have no connection with each other.
To prevent this, we keep a ref-count, tracking how many fibers are using the FD. Wrap uses of the FD in use
to ensure that it won't be closed while you're using it.
Calling close
while one or more operations are still in progress marks the wrapper as closing (so that no futher operations can start); the last operation to finish will close the underlying FD.
val make : Unix.file_descr -> t
val use : if_closed:(unit -> 'a) -> t -> (Unix.file_descr -> 'a) -> 'a
use t fn ~if_closed
calls fn fd
, preventing fd
from being closed until fn
returns.
if_closed ()
is used if t
is closed before we can increment the ref-count. use
can be used in parallel from multiple domains at the same time.
This operation is lock-free and can be used safely from signal handlers, etc.
val is_open : t -> bool
is_open t
returns true
until t
has been marked as closing, after which it returns false
.
This is mostly useful inside the callback of use
, to test whether another fiber has started closing t
(in which case you may decide to stop early).
val close : t -> bool
close t
marks t
as closed and arranges for its FD to be closed.
If there are calls to use
in progress, the last one to finish will close the underlying FD. Note that this function returns without waiting for the close to happen in that case.
Returns true
after successfully marking t
as closing, or false
if it was already marked.
If you need to wait until the underlying FD is closed, use remove
and then close the FD yourself instead.
val remove : t -> Unix.file_descr option
remove t
closes t
and returns the FD.
It immediately marks t
as closing (so no further operations can start) and then waits until there are no further users.
This operation suspends the calling fiber and so must run from an Eio fiber. It does not allow itself to be cancelled, since it takes ownership of the FD and that would be leaked if it aborted.
If another fiber marks t
as closing before remove
can, it returns None
immediately.
val peek : t -> Unix.file_descr
peek t
returns the file-descriptor without updating the ref-count.
You must ensure that t
isn't closed while using the result. This is a dangerous operation and may be removed in the future.
If t
was closed, it instead raises an exception (if you're not sure when t
might get closed, you shouldn't be using this function).