-- | Mechanism to get messages sent to a 'Handle' concurrently -- without getting them mixed. They are sent to the handle in the same -- order they are received by a /passer object/ (see 'Passer'), not -- sending a message before the previous message is sent completely. module Control.Concurrent.PostMessAge ( -- * Passer type Passer -- * Open/Close a passer , createPasser , closePasser -- * Send messages to the passer , postMessage -- * Check passer status , isPasserClosed , isPasserOpen ) where import Control.Monad (when,void) import Control.Concurrent import Control.Concurrent.MVar import Control.Concurrent.Chan import System.IO (Handle) data PasserStatus = Open | Closed -- | The 'Passer' is the object that you send the messages to. -- It will redirect this message to its attached 'Handle', -- making sure the messages are not intercalated. -- Use 'postMessage' to send message to a passer object. data Passer a = Passer { passerStatus :: MVar PasserStatus , passerHandle :: Handle , passerChannel :: Chan (Maybe a) } -- | Passer object feeder loop. feedHandle :: (Handle -> a -> IO ()) -> Passer a -> IO () feedHandle f p = loop where ch = passerChannel p h = passerHandle p loop = do mx <- readChan ch case mx of Nothing -> return () Just x -> f h x >> loop -- | Check if a passer object is closed. When a passer object -- is closed, it won't send any more messages to its attached -- handle. This does not mean the handle itself is closed. isPasserClosed :: Passer a -> IO Bool isPasserClosed p = do st <- readMVar $ passerStatus p return $ case st of Closed -> True _ -> False -- | Check if a passer object is open. While a passer object -- is open, all the messages received by the passer are -- sent to its attached handle. isPasserOpen :: Passer a -> IO Bool isPasserOpen = fmap not . isPasserClosed -- | Send a message to a passer object. It returns a value -- indicating if the message reached the passer object. postMessage :: Passer a -> a -> IO Bool postMessage p x = do b <- isPasserOpen p when b $ writeChan (passerChannel p) $ Just x return b -- | Close a passer object, so it won't receive any more messages -- in the future. Once a passer object is closed, it can't be -- opened again. If you want to reuse a handle, create another -- passer object with 'createPasser'. closePasser :: Passer a -> IO () closePasser p = do st <- swapMVar (passerStatus p) Closed case st of Closed -> return () Open -> writeChan (passerChannel p) Nothing -- | Create a passer object from a 'Handle' and a function to send -- values to that handle. createPasser :: Handle -- ^ Handle to use -> (Handle -> a -> IO ()) -- Put Handle operation -> IO (Passer a) createPasser h f = do stv <- newMVar Open ch <- newChan let p = Passer stv h ch _ <- forkIO $ feedHandle f p return p