[Haskell-cafe] runInteractiveCommand: program ends before writing or reading all the output

Duncan Coutts duncan.coutts at worc.ox.ac.uk
Thu May 15 18:23:52 EDT 2008


On Thu, 2008-05-15 at 13:40 -0400, Olivier Boudry wrote:
> Hi all,
> 
> It's the first time I use the runInteractiveCommand and I was probably
> bitten by laziness.

Yes. I think Philip diagnosed the problem correctly.

As an example let me show you as an example how we use it in Cabal:

rawSystemStdout :: Verbosity -> FilePath -> [String] -> IO String
rawSystemStdout verbosity path args = do
  Exception.bracket
     (runInteractiveProcess path args Nothing Nothing)
     (\(inh,outh,errh,_) -> hClose inh >> hClose outh >> hClose errh)
    $ \(_,outh,errh,pid) -> do

      -- We want to process the output as text.
      hSetBinaryMode outh False

      -- fork off a thread to pull on (and discard) the stderr
      -- so if the process writes to stderr we do not block.
      -- NB. do the hGetContents synchronously, otherwise the outer
      -- bracket can exit before this thread has run, and hGetContents
      -- will fail.
      err <- hGetContents errh 
      forkIO $ do evaluate (length err); return ()

      -- wait for all the output
      output <- hGetContents outh
      evaluate (length output)

      -- wait for the program to terminate
      exitcode <- waitForProcess pid

      -- on failure, throw the exit code as an exception      
      unless (exitCode == ExitSuccess) $ exitWith exitCode

      return (output, exitcode)


So as you can see there are two subtleties. One is the issue that we
have to make sure we get all the output before we wait for the program
to finish. Using evaluate is the trick there.

The other is that we also have to pull on the stderr of the process.
Otherwise the process may be trying to output to stderr but if the pipe
buffer fills up then writing to stderr will block and so it will not be
able to continue writing to stdout we'll have deadlocked the process. So
we have to forkIO a thread to pull on stderr.

This is one reason that runInteractiveProcess is hard to use and
especially hard to use portably due to the use of preemptable threads.
It would be possible to do without threads if we used non-blocking IO
and interleaved between reading from stdout and stderr.

Duncan



More information about the Haskell-Cafe mailing list