getChar + System.Cmd.system + threads causes hangups

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

getChar + System.Cmd.system + threads causes hangups

Einar Karttunen
Hello

Using system or any variant of it from System.Process
seems broken in multithreaded environments. This
example will fail with and without -threaded.

When run the program will print "hello: start" and
then freeze. After pressing enter (the first getChar)
System.Cmd.system will complete, but without that
it will freeze for all eternity.

What is the best way to fix this? I could use System.Posix,
but that would lose windows portablity which is needed.


import Control.Concurrent
import System.Cmd

main = do forkIO (threadDelay 100000 >> hello)
          getChar
          getChar

hello = do putStrLn "hello: start"
           system "echo hello world!"
           putStrLn "hello: done"


- Einar Karttunen
_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: getChar + System.Cmd.system + threads causes hangups

Einar Karttunen
Here is a version that works fine:


myRawSystem cmd args = do
    (inP, outP, errP, pid) <- runInteractiveProcess cmd args Nothing Nothing
    hClose inP
    os <- pGetContents outP
    es <- pGetContents errP
    ec <- waitForProcess pid
    case ec of
      ExitSuccess   -> return ()
      ExitFailure e ->
          do hPutStrLn stderr ("Running process "++unwords (cmd:args)++" FAILED ("++show e++")")
             hPutStrLn stderr os
             hPutStrLn stderr es
             hPutStrLn stderr ("Raising error...")
             fail "Running external command failed"

pGetContents h = do
    mv <- newEmptyMVar
    let put [] = putMVar mv []
        put xs = last xs `seq` putMVar mv xs
    forkIO (hGetContents h >>= put)
    takeMVar mv

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: getChar + System.Cmd.system + threads causes hangups

Simon Marlow-5
In reply to this post by Einar Karttunen
Einar Karttunen wrote:

> Hello
>
> Using system or any variant of it from System.Process
> seems broken in multithreaded environments. This
> example will fail with and without -threaded.
>
> When run the program will print "hello: start" and
> then freeze. After pressing enter (the first getChar)
> System.Cmd.system will complete, but without that
> it will freeze for all eternity.
>
> What is the best way to fix this? I could use System.Posix,
> but that would lose windows portablity which is needed.
>
> import Control.Concurrent
> import System.Cmd
>
> main = do forkIO (threadDelay 100000 >> hello)
>           getChar
>           getChar
>
> hello = do putStrLn "hello: start"
>            system "echo hello world!"
>            putStrLn "hello: done"

The reason for the deadlock is because getChar is holding a lock on
stdin, and System.Cmd.system needs to access the stdin Handle in order
to know which file descriptor to dup as stdin in the child process (the
stdin Handle isn't always FD 0, because of hDuplicateTo).

Maybe getChar shouldn't hold the lock while it is waiting.  I was
vaguely aware of this when I wrote System.IO, but couldn't see an easy
way to implement it, so currently all operations that block in I/O hold
the Handle lock while they block.  Mostly this isn't a problem, but it
does mean that things like hClose will block if there's another thread
blocked in hGetChar on the same Handle (maybe you want it to cause the
hGetChar to immediately fail instead).

One way to work around it in this case is to hDuplicate the standard
Handles, and call runProcess passing your duplicate Handles.  I've just
checked; this works fine.

import GHC.Handle (hDuplicate)

main = do
  i <- hDuplicate stdin
  o <- hDuplicate stdout
  e <- hDuplicate stderr
  forkIO (threadDelay 100000 >> hello i o e)
  getChar
  getChar

hello i o e = do
   putStrLn "hello: start"
   p <- runProcess "echo" ["hello world!"] Nothing Nothing (Just i)
(Just o) (Just e)
   waitForProcess p
   putStrLn "hello: done"

Cheers,
        Simon
_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Re: getChar + System.Cmd.system + threads causes hangups

Donn Cave-2
On Tue, 21 Feb 2006, Simon Marlow wrote:
...
> The reason for the deadlock is because getChar is holding a lock on
> stdin, and System.Cmd.system needs to access the stdin Handle in order
> to know which file descriptor to dup as stdin in the child process (the
> stdin Handle isn't always FD 0, because of hDuplicateTo).

I was puzzled by this;  from a quick review of the source, it seems
that this is because system calls runProcessPosix, which has optional
arguments for (stdin, stdout, stderr) for the child process, and when
value is Nothing the parent's stdin etc. are used instead.

That last part doesn't seem right to me, so if it's awkward to implement,
I hope you will consider the possibility that no one needs it.  The
default value for fd 0 should be fd 0.  A process that intends to change
the default input stream on this level where the same stream should be
inherited by children, can open or dup another file onto fd 0.  Conversely,
if a program contrives to change stdin to something besides fd 0, I would
have assumed the intent was to avoid any affect on child processes.

        Donn Cave, [hidden email]

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: getChar + System.Cmd.system + threads causes hangups

Simon Marlow-5
Donn Cave wrote:

> On Tue, 21 Feb 2006, Simon Marlow wrote:
> ....
>
>>The reason for the deadlock is because getChar is holding a lock on
>>stdin, and System.Cmd.system needs to access the stdin Handle in order
>>to know which file descriptor to dup as stdin in the child process (the
>>stdin Handle isn't always FD 0, because of hDuplicateTo).
>
>
> I was puzzled by this;  from a quick review of the source, it seems
> that this is because system calls runProcessPosix, which has optional
> arguments for (stdin, stdout, stderr) for the child process, and when
> value is Nothing the parent's stdin etc. are used instead.
>
> That last part doesn't seem right to me, so if it's awkward to implement,
> I hope you will consider the possibility that no one needs it.  The
> default value for fd 0 should be fd 0.  A process that intends to change
> the default input stream on this level where the same stream should be
> inherited by children, can open or dup another file onto fd 0.  Conversely,
> if a program contrives to change stdin to something besides fd 0, I would
> have assumed the intent was to avoid any affect on child processes.

That's certainly a reasonable point of view.  Currently the semantics
are such that you can say

   h <- openFile "out" WriteMode
   hDuplicateTo h stdout
   system "echo \"hello\""

and the output from the echo command will go to the file "out".

I'm perfectly willing to accept that supporting this behaviour is of
limited usefulness, and even if you need it then you can call system the
long way using runProcess.

Anyone else have any opinions on this?

Cheers,
        Simon

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe