Waiting on multiple Chan-nels
Hi, How do you wait on multiple channels, but read only from one of them (the first that gets an entry)? Is there a library-function I missed which already does this? What do you think of this solution? Sebastian Setzer -------------------------------------------------------------- -- ghc chantest.hs -o chantest -- Usage: Enter "1", "2" or "0" to exit. import System.IO import Control.Concurrent import Control.Concurrent.Chan main :: IO() main = do chan1 <- newChan chan2 <- newChan c1Thread <- forkIO (printChan "c1" chan1) c2Thread <- forkIO (printChan "c2" chan2) c12Thread <- forkIO (printChannels "c12" [chan1, chan2]) mainloop chan1 chan2 mainloop :: Chan String -> Chan String -> IO () mainloop chan1 chan2 = do c <- getChar command c where -- chan1 and chan2 are in scope, here. go_on :: IO() go_on = mainloop chan1 chan2 command :: Char -> IO() command '0' = do print "done" command '1' = do writeChan chan1 "main: 1" go_on command '2' = do writeChan chan2 "main: 2" go_on command '\n' = do -- ignore go_on command c = do print ("illegal: " ++ [c]) go_on -- return microseconds seconds :: Int -> Int seconds = (* 1000000) printChan :: String -> (Chan String) -> IO() printChan name chan = do -- threadDelay (seconds 3) -- print (name ++ " ready") s <- readChan chan printString name s printChan name chan printChannels :: String -> [Chan String] -> IO() printChannels name channels = do -- select (zip channels (repeat (printString name))) select $ zip channels $ repeat $ printString name printChannels name channels printString :: String -> String -> IO () printString name s = print (name ++ ": " ++ s) select :: [(Chan a, a -> IO())] -> IO () select channel_handler_pairs = do threadDelay (seconds 3) print "TODO: implement select" {- TODO: implement select. - create MVar - For each element of the channel_handler_pairs - list, fork a thread. - wait on an MVar for the signal "I'm done" - kill all helper threads (just to free the resources. They wouldn't do any harm) In the helper-threads: - readChan - try to signal "I'm ready" on the MVar. If another thread was faster, unGetChan and exit - call the handler - signal "I'm done" and exit Problems: - If the Thread gets killed between readChan and unGetChan, the message is lost. - if another thread reads between readChan and unGetChan, its no fifo anymore (unlikely, because why would you want to read with multiple threads on the same Chan? - You'd use dupChan for that purpose. Unless you want every "event" to be handled only once. But then you would'nt use multiple threads, would you?) - "a" must be the same for every channel-handler-pair Thats easy: instead of channel-handler-tuples, put functions MVar -> IO() into the list and provide a nice-looking operator to create them - Probably inefficient (lots of helper threads) -}
Setzer, Sebastian (ext) wrote:
Hi, How do you wait on multiple channels, but read only from one of them (the first that gets an entry)? Is there a library-function I missed which already does this?
This is pretty hard with Control.Concurrent.Chan. They are simply not very easy to compose. STM is designed to be very easy to compose. (The first word of the title of the first paper - "Composable memory transactions", http://research.microsoft.com/%7Esimonpj/papers/stm/index.htm) If you can switch to TChan, you can use this function: waitAny :: [TChan a] -> STM a waitAny chans = msum (map readTChan chans) which needs import Control.Concurrent.STM.TChan (TChan, readTChan) import Control.Monad (msum) import Control.Monad.STM (STM) --also supplies instance MonadPlus STM Brandon
participants (2)
-
Brandon Moore -
Setzer, Sebastian (ext)