From: David Scott Date: Tue, 22 Dec 2009 11:37:00 +0000 (+0000) Subject: [ocamldoc]: attempt to tidy-up the ocamldoc comments in the forkhelpers module X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=fb263074a5cbcbf1d55bec4728d88c6450f98e74;p=xcp%2Fxen-api-libs.git [ocamldoc]: attempt to tidy-up the ocamldoc comments in the forkhelpers module Signed-off-by: David Scott --- diff --git a/stdext/forkhelpers.mli b/stdext/forkhelpers.mli index bedb87e..b7754ee 100644 --- a/stdext/forkhelpers.mli +++ b/stdext/forkhelpers.mli @@ -11,17 +11,39 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) -(* Functions to safely fork potentially long-running sub-processes without - leaking file descriptors or accidentally deadlocking the parent process. *) + +(** Functions to execute processes, pass file descriptors around and return results *) + +(** The low-level Unix.fork(), Unix.exec*() functions and friends are not safe to + call in multithreaded programs for two reasons: + + parallel threads opening new file descriptors will have these descriptors captured + by a fork(). This leads to annoying glitches like (for example) attempts to 'umount' + a filesystem being rejected because a file is still open. + + although Unix.fork() will call (via the ocaml runtime) a pthread_atfork handler + which attempts to clean up the state of the threading system in the child, this relies + on quite a complex glibc implementation. + + Additionally Unix.fork(), Unix.exec*() are very low-level primitives. When we call + these functions what we actually want to do is run some separate process with certain + file-descriptors, optionally returning results. + + The interface in this module + + is higher-level than Unix.fork(), Unix.exec*() + + allows us to offload Unix.fork(), Unix.exec*() to a single-threaded separate process + where the glibc+ocaml runtime codepaths are simpler and hopefully more reliable. *) + +(** { 1 High-level interface } *) (** [execute_command_get_output cmd args] runs [cmd args] and returns (stdout, stderr) - on success (exit 0). On failure this raises [Spawn_internal_error(stderr, stdout, Unix.process_status)] -*) + on success (exit 0). On failure this raises + [Spawn_internal_error(stderr, stdout, Unix.process_status)] *) val execute_command_get_output : ?env:string array -> string -> string list -> string * string (** Thrown by [execute_command_get_output] if the subprocess exits with a non-zero exit code *) exception Spawn_internal_error of string * string * Unix.process_status +(** { 2 Low-level interface } *) + (** Represents a forked process *) type pidty @@ -33,6 +55,7 @@ val getpid : pidty -> int (** Thrown by [safe_close_and_exec] if the process exits with a non-zero exit code. *) exception Subprocess_failed of int + (** Thrown by [safe_close_and_exec] if the process exits due to a signal *) exception Subprocess_killed of int @@ -44,18 +67,21 @@ val safe_close_and_exec : ?env:string array -> Unix.file_descr option -> Unix.fi (** [waitpid p] returns the (pid, Unix.process_status) *) val waitpid : pidty -> (int * Unix.process_status) + (** [waitpid_nohang p] returns the (pid, Unix.process_status) if the process has already quit or (0, Unix.WEXITTED 0) if the process is still running. *) val waitpid_nohang : pidty -> (int * Unix.process_status) + (** [dontwaitpid p]: signals the caller's desire to never call waitpid. Note that the final process will not persist as a zombie. *) val dontwaitpid : pidty -> unit + (** [waitpid_fail_if_bad_exit p] calls waitpid on [p] and throws [Subprocess_failed x] if the process exits with non-zero code x and [Subprocess_killed x] if the process is killed by a signal and exits with non-zero code x. *) val waitpid_fail_if_bad_exit : pidty -> unit - +(** result returned by [with_logfile_fd] *) type 'a result = Success of string * 'a | Failure of string * exn (** Creates a temporary file and opens it for logging. The fd is passed to the function