Dear GHC team, I am testing the IO operations of GHC with the Unix named pipes (in ghc-7.01 under Debian Linux). In the below example, the pipe pair are created by > mkfifo toA > mkfifo fromA, `main' in Main.hs opens toA for writing, opens fromA for reading, outputs "A1" to toA. The C program fifo2 opens toA for reading, opens fromA for writing, inputs a string from toA, prints "done" to the screen (fromA is not really used, but will be needed in future). The effect is that fifo2 (on terminal-2) hangs silent, while Main.hs (on terminal-1) does its part. If I remove the fopen(..fromA..) call in fifo2.c, then it runs as expected (after this example is fixed, fifo2 will need to write to fromA). Main.hs is built by ghc --make Main, The C program is built by gcc -o fifo2 fifo2.c First, ./fifo2 is run on terminal-2, then ./Main is run on terminal-1. The source is quite small: -- Main.hs --------------------------------------------------------- import System.IO (IOMode(..), IO(..), Handle, openFile, hPutStr, hFlush) dir = showString "/home/mechvel/ghc/notes/npipe/" toA_IO = openFile (dir "toA") WriteMode :: IO Handle fromA_IO = openFile (dir "fromA") ReadMode axiomIO :: Handle -> String -> IO() axiomIO h str = do hPutStr h str hFlush h putStr "hFlush done\n" main = do h <- toA_IO putStr "str1 --> " axiomIO h "A1\n" ----------------------- fifo2.c ------------------------------------- #include <stdio.h> #include <string.h> #define BOUND 64 static char str[BOUND]; main() { int l, i; FILE *toA, *fromA; toA = fopen("/home/mechvel/ghc/notes/npipe/toA", "r"); if (toA == NULL) {perror("fopen(toA, r) failed: "); return;}; fromA = fopen("/home/mechvel/ghc/notes/npipe/fromA", "w"); // ! if (fgets(str, BOUND, toA) == NULL) { perror("fgets(str, bound, toA) failed: "); return; }; printf("input = %s\n", str); printf("done\n"); return; } --------------------------------------------------------------------- Can you, please, explain the effect? Regards, ------ Sergei mechvel@botik.ru
Main.hs does not open fromA at all. (fromA_IO is dead code.) This causes fifo2.c to be hung whenever it opens fromA. From the man page of mkfifo(3) on Linux: "Opening a FIFO for reading normally blocks until some other process opens the same FIFO for writing, and vice versa. See fifo(7) for nonblocking handling of FIFO special files." In the sister thread in haskell-cafe, the addition of unsafePerformIO throws further disorder into these highly order-sensitive operations.
To my
Dear GHC team, I am testing the IO operations of GHC with the Unix named pipes [..]
Albert Y. C. Lai writes on 19 Jan 2012
Main.hs does not open fromA at all. (fromA_IO is dead code.) This causes fifo2.c to be hung whenever it opens fromA. From the man page of mkfifo(3) on Linux: "Opening a FIFO for reading normally blocks until some other process opens the same FIFO for writing, and vice versa. See fifo(7) for nonblocking handling of FIFO special files."
I see now. Thank you. The intitial example was with a real usage of toA and fromA, and it did not work as expected. Then, I simplified it, but just missed the point that ./Main does not really open fromA. Now, I provide below the initial example. Can people explain, please, why it performs so?
In the sister thread in haskell-cafe, the addition of unsafePerformIO throws further disorder into these highly order-sensitive operations.
By the way, can you respond (if not to `glasgow' then to mechvel@botik.ru) to my last simple and concrete question to cafe on the subject of unsafePerformIO ? Anyway, the below example is free of unsafePerformIO. And I need to delay the remaining questions on unsafePerformIO (for myself and maybe, for `cafe') until the current example is fixed. Regards, ------ Sergei mechvel@botik.ru ******************************************************************** I am testing the IO operations of GHC with the Unix named pipes -- (in ghc-7.01 under Debian Linux). In the below example, the pipe pair are created by > mkfifo toA > mkfifo fromA, `main' in Main.hs opens toA for writing, opens fromA for reading, outputs "A1" to toA, inputs the respond string str' from fromA, prints str' to the screen. The C program fifo2 opens toA for reading, opens fromA for writing, inputs a string str from toA, converts str to the lower case, obtaining str' outputs str' to fromA. Main.hs is built by ghc --make Main, The C program is built by gcc -o fifo2 fifo2.c First, ./fifo2 is run on terminal-2, then ./Main is run on terminal-1. The effect is that fifo2 (on terminal-2) hangs silent, and Main.hs (on terminal-1) reports only "str1 --> hFlush done", and then hangs. This contradicts to that ./Main must print str' to the screen. The modules are as follows. -- Main.hs --------------------------------------------------------- import System.IO (IOMode(..), IO(..), Handle, openFile, hPutStr, hFlush, hGetLine) dir = showString "/home/mechvel/ghc/notes/npipe/" toA_IO = openFile (dir "toA") WriteMode :: IO Handle fromA_IO = openFile (dir "fromA") ReadMode axiomIO :: Handle -> Handle -> String -> IO String axiomIO h1 h2 str = do hPutStr h1 str hFlush h1 putStr "hFlush done\n" str' <- hGetLine h2 putStr (concat ["hGetLine -> ", str', "\n"]) return str' main = do h1 <- toA_IO h2 <- fromA_IO putStr "str1 --> " str1' <- axiomIO h1 h2 "A1" putStr (str1' ++ "\n") ----------------------- fifo2.c ------------------------------------- #include <stdio.h> #include <string.h> #define BOUND 64 static char str[BOUND]; main() { int l, i; FILE *toA, *fromA; toA = fopen("/home/mechvel/ghc/notes/npipe/toA", "r"); if (toA == NULL) {perror("fopen(toA, r) failed: "); return;}; fromA = fopen("/home/mechvel/ghc/notes/npipe/fromA", "w"); if (fromA == NULL) {perror("fopen(fromA, w) failed: "); return;}; if (fgets(str, BOUND, toA) == NULL) { perror("fgets(str, bound, toA) failed: "); return; }; printf("input = %s\n", str); l = strlen(str) - 1; i = 0; // convert the string to the lower case while (i < l) {str[i] = tolower(str[i]); i++;} printf("output = %s\n", str); fputs(str, fromA); fflush(fromA); printf("output to fromA done\n"); return; } --------------------------------------------------------------------- Comparison with C - C --------------------- If both programs are in C, then the whole loop of the string exchange (with fifo2.c rewritten into a loop) works as needed, both for the variants with fgets and with `read'.
On 12-01-19 02:16 PM, Serge D. Mechveliani wrote:
-- Main.hs --------------------------------------------------------- hPutStr h1 str hFlush h1
This fails to send a newline.
----------------------- fifo2.c ------------------------------------- if (fgets(str, BOUND, toA) == NULL) {
This refuses to finish until it sees a newline (or BOUND chars, or the eof condition, or an error condition). What really interests me is that if Main.hs is linked with -threaded, the deadlock is unlocked, and both programs proceed to completion. This is because the -threaded runtime goes through garbage collection when idling (such as waiting for hGetLine). Since there is no further reference to h1 by then, GC hCloses it. That unlocks the fgets side. This is fragile and not to be relied on. You will always have further references to h1 in practice.
Comparison with C - C --------------------- If both programs are in C, then the whole loop of the string exchange (with fifo2.c rewritten into a loop) works as needed, both for the variants with fgets and with `read'.
I am not convinced in the case of fgets. I cannot reproduce it. (Oh, I know read has a much better chance, it doesn't wait for a newline.)
participants (2)
-
Albert Y. C. Lai -
Serge D. Mechveliani