
Hi, there's something I don't get about interaction among OS processes and Haskell handles/channels. Suppose I have a very small program that takes a line and prints it till you write "quit": main = do s <- getLine case s of "quit" -> putStrLn "quitting" >> return () _ -> loop s where loop s = do putStrLn s main This is a small interactive process I would like to talk to from another Haskell program, like the following one which, indeed, is just a wrapper around the first. Now, if I write a single line with "quit", I get "quitting" back, otherwise the program doesn't work. I think I need some direction in order to understand how handles work. The same with channels, I'm afraid. Could you please point me in the right direction? Thanks for your kind attention. Andrea The not working code: import Control.Concurrent import System.Process import System.IO main = do c <- runInteractiveCommand "./main2" loop c loop c@(i,o,e,p) = do s <- getLine hPutStrLn i s hFlush i -- now "i" is closed, right? s' <- hGetLine o putStrLn s' loop c

On 2007-08-30, Andrea Rossato
Hi,
there's something I don't get about interaction among OS processes and Haskell handles/channels.
This looks like a buffering problem. You might be able to make it work by explicitly setting "line buffering" with hSetBuffering -- Aaron Denney -><-

On Fri, Aug 31, 2007 at 12:23:42AM +0000, Aaron Denney wrote:
On 2007-08-30, Andrea Rossato
wrote: Hi,
there's something I don't get about interaction among OS processes and Haskell handles/channels.
This looks like a buffering problem. You might be able to make it work by explicitly setting "line buffering" with hSetBuffering
Thanks for your kind attention, but I don't think it's a matter of buffering (I indeed tried playing with hSetBuffering with no results). As I said I'm not even sure if what I intend to do can actually be done...;-) Basically I'd like to write the equivalent of "expect", which talks to interactive programs (with the difference the mine is supposed to talk to a program I wrote, so I don't need to embed a domain specific language in it). Since I don't know all this handle/channel/process interaction stuff I'm leaning towards some other kind of server/client type of interaction, probably with sockets...:-( Thanks again. Andrea

On Fri, Aug 31, 2007 at 11:31:38AM +0200, Andrea Rossato wrote:
Thanks for your kind attention, but I don't think it's a matter of buffering (I indeed tried playing with hSetBuffering with no results).
That is because you need to change output buffering on both ends. I don't know about haskell, but stdio says that handles to tty's are line-buffered, stderr is not buffered and all other handles are fully buffered. Of course you can flush the handles instead.
Basically I'd like to write the equivalent of "expect", which talks to interactive programs (with the difference the mine is supposed to talk to a program I wrote, so I don't need to embed a domain specific language in it).
AFAIR expect uses pseudo-ttys (ptys) because in general one can't force a program to change it's output buffering mode, so forcing it to talk to a pair of terminals instead of fifos is used as a workaround. Have a look at pty(7). Cheers, -- 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 2007-08-31, Andrea Rossato
On Fri, Aug 31, 2007 at 12:23:42AM +0000, Aaron Denney wrote:
On 2007-08-30, Andrea Rossato
wrote: Hi,
there's something I don't get about interaction among OS processes and Haskell handles/channels.
This looks like a buffering problem. You might be able to make it work by explicitly setting "line buffering" with hSetBuffering
Thanks for your kind attention, but I don't think it's a matter of buffering (I indeed tried playing with hSetBuffering with no results).
I'd like to convince you otherwise: changing main1 to main = do (i,o,e,p) <- runInteractiveCommand "./main2" hSetBuffering i LineBuffering hSetBuffering o LineBuffering loop (i,o,e,p) loop c@(i,o,e,p) = do s <- getLine hPutStrLn i s hFlush i -- now "i" is closed, right? s' <- hGetLine o putStrLn s' loop c and main2 to import System.IO main = do hSetBuffering stdin LineBuffering hSetBuffering stdout LineBuffering s <- getLine case s of "quit" -> putStrLn "quitting" >> return () _ -> loop s where loop s = do putStrLn s main seems to work for me. If you want "expect" like functionality, i.e. working for arbitrary client programs, you'll need to use pseudottys, as expect, script, screen, xterm, etc. do. Unfortunately, the exact details of how to do this vary from unix to unix, and as far as I know, have not been packaged up nicely as a Haskell library. You will have to use the FFI. -- Aaron Denney -><-

Aaron Denney wrote:
If you want "expect" like functionality, i.e. working for arbitrary client programs, you'll need to use pseudottys, as expect, script, screen, xterm, etc. do.
I packaged up a patch for System.Posix to add this a month or three ago, but forgot to follow through on it. Thanks for the (albeit indirect) reminder :-)

On Fri, Sep 14, 2007 at 01:55:32AM +0000, Aaron Denney wrote:
On 2007-08-31, Andrea Rossato
wrote: Thanks for your kind attention, but I don't think it's a matter of buffering (I indeed tried playing with hSetBuffering with no results).
I'd like to convince you otherwise:
Sorry if I came back so late, but I wanted to thank you for taking the time to show me I was making not one, but two errors. Yes indeed, you are right, if I have access to the buffering of both processes I can send messages from one to the other. Now I start grasping something more about buffering and terminals. Thank you. Andrea

Andrea Rossato wrote:
loop s = do putStrLn s
Most likely, the content of s sits in a local buffer and never leaves this process, following most OS conventions and as others point out. Another process waiting for it will deadlock. Most similar process deadlock problems are not specific to Haskell or even relevant to Haskell; they are misunderstandings of the underneath OS. I recommend every Haskell programmer to take an in-depth Unix course.

On Sat, Sep 01, 2007 at 09:12:30PM -0400, Albert Y. C. Lai wrote:
Andrea Rossato wrote:
loop s = do putStrLn s
Most likely, the content of s sits in a local buffer and never leaves this process, following most OS conventions and as others point out. Another process waiting for it will deadlock.
Most similar process deadlock problems are not specific to Haskell or even relevant to Haskell; they are misunderstandings of the underneath OS. I recommend every Haskell programmer to take an in-depth Unix course.
Yes, I knew it was something related to the underneath OS. I'll have to study Unix seriously.... Thanks you guys for your kind attention. Andrea

Andrea Rossato wrote:
Most likely, the content of s sits in a local buffer and never leaves this process, following most OS conventions and as others point out. Another process waiting for it will deadlock.
Yes, I knew it was something related to the underneath OS. I'll have to study Unix seriously....
Your problem may be buffering-related (I haven't read your code to check), but if so, there's a fair likelihood that it has nothing to do with the OS. GHC's runtime does its own buffer management on Handles. It's quite possible that your deadlock lies at that level, rather than anything lower. Are you calling hFlush after writing to your pipe?

Bryan O'Sullivan wrote:
Your problem may be buffering-related (I haven't read your code to check), but if so, there's a fair likelihood that it has nothing to do with the OS. GHC's runtime does its own buffer management on Handles. It's quite possible that your deadlock lies at that level, rather than anything lower.
Although GHC's runtime kind of re-invents buffering, it still follows the Unix convention, i.e., if stdio is determined to be attached to a tty, then default to line buffering, else default to block buffering. Indeed, I wager that it bothers to re-invent buffering because of some other technical necessity (green-threading comes to mind), and if it goes out of its way to re-invent buffering, why, of all conceivable conventions, the Unix convention again? E.g., why not take this opportunity to spare beginners a nasty surprise and default to line buffering across the board? It seems to me clearly that the answer is precisely to spare the Unix-informed programmers a nasty surprise. Therefore although GHC's runtime does its own buffering, a Unix education still informs you of what it does. The problem is the same and the solution is the same. Even taking a step back, even if GHC or some other Haskell runtimes or some other language runtimes or even other VMs and OSes do not follow the Unix convention, a Unix course still serves to alert you of buffering issues, that buffering can be weird, that the OS does its weird buffering, that a language runtime may or may not add yet its weird buffering... You will develop a habit of double-checking with the docs and testing, not a habit of just assuming that what you see on a tty is what you get on a pipeline. Along the way, you will also see why getChar waits for a newline (and why sometimes even a newline doesn't suffice)... It is similar to saying, if you use a high-level language on x86, you don't have to learn low-level 680x0. Ah, but knowing low-level 680x0 informs you of certain issues of the high-level language (e.g., performance) and how to use it more successfully. This is despite 680x0 is not the x86 you use. It is similar to saying, if you use Haskell, you don't have to learn dependent typing. Ah, but knowing dependent typing informs you of certain typing issues and how to use the Haskell type system more successfully. This is despite tutorials on dependent typing talk about Clean or Coq rather than the Haskell you use. It is similar to saying, if you use Java and C#, you don't have to learn Haskell. Ah, but knowing Haskell informs you of certain programming issues and how to use Java and C# more successfully. This is despite Haskell does not talk about objects. It is education and personal growth. It is not just vocational training.

Albert Y. C. Lai wrote:
It is similar to saying, if you use Haskell, you don't have to learn dependent typing. Ah, but knowing dependent typing informs you of certain typing issues and how to use the Haskell type system more successfully. This is despite tutorials on dependent typing talk about Clean or Coq rather than the Haskell you use.
s/Clean/Cayenne/
participants (5)
-
Aaron Denney
-
Albert Y. C. Lai
-
Andrea Rossato
-
Bryan O'Sullivan
-
Krzysztof Kościuszkiewicz