Hello,
After many hours of vain debugging, I suspect that GHC might not do what I
think it should do.
I made a module 'Telnet', exporting one function 'telnet', which takes a
handle (an established connection) and returns an input channel, an output
channel and a function to close the connection and clean up.
The 'telnet' function creates two channels (input and output), one MVar and
two threads:
* One thread takes chars from the output channel and writes them into
the handle.
* The other thread reads chars from the handle and puts them in the
input channel.
* The MVar regulates writing access to the handle: the reader thread
sometimes has to respond to telnet commands, but those responses must not
be interspersed with ordinary data.
Now, if I let one side of the connection send some characters:
sequence_ $ take 10 $ repeat $ (writeChan outputChan 's')
and I let the other side consume them:
sequence_ $ take 10 $ repeat $ (putChar = readChan inputChan)
then the characters are sent and received correctly, but the receiving side
shows the characters only after the connection is closed.
If I let both sides send and consume characters in turn, like this:
sequence_ $ take 10 $ repeat $ threadDelay 250 (writeChan outputChan
's') (putChar = readChan inputChan)
and
sequence_ $ take 10 $ repeat $ (putChar = readChan inputChan)
threadDelay 250 (writeChan outputChan 'c')
, one character is sent, and the other end waits indefinitely before
sending his character.
Maybe some buffering mechanism is holding my characters?
I already tried hFlush and setting buffering to NoBuffering on all handles.
Could it have anything to with this?
One final note: the example may not work too well on GHC (see
Scheduling, above), due to the locking on a Handle. Only one thread may
hold the lock on a Handle at any one time, so if a reschedule happens while
a thread is holding the lock, the other thread won't be able to run. The
upshot is that the switch from to b happens infrequently. It can
be improved by lowering the reschedule tick period. We also have a patch
that causes a reschedule whenever a thread waiting on a lock is woken up,
but haven't found it to be useful for anything other than this example :-)
(http://haskell.org/ghc/docs/latest/html/libraries/base/Control.Concurrent.html#11)
I tried all this both in compiled and interactive mode with GHC 6.0 and 6.2
on Windows 2000.
Thanks for your time.
Arie Peterson
Telnet.hs, with many superfluous debugging output:
###
module Telnet (telnet) where
import Control.Concurrent (forkIO,killThread)
import Control.Concurrent.Chan (Chan,newChan,readChan,writeChan)
import Control.Concurrent.MVar (MVar,newMVar,takeMVar,putMVar)
import System.IO (Handle,hGetChar,hPutChar)
telnet :: Handle - IO (Chan Char,Chan Char,IO ())
telnet handle = do
inputChan - newChan
outputChan - newChan
writing - newMVar ()
readerId - forkIO $ reader handle inputChan writing
writerId - forkIO $ writer handle outputChan writing
return
(
inputChan,
outputChan,
do -- function to close connection
takeMVar writing
hPutChar handle '\255' -- IAC
hPutChar handle '\244' -- IP (Interrupt Process)
killThread readerId
killThread writerId
)
reader :: Handle - Chan Char - MVar () - IO ()
reader handle inputChan writing = sequence_ . repeat $ do
putStrLn Telnet.reader: waiting for char in handle
c - hGetChar handle
putStrLn (Telnet.reader: received ++ show c)
case c of
'\255' - hGetChar handle = \c - putStrLn (Telnet.reader: received
++ show c) case c of
'\255' - writeChan inputChan '\255' -- escaped IAC
'\254' - respond '\253' -- received DONT, send WONT
'\253' - respond '\254' -- received WONT, send DONT
'\252' - respond '\253' -- received DO, send WONT
'\251' - respond '\254' -- received WILL, send DONT
_ - return () -- received unknown command, ignore
c - writeChan inputChan c
where
respond c = do
d - hGetChar handle
putStrLn (Telnet.reader: received ++ show d ++ , responding)
takeMVar writing
hPutChar handle '\255'
hPutChar handle c
hPutChar handle d
putMVar writing ()
writer :: Handle - Chan Char - MVar () - IO ()
writer handle outputChan writing = sequence_ . repeat $ do
putStrLn Telnet.writer: waiting for character in chan
c - readChan outputChan
putStrLn (Telnet.writer: going to write: ++ show c)
takeMVar writing
case c of
'\255' - hPutChar handle '\255' hPutChar handle '\255' -- escaped IAC
_ - hPutChar handle c
putMVar writing ()
putStrLn (Telnet.writer: written: ++ show c)
___
Glasgow-haskell-users mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users