-- | -- Module : Simulation.Aivika.RealTime.Internal.Channel -- Copyright : Copyright (c) 2016, David Sorokin -- License : BSD3 -- Maintainer : David Sorokin -- Stability : experimental -- Tested with: GHC 8.0.1 -- -- This module defines a channel with fast checking procedure whether the channel is empty. -- module Simulation.Aivika.RealTime.Internal.Channel (Channel, newChannel, channelEmpty, readChannel, writeChannel, awaitChannel) where import Data.List import Data.IORef import Control.Concurrent.STM import Control.Monad -- | A channel. data Channel a = Channel { channelList :: TVar [a], channelListEmpty :: TVar Bool, channelListEmptyIO :: IORef Bool } -- | Create a new channel. newChannel :: IO (Channel a) newChannel = do list <- newTVarIO [] listEmpty <- newTVarIO True listEmptyIO <- newIORef True return Channel { channelList = list, channelListEmpty = listEmpty, channelListEmptyIO = listEmptyIO } -- | Test quickly whether the channel is empty. channelEmpty :: Channel a -> IO Bool channelEmpty ch = readIORef (channelListEmptyIO ch) -- | Read all data from the channel. readChannel :: Channel a -> IO [a] readChannel ch = do empty <- readIORef (channelListEmptyIO ch) if empty then return [] else do writeIORef (channelListEmptyIO ch) True xs <- atomically $ do xs <- readTVar (channelList ch) writeTVar (channelList ch) [] writeTVar (channelListEmpty ch) True return xs return (reverse xs) -- | Write the value in the channel. writeChannel :: Channel a -> a -> IO () writeChannel ch a = do atomically $ do xs <- readTVar (channelList ch) writeTVar (channelList ch) (a : xs) writeTVar (channelListEmpty ch) False writeIORef (channelListEmptyIO ch) False -- | Wait for data in the channel. awaitChannel :: Channel a -> IO () awaitChannel ch = atomically $ do empty <- readTVar (channelListEmpty ch) when empty retry