git-annex/Database/Keys/Handle.hs
2025-09-23 13:55:13 -04:00

74 lines
1.8 KiB
Haskell

{- Handle for the Keys database.
-
- Copyright 2015 Joey Hess <id@joeyh.name>
-:
- Licensed under the GNU AGPL version 3 or higher.
-}
module Database.Keys.Handle (
DbHandle,
newDbHandle,
DbState(..),
DbWasOpen(..),
withDbState,
flushDbQueue,
closeDbHandle,
) where
import qualified Database.Queue as H
import Database.Keys.Tables
import Utility.Exception
import Utility.DebugLocks
import Control.Concurrent
import Control.Monad.IO.Class (liftIO, MonadIO)
-- The MVar is always left full except when actions are run
-- that access the database.
newtype DbHandle = DbHandle (MVar DbState)
-- The database can be closed or open, but it also may have been
-- tried to open (for read) and didn't exist yet or is not readable.
data DbState
= DbClosed DbWasOpen
| DbOpen (H.DbQueue, DbTablesChanged)
| DbUnavailable
-- Was the database previously opened by this process?
data DbWasOpen = DbWasOpen Bool
newDbHandle :: IO DbHandle
newDbHandle = DbHandle <$> newMVar (DbClosed (DbWasOpen False))
-- Runs an action on the state of the handle, which can change its state.
-- The MVar is empty while the action runs, which blocks other users
-- of the handle from running.
withDbState
:: (MonadIO m, MonadCatch m)
=> DbHandle
-> (DbState -> m (v, DbState))
-> m v
withDbState (DbHandle mvar) a = do
st <- liftIO $ debugLocks $ takeMVar mvar
go st `onException` (liftIO $ debugLocks $ putMVar mvar st)
where
go st = do
(v, st') <- a st
liftIO $ debugLocks $ putMVar mvar st'
return v
flushDbQueue :: DbHandle -> IO ()
flushDbQueue h = withDbState h go
where
go (DbOpen (qh, _)) = do
H.flushDbQueue qh
return ((), DbOpen (qh, mempty))
go st = return ((), st)
closeDbHandle :: DbHandle -> IO ()
closeDbHandle h = withDbState h go
where
go (DbOpen (qh, _)) = do
H.closeDbQueue qh
return ((), DbClosed (DbWasOpen True))
go st = return ((), st)