MLton 20051202 MLtonProcess
Home  Index  
signature MLTON_PROCESS =
   sig
      type pid

      val spawn: {args: string list, path: string} -> pid
      val spawne: {args: string list, env: string list, path: string} -> pid
      val spawnp: {args: string list, file: string} -> pid

      structure Child:
        sig
          type ('use, 'dir) t

          val binIn: (BinIO.instream, input) t -> BinIO.instream
          val binOut: (BinIO.outstream, output) t -> BinIO.outstream
          val fd: (Posix.FileSys.file_desc, 'dir) t -> Posix.FileSys.file_desc
          val remember: (any, 'dir) t -> ('use, 'dir) t
          val textIn: (TextIO.instream, input) t -> TextIO.instream
          val textOut: (TextIO.outstream, output) t -> TextIO.outstream
        end

      structure Param:
        sig
          type ('use, 'dir) t

          val child: (chain, 'dir) Child.t -> (none, 'dir) t
          val fd: Posix.FileSys.file_desc -> (none, 'dir) t
          val file: string -> (none, 'dir) t
          val forget: ('use, 'dir) t -> (any, 'dir) t
          val null: (none, 'dir) t
          val pipe: ('use, 'dir) t
          val self: (none, 'dir) t
        end

      type ('stdin, 'stdout, 'stderr) t
      type any
      type chain
      type input
      type none
      type output

      exception MisuseOfForget
      exception DoublyRedirected

      val create:
         {args: string list,
          env: string list option,
          path: string,
          stderr: ('stderr, output) Param.t,
          stdin: ('stdin, input) Param.t,
          stdout: ('stdout, output) Param.t}
         -> ('stdin, 'stdout, 'stderr) t
      val getStderr: ('stdin, 'stdout, 'stderr) t -> ('stderr, input) Child.t
      val getStdin:  ('stdin, 'stdout, 'stderr) t -> ('stdin, output) Child.t
      val getStdout: ('stdin, 'stdout, 'stderr) t -> ('stdout, input) Child.t
      val kill: ('stdin, 'stdout, 'stderr) t * Posix.Signal.signal -> unit
      val reap: ('stdin, 'stdout, 'stderr) t -> Posix.Process.exit_status
   end

Spawn

The spawn functions provide an alternative to the fork/exec idiom that is typically used to create a new process. On most platforms, the spawn functions are simple wrappers around fork/exec. However, under Windows, the spawn functions are primitive. All spawn functions return the process id of the spawned process. They differ in how the executable is found and the environment that it uses.

Create

MLton.Process.create provides functionality similar to Unix.executeInEnv, but provides more control control over the input, output, and error streams. In addition, create works on all platforms, including Cygwin and MinGW (Windows) where Posix.fork is unavailable. For greatest portability programs should still use the standard Unix.execute, Unix.executeInEnv, and OS.Process.system.

The following types and sub-structures are used by the create function. They provide static type checking of correct stream usage.

Child

Param

Process

The types BinIO.instream, BinIO.outstream, TextIO.instream, TextIO.outstream, and Posix.FileSys.file_desc are also valid types with which to instantiate child streams.

Important usage notes

When building an application with many pipes between child processes, it is important to ensure that there are no cycles in the undirected pipe graph. If this property is not maintained, deadlocks are a very serious potential bug which may only appear under difficult to reproduce conditions.

The danger lies in that most operating systems implement pipes with a fixed buffer size. If process A has two output pipes which process B reads, it can happen that process A blocks writing to pipe 2 because it is full while process B blocks reading from pipe 1 because it is empty. This same situation can happen with any undirected cycle formed between processes (vertexes) and pipes (undirected edges) in the graph.

It is possible to make this safe using low-level I/O primitives for polling. However, these primitives are not very portable and difficult to use properly. A far better approach is to make sure you never create a cycle in the first place.

For these reasons, the Unix.executeInEnv is a very dangerous function. Be careful when using it to ensure that the child process only operates on either stdin or stdout, but not both.

Example use of MLton.Process.create

The following example program launches the ipconfig utility, pipes its output through grep, and then reads the result back into the program.

open MLton.Process
val p = 
        create {args = [ "/all" ],
                env = NONE,
                path = "C:\\WINDOWS\\system32\\ipconfig.exe",
                stderr = Param.self,
                stdin = Param.null,
                stdout = Param.pipe}
val q =
        create {args = [ "IP-Ad" ],
                env = NONE,
                path = "C:\\msys\\bin\\grep.exe",
                stderr = Param.self,
                stdin = Param.child (getStdout p),
                stdout = Param.pipe}
fun suck h =
        case TextIO.inputLine h of
                NONE => ()
                | SOME s => (print ("'" ^ s ^ "'\n"); suck h)

val () = suck (Child.textIn (getStdout q))


Last edited on 2005-12-02 04:22:19 by StephenWeeks.