@@ -1,4 +1,4 @@
1−{-# LANGUAGE BangPatterns, CPP #-}
1+{-# LANGUAGE BangPatterns, CPP, ScopedTypeVariables #-}
3module Network.Transport.Pipes
3module Network.Transport.Pipes
4 ( mkTransport
4 ( mkTransport
@@ -6,7 +6,7 @@ module Network.Transport.Pipes
7import Control.Monad (when)
7import Control.Monad (when)
8import Control.Concurrent.MVar
8import Control.Concurrent.MVar
9−import Control.Concurrent (threadDelay)
9+import Control.Concurrent (threadDelay, forkOS)
10import Data.IntMap (IntMap)
10import Data.IntMap (IntMap)
11-- import Data.ByteString.Char8 (ByteString)
11-- import Data.ByteString.Char8 (ByteString)
12import Data.Word
12import Data.Word
@@ -19,12 +19,14 @@ import Data.Serialize (encode,decode)
19import Data.List (foldl')
19import Data.List (foldl')
20import Network.Transport
20import Network.Transport
21import System.Random (randomIO)
21import System.Random (randomIO)
22−import System.IO (IOMode(ReadMode,AppendMode), openFile, hClose)
22+import System.IO (IOMode(ReadMode,AppendMode,WriteMode,ReadWriteMode),
23+ openFile, hClose, hPutStrLn, hPutStr, stderr, stdout)
23import System.Posix.Files (createNamedPipe, unionFileModes, ownerReadMode, ownerWriteMode)
24import System.Posix.Files (createNamedPipe, unionFileModes, ownerReadMode, ownerWriteMode)
25-- Option 1: low level unix routines:
26-- Option 1: low level unix routines:
26-- Option 2: ByteString-provided IO routines
27-- Option 2: ByteString-provided IO routines
27−#if UNIXIO
29+#ifdef UNIXIO
28-- Added in unix-2.5.1.0. Let's not depend on this yet:
30-- Added in unix-2.5.1.0. Let's not depend on this yet:
29-- import qualified System.Posix.IO.ByteString as PIO
31-- import qualified System.Posix.IO.ByteString as PIO
30import qualified System.Posix.IO as PIO
32import qualified System.Posix.IO as PIO
@@ -47,16 +49,14 @@ mkTransport = do
47 lock <- newMVar ()
49 lock <- newMVar ()
48 let filename = "/tmp/pipe_"++show uid
50 let filename = "/tmp/pipe_"++show uid
49 createNamedPipe filename $ unionFileModes ownerReadMode ownerWriteMode
51 createNamedPipe filename $ unionFileModes ownerReadMode ownerWriteMode
51− pipe <- PIO.openFd filename PIO.ReadOnly Nothing PIO.defaultFileFlags
53+ dbgprint1$ " Created pipe at location: "++ filename
53− pipe <- openFile filename ReadMode
56 return Transport
55 return Transport
57 { newConnectionWith = \ _ -> do
56 { newConnectionWith = \ _ -> do
57+ dbgprint1$ " Creating new connection"
58 return (mkSourceAddr filename,
58 return (mkSourceAddr filename,
59− mkTargetEnd pipe lock)
59+ mkTargetEnd filename lock)
60 , newMulticastWith = error "Pipes.hs: newMulticastWith not implemented yet"
60 , newMulticastWith = error "Pipes.hs: newMulticastWith not implemented yet"
61 , deserialize = \bs -> return$ mkSourceAddr (BS.unpack bs)
61 , deserialize = \bs -> return$ mkSourceAddr (BS.unpack bs)
@@ -70,52 +70,85 @@ mkTransport = do
70 mkSourceEnd :: String -> IO SourceEnd
70 mkSourceEnd :: String -> IO SourceEnd
71 mkSourceEnd filename = do
71 mkSourceEnd filename = do
72-- fd <- openFile filename AppendMode
72-- fd <- openFile filename AppendMode
73+-- mv <- onOSThread$ openFile filename WriteMode
74 -- Write to the named pipe. If the message is less than
75 -- Write to the named pipe. If the message is less than
75 -- PIPE_BUF (4KB on linux) then this should be atomic, otherwise
76 -- PIPE_BUF (4KB on linux) then this should be atomic, otherwise
76 -- we have to do something more sophisticated.
77 -- we have to do something more sophisticated.
78 { send = \bss -> do
79 { send = \bss -> do
79− putStrLn$ "SENDING ... "++ show bss
80+ dbgprint1$ "SENDING ... "++ show bss
81 -- This may happen on multiple processes/threads:
82 -- This may happen on multiple processes/threads:
82− let msgsize = foldl' (\n s -> n + BS.length s) 0 bss
83+ let msgsize :: Word32 = fromIntegral$ foldl' (\n s -> n + BS.length s) 0 bss
83 when (msgsize > 4096)$ -- TODO, look up PIPE_BUF in foreign code
84 when (msgsize > 4096)$ -- TODO, look up PIPE_BUF in foreign code
84 error "Message larger than blocksize written atomically to a named pipe. Unimplemented."
85 error "Message larger than blocksize written atomically to a named pipe. Unimplemented."
85 -- Otherwise it's just a simple write:
86 -- Otherwise it's just a simple write:
86 -- We append the length as a header. TODO - REMOVE EXTRA COPY HERE:
87 -- We append the length as a header. TODO - REMOVE EXTRA COPY HERE:
90+ dbgprint1$ " (sending... got file descriptor) "
87− fd <- openFile filename AppendMode
91+-- fd <- openFile filename AppendMode
92+ fd <- openFile filename WriteMode
94+-- TODO: Consider nonblocking opening of file to make things simpler.
88− BS.hPut fd (BS.concat (encode msgsize : bss))
96+ let finalmsg = BS.concat (encode msgsize : bss)
97+ dbgprint1$ " Final send msg: " ++ show finalmsg
89 hClose fd -- TEMP -- opening and closing on each send!
99 hClose fd -- TEMP -- opening and closing on each send!
92-- mkTargetEnd :: Fd -> MVar () -> TargetEnd
102-- mkTargetEnd :: Fd -> MVar () -> TargetEnd
93− mkTargetEnd fd lock = TargetEnd
103+ mkTargetEnd filename lock = TargetEnd
94 { receive = do
104 { receive = do
105+ dbgprint2$ "Begin receive action..."
95 -- This should only happen on a single process. But it may
106 -- This should only happen on a single process. But it may
96 -- happen on multiple threads so we grab a lock.
107 -- happen on multiple threads so we grab a lock.
97 takeMVar lock
108 takeMVar lock
98#ifdef UNIXIO
109#ifdef UNIXIO
99− (bytes,cnt) <- PIO.fdRead fd sizeof_header
110+-- fd <- PIO.openFd filename PIO.ReadOnly Nothing PIO.defaultFileFlags
111+ fd <- PIO.openFd filename PIO.ReadWrite Nothing PIO.defaultFileFlags
112+ let oneread n = do (s,cnt) <- PIO.fdRead fd (fromIntegral n)
113+ return (BS.pack s, fromIntegral cnt)
115+ fd <- openFile filename ReadMode
116+ let oneread n = do bs <- BS.hGet fd n;
117+ return (bs, BS.length bs)
101− putStrLn$ " Attempt read header..."
119+ dbgprint2$ " Attempt read header..."
103− let rdloop = do
121+ let spinread :: Int -> IO BS.ByteString
104− putStr "."
122+ spinread desired = do
105− hdr <- BS.hGet fd sizeof_header
123+-- hPutStr stderr "."
124+ (bytes,len) <- oneread desired
106− case BS.length hdr of
125+ case len of
107− n | n == sizeof_header -> return hdr
126+ n | n == desired -> return bytes
108 0 -> do threadDelay (10*1000)
127 0 -> do threadDelay (10*1000)
109− rdloop
128+ spinread desired
110− l -> error$ "Inclomplete read of msg header, only "++ show l ++ " bytes"
129+ l -> error$ "Inclomplete read expected either 0 bytes or complete msg ("++
111− hdr <- rdloop
130+ show desired ++" bytes) got "++ show l ++ " bytes"
132+ hdr <- spinread sizeof_header
112− putStrLn$ " Got header "++ show hdr ++ " attempt read payload"
133+ dbgprint2$ " Got header "++ show hdr ++ " attempt read payload"
113− payload <- case decode hdr of
134+ payload <- case decode hdr of
114− Left err -> error$ "ERROR: "++ err
135+ Left err -> error$ "ERROR: "++ err
115− Right size -> BS.hGet fd size
136+ Right size -> spinread (fromIntegral (size::Word32))
116− putStrLn$ " Got payload "++ show payload
137+ dbgprint2$ " Got payload "++ show payload
118 putMVar lock ()
139 putMVar lock ()
119 return [payload]
140 return [payload]
143+-- dbgprint2 = hPutStrLn stdout
144+-- dbgprint1 = hPutStrLn stderr
145+dbgprint1 _ = return ()
146+dbgprint2 _ = return ()
149+-- Execute an action on its own OS thread. Return an MVar to synchronize on.
150+onOSThread :: IO a -> IO (MVar a)
151+onOSThread action = do
153+ forkOS (action >>= putMVar mv )