module Ecluse.Core.Cve.Slot (
CveSlot,
newCveSlot,
withSlotLookup,
currentAdvisoryEtag,
swapIn,
) where
import Control.Concurrent.STM (check)
import UnliftIO.Exception (bracket)
import Ecluse.Core.Cve (CveDb (..), CveLookup, DbEtag)
data Generation = Generation
{ Generation -> CveDb
genDb :: CveDb
, Generation -> DbEtag
genEtag :: DbEtag
, Generation -> TVar Int
genReaders :: TVar Int
}
newtype CveSlot = CveSlot (TVar (Maybe Generation))
newCveSlot :: IO CveSlot
newCveSlot :: IO CveSlot
newCveSlot = TVar (Maybe Generation) -> CveSlot
CveSlot (TVar (Maybe Generation) -> CveSlot)
-> IO (TVar (Maybe Generation)) -> IO CveSlot
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Generation -> IO (TVar (Maybe Generation))
forall (m :: * -> *) a. MonadIO m => a -> m (TVar a)
newTVarIO Maybe Generation
forall a. Maybe a
Nothing
withSlotLookup :: CveSlot -> (Maybe CveLookup -> IO a) -> IO a
withSlotLookup :: forall a. CveSlot -> (Maybe CveLookup -> IO a) -> IO a
withSlotLookup (CveSlot TVar (Maybe Generation)
cell) Maybe CveLookup -> IO a
use = IO (Maybe Generation)
-> (Maybe Generation -> IO ())
-> (Maybe Generation -> IO a)
-> IO a
forall (m :: * -> *) a b c.
MonadUnliftIO m =>
m a -> (a -> m b) -> (a -> m c) -> m c
bracket IO (Maybe Generation)
acquire Maybe Generation -> IO ()
release (Maybe CveLookup -> IO a
use (Maybe CveLookup -> IO a)
-> (Maybe Generation -> Maybe CveLookup)
-> Maybe Generation
-> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Generation -> CveLookup) -> Maybe Generation -> Maybe CveLookup
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (CveDb -> CveLookup
cveDbLookup (CveDb -> CveLookup)
-> (Generation -> CveDb) -> Generation -> CveLookup
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Generation -> CveDb
genDb))
where
acquire :: IO (Maybe Generation)
acquire = STM (Maybe Generation) -> IO (Maybe Generation)
forall (m :: * -> *) a. MonadIO m => STM a -> m a
atomically (STM (Maybe Generation) -> IO (Maybe Generation))
-> STM (Maybe Generation) -> IO (Maybe Generation)
forall a b. (a -> b) -> a -> b
$ do
mGen <- TVar (Maybe Generation) -> STM (Maybe Generation)
forall a. TVar a -> STM a
readTVar TVar (Maybe Generation)
cell
for_ mGen (\Generation
g -> TVar Int -> (Int -> Int) -> STM ()
forall a. TVar a -> (a -> a) -> STM ()
modifyTVar' (Generation -> TVar Int
genReaders Generation
g) (Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1))
pure mGen
release :: Maybe Generation -> IO ()
release = (Generation -> IO ()) -> Maybe Generation -> IO ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ (\Generation
g -> STM () -> IO ()
forall (m :: * -> *) a. MonadIO m => STM a -> m a
atomically (TVar Int -> (Int -> Int) -> STM ()
forall a. TVar a -> (a -> a) -> STM ()
modifyTVar' (Generation -> TVar Int
genReaders Generation
g) (Int -> Int -> Int
forall a. Num a => a -> a -> a
subtract Int
1)))
currentAdvisoryEtag :: CveSlot -> IO (Maybe DbEtag)
currentAdvisoryEtag :: CveSlot -> IO (Maybe DbEtag)
currentAdvisoryEtag (CveSlot TVar (Maybe Generation)
cell) = (Generation -> DbEtag) -> Maybe Generation -> Maybe DbEtag
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Generation -> DbEtag
genEtag (Maybe Generation -> Maybe DbEtag)
-> IO (Maybe Generation) -> IO (Maybe DbEtag)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TVar (Maybe Generation) -> IO (Maybe Generation)
forall (m :: * -> *) a. MonadIO m => TVar a -> m a
readTVarIO TVar (Maybe Generation)
cell
swapIn :: CveSlot -> DbEtag -> CveDb -> IO ()
swapIn :: CveSlot -> DbEtag -> CveDb -> IO ()
swapIn (CveSlot TVar (Maybe Generation)
cell) DbEtag
etag CveDb
newDb = do
readers <- Int -> IO (TVar Int)
forall (m :: * -> *) a. MonadIO m => a -> m (TVar a)
newTVarIO (Int
0 :: Int)
displaced <- atomically $ do
old <- readTVar cell
writeTVar cell (Just (Generation newDb etag readers))
pure old
for_ displaced $ \Generation
g -> do
STM () -> IO ()
forall (m :: * -> *) a. MonadIO m => STM a -> m a
atomically (TVar Int -> STM Int
forall a. TVar a -> STM a
readTVar (Generation -> TVar Int
genReaders Generation
g) STM Int -> (Int -> STM ()) -> STM ()
forall a b. STM a -> (a -> STM b) -> STM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Bool -> STM ()
check (Bool -> STM ()) -> (Int -> Bool) -> Int -> STM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0))
CveDb -> IO ()
cveDbClose (Generation -> CveDb
genDb Generation
g)