Can't figure out source of race condition when using System.Process

Greetings Gentlemen (and Ladies), As part of my small and simple framework for making presentations in Haskell I have a module which, given some text, makes a .tex file and then converts it to .svg for loading into a cairo canvas. It features simple caching (performs md5 on the contents of the .tex file and uses that as the file name... if an .svg by that name exists, it'll get loaded rather than regenerated). Unfortunately, it also segfaults once in a while, probably indicating I have some kind of race condition ... but I can't figure out why. This is the only point in my code that I think I'm using any concurrency, although I'm compiling with ghc --make -threaded. Compiling without -threaded results in a deadlock. Ubuntu 8.10, 64-bit, GHC 6.8.2. Any advice? I'm attaching the module in question (79 lines). Sincerely, Rafal Kolanski.

2008/11/2 Rafal Kolanski
Unfortunately, it also segfaults once in a while, probably indicating I have some kind of race condition ... but I can't figure out why.
What is the "it" that segfaults? The Haskell program shouldn't, at least. That said, your code for reading from child processes is wrong. You should read all of a child's output strictly (i.e. hGetContents alone will not do the trick) before you wait for its exit status. Your current scheme reverses this order, and there is hence no guarantee that it will read anything from the defunct child process. There's some possibility that this might be the cause of your segfault, which to me would suggest the presence of a bug in the runtime system.

Bryan O'Sullivan wrote:
What is the "it" that segfaults? The Haskell program shouldn't, at least.
The Haskell program. It does so rarely, however, and I'm unable to reproduce it with any consistency, only enough to notice something is wrong with what I've written. Upon closer examination my code also returns an error exit code for subprocesses that should've succeeded (pdflatex, but once in a blue moon also pstoedit) similarly rarely.
That said, your code for reading from child processes is wrong. You should read all of a child's output strictly (i.e. hGetContents alone will not do the trick) before you wait for its exit status. Your current scheme reverses this order, and there is hence no guarantee that it will read anything from the defunct child process. There's some possibility that this might be the cause of your segfault, which to me would suggest the presence of a bug in the runtime system.
Well, if there's a bug in the runtime system, I'll try to make sure I can reproduce it before filing a report. Meanwhile, you are absolutely right. How would you propose I properly perform that sort of interaction? It's often useful to use external programs to do things for you by throwing data at them and reading (or ignoring) the result until they die. Is there a Haskell idiom for this? Overall, I find Haskell to be very nice. I just keep getting bitten by laziness, at least until I get used to it. Sincerely, Rafal Kolanski. PS. The file I attached is the only real place where concurrency happens in my code. The rest of the uses are simple ... when rendering a slide using cairo: slideNaive = do ... paintLatex "\\textbf{datatype} ref = Ref int $\\mid$ Null" 50 0 ... which calls: paintLatex text x y = do svg <- liftIO $ updateSVG text paintSVG svg 3 x y updateSVG is defined in SVGLatex.hs which I attached earlier, and the rest: paintSVG :: FilePath -> Double -> Double -> Double -> Render () paintSVG file scale' x y = do save svg <- liftIO $ svgNewFromFile file let (w,h) = svgGetSize svg translate x y scale scale' scale' moveTo 0 0 svgRender svg restore which invokes functions defined in: import Graphics.Rendering.Cairo import Graphics.Rendering.Cairo.SVG

Rafal Kolanski wrote:
Bryan O'Sullivan wrote:
What is the "it" that segfaults? The Haskell program shouldn't, at least.
The Haskell program. It does so rarely, however, and I'm unable to reproduce it with any consistency, only enough to notice something is wrong with what I've written. Upon closer examination my code also returns an error exit code for subprocesses that should've succeeded (pdflatex, but once in a blue moon also pstoedit) similarly rarely.
That said, your code for reading from child processes is wrong. You should read all of a child's output strictly (i.e. hGetContents alone will not do the trick) before you wait for its exit status. Your current scheme reverses this order, and there is hence no guarantee that it will read anything from the defunct child process. There's some possibility that this might be the cause of your segfault, which to me would suggest the presence of a bug in the runtime system.
Well, if there's a bug in the runtime system, I'll try to make sure I can reproduce it before filing a report.
Even if the bug only happens once every few runs, please report it anyway. (I've been chasing a lot of bugs like that recently :-). Cheers, Simon

On Mon, Nov 03, 2008 at 01:02:12AM +1100, Rafal Kolanski wrote:
Rafal Kolanski. -- Pass text to md5sum and drop the final " -" when returning hashMD5 text = do (inp,out,err,pid) <- runInteractiveProcess "md5sum" [] Nothing Nothing forkIO $ do hPutStrLn inp text hClose inp exit <- waitForProcess pid case exit of ExitFailure _ -> error "md5sum fail" _ -> return () [...]
Why do you use forkIO here? It's not necessary. The process will run in background on its own. ? It would help me tracking the problem down having a full compilable example.. Sincerly Marc Weber

On Sun, Nov 02, 2008 at 09:15:25PM +0100, Marc Weber wrote:
On Mon, Nov 03, 2008 at 01:02:12AM +1100, Rafal Kolanski wrote:
Rafal Kolanski. -- Pass text to md5sum and drop the final " -" when returning hashMD5 text = do (inp,out,err,pid) <- runInteractiveProcess "md5sum" [] Nothing Nothing forkIO $ do hPutStrLn inp text hClose inp exit <- waitForProcess pid case exit of ExitFailure _ -> error "md5sum fail" _ -> return () [...]
Why do you use forkIO here? It's not necessary. The process will run in background on its own. ?
It looks to me like this code requires a forkIO, not in the writing to inp, but rather in the reading of out and err. Otherwise, those buffers may fill up and your process will hang... -- David Roundy http://www.darcs.net

David Roundy wrote:
On Sun, Nov 02, 2008 at 09:15:25PM +0100, Marc Weber wrote:
On Mon, Nov 03, 2008 at 01:02:12AM +1100, Rafal Kolanski wrote:
Rafal Kolanski. [...] Why do you use forkIO here? It's not necessary. The process will run in background on its own. ?
It looks to me like this code requires a forkIO, not in the writing to inp, but rather in the reading of out and err. Otherwise, those buffers may fill up and your process will hang...
It seems to be as you say. Trying for a single-threaded solution to this problem always locked up the program, until I found someone's code snippet online using forkIO and extrapolated from that. Sincerely, Rafal Kolanski.

Rafal Kolanski wrote:
..., until I found someone's code snippet online ... and extrapolated from that.
Oh yes, I love that kind of programming. Hardly possible in other languages than Haskell. :-) -- Dr. Janis Voigtlaender http://wwwtcs.inf.tu-dresden.de/~voigt/ mailto:voigt@tcs.inf.tu-dresden.de
participants (6)
-
Bryan O'Sullivan
-
David Roundy
-
Janis Voigtlaender
-
Marc Weber
-
Rafal Kolanski
-
Simon Marlow