module Ecluse.Core.Cve.Slot (
CveSlot,
newCveSlot,
withSlotLookup,
swapIn,
) where
import Control.Concurrent.STM (check)
import UnliftIO.Exception (bracket, catchAny)
import Ecluse.Core.Cve (CveDb (..), CveLookup)
data Generation = Generation
{ Generation -> CveDb
genDb :: CveDb
, 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)))
swapIn :: CveSlot -> CveDb -> IO ()
swapIn :: CveSlot -> CveDb -> IO ()
swapIn (CveSlot TVar (Maybe Generation)
cell) 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 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) IO () -> (SomeException -> IO ()) -> IO ()
forall (m :: * -> *) a.
MonadUnliftIO m =>
m a -> (SomeException -> m a) -> m a
`catchAny` IO () -> SomeException -> IO ()
forall a b. a -> b -> a
const IO ()
forall (f :: * -> *). Applicative f => f ()
pass