Strange behavior of executeFile

Fellow Haskellers, I wrote a small script that intercepts arguments and exec's the pstops program. The intention was to center and scale pages in a document before processing it by psnup. So far so good, I've ended up with something like:
runPstops :: [Flag] -> IO () runPstops flags = do let args = mkArgs flags when (isVerbose flags) $ do hPutStrLn stderr $ "pstops " ++ unwords (map show args) executeFile "pstops" True args Nothing
main = do (opts, _) <- getOptions =<< getArgs runPstops opts
This works for files, but "randomly" fails when stdin is connected to a pipe (pstops complains that it can't seek input). I've tested "raw" pstops with pipes, files and /dev/null and it never fails, so I guess there is something wrong with my code. Can anyone enlighten me in this matter? :) Regards, -- Krzysztof Kościuszkiewicz Skype: dr.vee, Gadu: 111851, Jabber: kokr@jabberpl.org Phone IRL: +353851383329, Phone PL: +48783303040 "Simplicity is the ultimate sophistication" -- Leonardo da Vinci

Krzysztof Kościuszkiewicz wrote:
This works for files, but "randomly" fails when stdin is connected to a pipe (pstops complains that it can't seek input).
GHC's file handles are backed by non-blocking file descriptors. The child process run by executeFile inherits the stdin, stdout and stderr file descriptors of your Haskell process, so they're unexpectedly (from its perspective) in non-blocking mode. Due to POSIX sharing semantics, you can't simply switch those file descriptors to blocking in the child, because they'll then become blocking in the parent, too. Anything involving sharing file descriptors between processes becomes similarly broken if the GHC runtime starts using a file descriptor as a Handle. You're not the only one to be surprised by this behaviour, but unfortunately it's not trivial to work around. Simon Marlow was going to look into this problem a few months ago, but I don't know if he's had a chance to.

On Sun, Jul 29, 2007 at 10:34:10AM -0700, Bryan O'Sullivan wrote:
GHC's file handles are backed by non-blocking file descriptors. The child process run by executeFile inherits the stdin, stdout and stderr file descriptors of your Haskell process, so they're unexpectedly (from its perspective) in non-blocking mode.
Due to POSIX sharing semantics, you can't simply switch those file descriptors to blocking in the child, because they'll then become blocking in the parent, too.
Yes, this would explain the behavior I'm seeing. My script neither forks nor reads stdin, so I could hack around this problem by clearing the O_NONBLOCK flag:
setFdOption stdInput NonBlockingRead False
Thanks for your help! Regards, -- Krzysztof Kościuszkiewicz Skype: dr.vee, Gadu: 111851, Jabber: kokr@jabberpl.org Mobile IRL: +353851383329, Mobile PL: +48783303040 "Simplicity is the ultimate sophistication" -- Leonardo da Vinci

On Sun, Jul 29, 2007 at 10:34:10AM -0700, Bryan O'Sullivan wrote:
Simon Marlow was going to look into this problem a few months ago, but I don't know if he's had a chance to.
It's fixed in the HEAD: http://hackage.haskell.org/trac/ghc/ticket/724 Thanks Ian

| Anything involving sharing file descriptors between processes becomes | similarly broken if the GHC runtime starts using a file descriptor as a | Handle. You're not the only one to be surprised by this behaviour, but | unfortunately it's not trivial to work around. | | Simon Marlow was going to look into this problem a few months ago, but I | don't know if he's had a chance to. File a Trac bug report? (If there isn't one already.) Simon
participants (4)
-
Bryan O'Sullivan
-
Ian Lynagh
-
Krzysztof Kościuszkiewicz
-
Simon Peyton-Jones