POpen, opening lots of processes

Tomasz Zielonka t.zielonka at students.mimuw.edu.pl
Fri Jan 9 08:35:02 EST 2004


On Thu, Jan 08, 2004 at 09:33:29AM -0800, Hal Daume III wrote:
> Hi,
> 
> I'm using POpen to shell out to a command several hundreds or thousands of 
> times per call (none of them simultaneous, though, this is completely 
> serial).  After running my program for a while, I get:
> 
> Fail: resource exhausted
> Action: forkProcess
> Reason: Resource temporarily unavailable
> 
> which basically seems to be telling me that the program hasn't been 
> closing the old processes, even though they're definitely not in use 
> anymore.
> 
> Does anyone have any suggestions how to get around this?

I had a similar problem, and finally I created my own solution that
doesn't leave zombies and doesn't block when the launched process writes
too much to stderr.

I tested it in GHC 6.0. For 6.2 you'd have to change the use of
forkProcess.

Usage:

  launch :: String -> [String] -> String -> IO (ProcessStatus, String, String)

  (status, out, err) <- launch prog args progInput

Example:

*Shell> (status, out, err) <- launch "tr" ["a-z", "A-Z"] 
				    (unlines (replicate 10000 "Haskell"))
*Shell> status
Exited ExitSuccess
*Shell> length out
80000
*Shell> mapM_ putStrLn (take 10 (lines out))
HASKELL
HASKELL
HASKELL
HASKELL
HASKELL
HASKELL
HASKELL
HASKELL
HASKELL
HASKELL

Best regards,
Tom

-- 
.signature: Too many levels of symbolic links
-------------- next part --------------

module Shell where

import System.Posix.Process
import System.Posix.IO
import Control.Concurrent
import IO

launch :: String -> [String] -> String -> IO (ProcessStatus, String, String)
launch prog args inputStr = do
    (childIn, parentIn) <- createPipe
    (parentOut, childOut) <- createPipe
    (parentErr, childErr) <- createPipe

    forkProcess >>= \pid -> case pid of
	Nothing -> do -- child
	    closeFd parentIn
	    closeFd parentOut
	    closeFd parentErr
	    closeFd 0 -- FIXME: What if some of 0,1,2 are already closed?
	    closeFd 1
	    closeFd 2
	    childIn `dupTo` 0
	    childOut `dupTo` 1
	    childErr `dupTo` 2
	    closeFd childIn
	    closeFd childOut
	    closeFd childErr
	    executeFile prog True args Nothing
	    fail "launch: executeFile failed"

	Just child -> do -- parent
	    closeFd childIn
	    closeFd childOut
	    closeFd childErr

	    input <- fdToHandle parentIn
	    output <- fdToHandle parentOut
	    err <- fdToHandle parentErr

	    outputCS <- hGetContents output
	    errCS <- hGetContents err

	    outputMV <- newEmptyMVar
	    errMV <- newEmptyMVar
	    inputMV <- newEmptyMVar

	    forkIO $ hPutStr input inputStr >> hClose input >> putMVar inputMV ()
	    forkIO $ foldr seq () outputCS `seq` hClose output >> putMVar outputMV ()
	    forkIO $ foldr seq () errCS `seq` hClose err >> putMVar errMV ()

	    takeMVar outputMV
	    takeMVar errMV
	    takeMVar inputMV

	    mStatus <- getProcessStatus True False child

	    case mStatus of
		Nothing -> fail "launch: can't get child process status"
		Just stat -> return (stat, outputCS, errCS)



More information about the Glasgow-haskell-users mailing list