module Control.Concurrent.NextRef
( NextRef
, newNextRef
, takeNextRef
, readLast
, writeNextRef
, modifyNextRef
, close
, open
, status
, Status (..)
) where
import Control.Concurrent.STM
import Data.IORef
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative
#endif
data Status = Open | Closed
deriving (Show, Eq, Ord, Read, Enum, Bounded)
data NextRef a = NextRef
{ nrAccum :: IORef a
, nrNextValue :: TMVar a
, nrStatus :: TVar Status
}
newNextRef :: a -> IO (NextRef a)
newNextRef x = NextRef <$> newIORef x <*> newTMVarIO x <*> newTVarIO Open
takeNextRef :: NextRef a -> IO (Maybe a)
takeNextRef NextRef {..} = atomically $ readTVar nrStatus >>= \case
Closed -> return Nothing
Open -> Just <$> takeTMVar nrNextValue
update :: NextRef a -> a -> IO ()
update NextRef {..} !new = atomically $ readTVar nrStatus >>= \case
Closed -> return ()
Open -> do
tryTakeTMVar nrNextValue
putTMVar nrNextValue new
readLast :: NextRef a -> IO a
readLast NextRef {..} = readIORef nrAccum
tupleResult :: (a, b) -> (a, (a, b))
tupleResult (x, y) = (x, (x, y))
writeNextRef :: NextRef a -> a -> IO ()
writeNextRef nv@(NextRef {..}) newValue = do
writeIORef nrAccum newValue
update nv newValue
modifyNextRef :: NextRef a -> (a -> (a, b)) -> IO b
modifyNextRef nv@(NextRef {..}) f = do
(!newValue, !result) <- atomicModifyIORef' nrAccum $ tupleResult . f
update nv newValue
return result
close :: NextRef a -> IO ()
close NextRef {..} = atomically $ writeTVar nrStatus Closed
open :: NextRef a -> IO ()
open NextRef {..} = atomically $ writeTVar nrStatus Open
status :: NextRef a -> IO Status
status = atomically . readTVar . nrStatus