Module Async_unix.Fd
An Fd.t is a wrapper around a Unix file descriptor, with additional information about the kind of file descriptor and logic to ensure that we don't use a file descriptor that has been closed, or close a file descriptor that is in use.
Since Async uses multiple threads to make read/write and other system calls on file descriptors, and Unix reuses descriptors after they are closed, Async has to be very careful that the file descriptor passed to a system call is referring to the file it intends, and not some other completely unrelated file that Unix has decided to assign to the same descriptor.
Provided that one only accesses a file descriptor within the context of the functions below, Fd guarantees that the file descriptor will not have been closed/reused and will correspond to the same file that it did when the Fd.t was created:
with_file_descr
with_file_descr_deferred
syscall
syscall_exn
syscall_result_exn
syscall_in_thread
syscall_in_thread_exnThe Fd module keeps track of which of these functions are currently accessing the file descriptor, and ensures that any close happens after they complete. Also, once close has been called, it refuses to provide further access to the file descriptor, either by returning a variant `Already_closed or by raising an exception.
Some of the above functions take an optional ?nonblocking:bool argument. The default is false, but if it is set to true, then before supplying the underlying file_descr, the Fd module will first call Unix.set_nonblock file_descr, if it hasn't previously done so on that file descriptor. This is intended to support making nonblocking system calls (e.g., connect, read, write) directly within Async, without releasing the OCaml lock or the Async lock, and without using another thread.
module Kind : sig ... endval sexp_of_t : t -> Ppx_sexp_conv_lib.Sexp.t
val info : t -> Core.Info.tval to_string : t -> stringto_string treturns a pretty sexp of the representation oft.
val create : ?avoid_nonblock_if_possible:bool -> Kind.t -> Core.Unix.File_descr.t -> Core.Info.t -> tcreate ?support_nonblock kind file_descrcreates a newtof the underlying kind and file descriptor.We thought about using
fstat()rather than requiring the user to supply the kind. Butfstatcan block, which would require putting this in a thread, which has some consequences, and it isn't clear that it gets us that much. Also,createis mostly used within the Async implementation -- clients shouldn't need it unless they are mixing Async and non-Async code.If
avoid_nonblock_if_possible, then Async will treat the file descriptor as blocking if it can (more precisely, if it's not a bound socket).
val supports_nonblock : t -> boolsupports_nonblock treturns true iftsupports nonblocking system calls.
val clear_nonblock : t -> unitclear_nonblock tclears thenonblockingflag ontand causes Async to treat the fd as though it doesn't support nonblocking I/O. This is useful for applications that want to share a file descriptor between Async and non-Async code and want to avoidEWOULDBLOCKorEAGAINbeing seen by the non-Async code, which would then cause aSys_blocked_ioexception.clear_nonblock thas no effect ifnot (supports_nonblock t).
module Close : sig ... endThe
Closemodule exists to collectcloseand its associated types, so they can be easily reused elsewhere, e.g.,Unix_syscalls.
include module type of Close
type socket_handling=|Shutdown_socket|Do_not_shutdown_sockettype file_descriptor_handling=|Close_file_descriptor of socket_handling|Do_not_close_file_descriptor
val close : ?file_descriptor_handling:file_descriptor_handling -> t -> unit Async_unix__.Import.Deferred.tclose tprevents further use oft, and makesshutdown()andclose()system calls ont's underlying file descriptor according to thefile_descriptor_handlingargument and whether or nottis a socket, i.e.,kind t = Socket `Active:| file_descriptor_handling | shutdown() | close() | |----------------------------------------------+------------+---------| | Do_not_close_file_descriptor | no | no | | Close_file_descriptor Shutdown_socket | if socket | yes | | Close_file_descriptor Do_not_shutdown_socket | no | yes |The result of
closebecomes determined once the system calls complete. It is OK to callclosemultiple times on the samet; calls subsequent to the initial call will have no effect, but will return the same deferred as the original call.
val close_started : t -> unit Async_unix__.Import.Deferred.tclose_started tbecomes determined whenclose tis called.
val close_finished : t -> unit Async_unix__.Import.Deferred.tclose_finishedreturns the same result asclose, but differs in that it does not have the side effect of initiating a close.
val is_closed : t -> boolis_closed treturnstrueiffclose thas been called.
val with_close : t -> f:(t -> 'a Async_unix__.Import.Deferred.t) -> 'a Async_unix__.Import.Deferred.twith_close t fappliesftot, returns the result off, and closest.
val is_open : t -> boolis_open tisnot (is_closed t)
val stdin : unit -> tstdin,stdout, andstderrare wrappers around the standard Unix file descriptors.
val stdout : unit -> tval stderr : unit -> tval with_file_descr : ?nonblocking:bool -> t -> (Core.Unix.File_descr.t -> 'a) -> [ `Ok of 'a | `Already_closed | `Error of exn ]with_file_descr t frunsfon the file descriptor underlyingt, ifis_open t, and returns`Okor`Erroraccording tof. Ifis_closed t, then it does not callfand returns`Already_closed.
val with_file_descr_exn : ?nonblocking:bool -> t -> (Core.Unix.File_descr.t -> 'a) -> 'awith_file_descr_exnis likewith_file_descrexcept that it raises rather than return`Already_closedor`Error.
val with_file_descr_deferred : t -> (Core.Unix.File_descr.t -> 'a Async_unix__.Import.Deferred.t) -> [ `Ok of 'a | `Already_closed | `Error of exn ] Async_unix__.Import.Deferred.twith_file_descr_deferred t frunsfon the file descriptor underlyingt, ifis_open t, and returns`Okor`Erroraccording tof. Ifis_closed t, then it does not callfand returns`Already_closed. It ensures that the file descriptor underlyingtis not closed until the result offbecomes determined (orfraises).
val with_file_descr_deferred_exn : t -> (Core.Unix.File_descr.t -> 'a Async_unix__.Import.Deferred.t) -> 'a Async_unix__.Import.Deferred.twith_file_descr_deferred_exnis likewith_file_descr_deferred, except that it raises rather than return`Already_closedor`Error.
val interruptible_ready_to : t -> [ `Read | `Write ] -> interrupt:unit Async_unix__.Import.Deferred.t -> [ `Bad_fd | `Closed | `Interrupted | `Ready ] Async_unix__.Import.Deferred.tinterruptible_ready_to t read_write ~interruptreturns a deferred that will become determined when the file descriptor underlyingtcan be read from or written to without blocking, or wheninterruptbecomes determined.
val ready_to : t -> [ `Read | `Write ] -> [ `Bad_fd | `Closed | `Ready ] Async_unix__.Import.Deferred.tready_to t read_writeis likeinterruptible_ready_to, but without the possibility of interruption.
val interruptible_every_ready_to : t -> [ `Read | `Write ] -> interrupt:unit Async_unix__.Import.Deferred.t -> ('a -> unit) -> 'a -> [ `Bad_fd | `Closed | `Unsupported | `Interrupted ] Async_unix__.Import.Deferred.tinterruptible_every_ready_to t read_write ~interrupt f achecks every Async cycle whether the file descriptor underlyingtcan be read from or written to without blocking, and if so, enqueues a job to runf a.interruptible_every_ready_tois level triggered -- it will enqueue a job every cycle if I/O is available, even if the prior job hasn't run yet, or the job ran but did not consume the available data.interruptible_every_ready_toreturns a deferred that will become determined wheninterruptbecomes determined or the file descriptor is closed.
val every_ready_to : t -> [ `Read | `Write ] -> ('a -> unit) -> 'a -> [ `Bad_fd | `Closed | `Unsupported ] Async_unix__.Import.Deferred.tevery_ready_to t read_write f xis likeinterruptible_every_ready_to, but without the possibility of interruption.
val syscall : ?nonblocking:bool -> t -> (Core.Unix.File_descr.t -> 'a) -> [ `Already_closed | `Ok of 'a | `Error of exn ]syscall t frunsAsync_unix.syscallwithfon the file descriptor underlyingt, ifis_open t, and returns`Okor`Erroraccording tof. Ifis_closed t, it does not callfand returns`Already_closed.
val syscall_exn : ?nonblocking:bool -> t -> (Core.Unix.File_descr.t -> 'a) -> 'asyscall_exn t fis likesyscall, except it raises rather than return`Already_closedor`Error.
val syscall_result_exn : ?nonblocking:bool -> t -> 'a -> (Core.Unix.File_descr.t -> 'a -> 'b Core.Unix.Syscall_result.t) -> 'b Core.Unix.Syscall_result.tsyscall_result_exn t f ais likesyscall_exn, except it does not allocate except in exceptional cases.ais passed unchanged tof, and should be used to eliminate allocations due to closure capture.
val syscall_in_thread : t -> name:string -> (Core.Unix.File_descr.t -> 'a) -> [ `Already_closed | `Ok of 'a | `Error of exn ] Async_unix__.Import.Deferred.tsyscall_in_thread t frunsIn_thread.syscallwithfon the file descriptor underlyingt, ifis_open t, and returns a deferred that becomes determined with`Okor`Errorwhen the system call completes. Ifis_closed t, it does not callfand returns`Already_closed.
val syscall_in_thread_exn : t -> name:string -> (Core.Unix.File_descr.t -> 'a) -> 'a Async_unix__.Import.Deferred.tsyscall_in_thread_exnis likesyscall_in_thread, except it raises rather than return`Already_closedor`Error.
val of_in_channel : Core.In_channel.t -> Kind.t -> tof_in_channelandof_out_channelcreate an fd from their underlying file descriptor.
val of_out_channel : Core.Out_channel.t -> Kind.t -> tval of_in_channel_auto : Core.In_channel.t -> t Async_unix__.Import.Deferred.tof_in_channel_auto icis just likeof_in_channel, but usesfstatto determine the kind. It makes some assumptions about sockets, specifically it assumes that a socket is either listening or connected to something (and it usesgetsockoptto find out which). Don't pass anin_channelcontaining an unconnected non-listening socket.
val of_out_channel_auto : Core.Out_channel.t -> t Async_unix__.Import.Deferred.tof_out_channel_auto icis just likeof_out_channel, but usesfstatto determine the kind. It makes some assumptions about sockets, specifically it assumes that a socket is either listening or connected to something (and it usesgetsockoptto find out which). Don't pass anin_channelcontaining an unconnected non listening socket.
val file_descr_exn : t -> Core.Unix.File_descr.tfile_descr_exn treturns the file descriptor underlyingt, unlessis_closed t, in which case it raises. One must be very careful when using this function, and should try not to, since any uses of the resulting file descriptor are unknown to theFdmodule, and hence can violate the guarantee it is trying to enforce.
val to_int_exn : t -> intto_int_exn treturns the the underlying file descriptor as an int. It has the same caveats asfile_descr_exn.
val replace : t -> Kind.t -> Core.Info.t -> unitreplace t kindis for internal use only, byUnix_syscalls. It is used when one wants to reuse a file descriptor in an fd with a new kind.