module Control.Concurrent.CachedIO (
cachedIO,
cachedIOWith,
cachedIO',
cachedIOWith'
) where
import Control.Concurrent.STM (atomically, newTVar, readTVar, writeTVar, retry, TVar)
import Control.Monad (join)
import Control.Monad.Catch (MonadCatch, onException)
import Control.Monad.IO.Class (liftIO, MonadIO)
import Data.Time.Clock (NominalDiffTime, addUTCTime, getCurrentTime, UTCTime)
data State a = Uninitialized | Initializing | Updating a | Fresh UTCTime a
cachedIO :: (MonadIO m, MonadIO t, MonadCatch t)
=> NominalDiffTime
-> t a
-> m (t a)
cachedIO :: forall (m :: * -> *) (t :: * -> *) a.
(MonadIO m, MonadIO t, MonadCatch t) =>
NominalDiffTime -> t a -> m (t a)
cachedIO NominalDiffTime
interval = forall (m :: * -> *) (t :: * -> *) a.
(MonadIO m, MonadIO t, MonadCatch t) =>
(UTCTime -> UTCTime -> Bool) -> t a -> m (t a)
cachedIOWith (NominalDiffTime -> UTCTime -> UTCTime -> Bool
secondsPassed NominalDiffTime
interval)
cachedIO' :: (MonadIO m, MonadIO t, MonadCatch t)
=> NominalDiffTime
-> (Maybe (UTCTime, a) -> t a)
-> m (t a)
cachedIO' :: forall (m :: * -> *) (t :: * -> *) a.
(MonadIO m, MonadIO t, MonadCatch t) =>
NominalDiffTime -> (Maybe (UTCTime, a) -> t a) -> m (t a)
cachedIO' NominalDiffTime
interval = forall (m :: * -> *) (t :: * -> *) a.
(MonadIO m, MonadIO t, MonadCatch t) =>
(UTCTime -> UTCTime -> Bool)
-> (Maybe (UTCTime, a) -> t a) -> m (t a)
cachedIOWith' (NominalDiffTime -> UTCTime -> UTCTime -> Bool
secondsPassed NominalDiffTime
interval)
secondsPassed :: NominalDiffTime
-> UTCTime
-> UTCTime
-> Bool
secondsPassed :: NominalDiffTime -> UTCTime -> UTCTime -> Bool
secondsPassed NominalDiffTime
interval UTCTime
start UTCTime
end = NominalDiffTime -> UTCTime -> UTCTime
addUTCTime NominalDiffTime
interval UTCTime
start forall a. Ord a => a -> a -> Bool
> UTCTime
end
cachedIOWith
:: (MonadIO m, MonadIO t, MonadCatch t)
=> (UTCTime -> UTCTime -> Bool)
-> t a
-> m (t a)
cachedIOWith :: forall (m :: * -> *) (t :: * -> *) a.
(MonadIO m, MonadIO t, MonadCatch t) =>
(UTCTime -> UTCTime -> Bool) -> t a -> m (t a)
cachedIOWith UTCTime -> UTCTime -> Bool
f t a
io = forall (m :: * -> *) (t :: * -> *) a.
(MonadIO m, MonadIO t, MonadCatch t) =>
(UTCTime -> UTCTime -> Bool)
-> (Maybe (UTCTime, a) -> t a) -> m (t a)
cachedIOWith' UTCTime -> UTCTime -> Bool
f (forall a b. a -> b -> a
const t a
io)
cachedIOWith'
:: (MonadIO m, MonadIO t, MonadCatch t)
=> (UTCTime -> UTCTime -> Bool)
-> (Maybe (UTCTime, a) -> t a)
-> m (t a)
cachedIOWith' :: forall (m :: * -> *) (t :: * -> *) a.
(MonadIO m, MonadIO t, MonadCatch t) =>
(UTCTime -> UTCTime -> Bool)
-> (Maybe (UTCTime, a) -> t a) -> m (t a)
cachedIOWith' UTCTime -> UTCTime -> Bool
isCacheStillFresh Maybe (UTCTime, a) -> t a
io = do
TVar (State a)
cachedT <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall a. STM a -> IO a
atomically (forall a. a -> STM (TVar a)
newTVar forall a. State a
Uninitialized))
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ do
UTCTime
now <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO UTCTime
getCurrentTime
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. STM a -> IO a
atomically forall a b. (a -> b) -> a -> b
$ do
State a
cached <- forall a. TVar a -> STM a
readTVar TVar (State a)
cachedT
case State a
cached of
previousState :: State a
previousState@(Fresh UTCTime
lastUpdated a
value)
| UTCTime -> UTCTime -> Bool
isCacheStillFresh UTCTime
lastUpdated UTCTime
now -> forall (m :: * -> *) a. Monad m => a -> m a
return (forall (m :: * -> *) a. Monad m => a -> m a
return a
value)
| Bool
otherwise -> do
forall a. TVar a -> a -> STM ()
writeTVar TVar (State a)
cachedT (forall a. a -> State a
Updating a
value)
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ State a -> TVar (State a) -> t a
refreshCache State a
previousState TVar (State a)
cachedT
Updating a
value -> forall (m :: * -> *) a. Monad m => a -> m a
return (forall (m :: * -> *) a. Monad m => a -> m a
return a
value)
State a
Uninitialized -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ State a -> TVar (State a) -> t a
refreshCache forall a. State a
Uninitialized TVar (State a)
cachedT
State a
Initializing -> forall a. STM a
retry
where
refreshCache :: State a -> TVar (State a) -> t a
refreshCache State a
previousState TVar (State a)
cachedT = do
let previous :: Maybe (UTCTime, a)
previous = case State a
previousState of
Fresh UTCTime
lastUpdated a
value -> forall a. a -> Maybe a
Just (UTCTime
lastUpdated, a
value)
State a
_ -> forall a. Maybe a
Nothing
a
newValue <- Maybe (UTCTime, a) -> t a
io Maybe (UTCTime, a)
previous forall (m :: * -> *) a b. MonadCatch m => m a -> m b -> m a
`onException` forall (m :: * -> *) a.
MonadIO m =>
State a -> TVar (State a) -> m ()
restoreState State a
previousState TVar (State a)
cachedT
UTCTime
now <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO UTCTime
getCurrentTime
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall a. STM a -> IO a
atomically (forall a. TVar a -> a -> STM ()
writeTVar TVar (State a)
cachedT (forall a. UTCTime -> a -> State a
Fresh UTCTime
now a
newValue)))
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall (m :: * -> *) a. Monad m => a -> m a
return a
newValue)
restoreState :: (MonadIO m) => State a -> TVar (State a) -> m ()
restoreState :: forall (m :: * -> *) a.
MonadIO m =>
State a -> TVar (State a) -> m ()
restoreState State a
previousState TVar (State a)
cachedT = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall a. STM a -> IO a
atomically (forall a. TVar a -> a -> STM ()
writeTVar TVar (State a)
cachedT State a
previousState))