module Database.Haskey.Alloc.Concurrent.Environment where
import Control.Applicative ((<$>))
import Control.Monad.State
import Data.Binary (Binary)
import Data.Set (Set)
import Data.Word (Word32)
import qualified Data.Binary as B
import qualified Data.Set as S
import STMContainers.Map (Map)
import Data.BTree.Primitives
import Database.Haskey.Alloc.Concurrent.FreePages.Tree
data StateType = TypeData
| TypeIndex
data S (t :: StateType) a where
DataState :: a -> S 'TypeData a
IndexState :: a -> S 'TypeIndex a
deriving instance Show a => Show (S t a)
instance Binary a => Binary (S 'TypeData a) where
put (DataState a) = B.put a
get = DataState <$> B.get
instance Binary a => Binary (S 'TypeIndex a) where
put (IndexState a) = B.put a
get = IndexState <$> B.get
instance Functor (S t) where
f `fmap` (DataState v) = DataState (f v)
f `fmap` (IndexState v) = IndexState (f v)
getSValue :: S t a -> a
getSValue (DataState a) = a
getSValue (IndexState a) = a
newtype ReaderEnv hnds = ReaderEnv { readerHnds :: hnds }
data FileState stateType = FileState {
fileStateNewlyFreedPages :: ![NewlyFreed]
, fileStateOriginalNumPages :: !(S stateType PageId)
, fileStateNewNumPages :: !(S stateType PageId)
, fileStateFreedDirtyPages :: !(S stateType (Set DirtyFree))
, fileStateFreeTree :: !(S stateType FreeTree)
, fileStateDirtyReusablePages :: !(Set DirtyOldFree)
, fileStateReusablePages :: ![OldFree]
, fileStateReusablePagesTxId :: !(Maybe TxId)
}
data WriterEnv hnds = WriterEnv
{ writerHnds :: !hnds
, writerTxId :: !TxId
, writerReaders :: Map TxId Integer
, writerIndexFileState :: FileState 'TypeIndex
, writerDataFileState :: FileState 'TypeData
, writerReusablePagesOn :: !Bool
, writerDirtyOverflows :: !(Set DirtyOverflow)
, writerOverflowCounter :: !Word32
, writerRemovedOverflows :: ![OldOverflow]
}
newWriter :: hnd -> TxId -> Map TxId Integer
-> S 'TypeData PageId -> S 'TypeIndex PageId
-> S 'TypeData (Set DirtyFree) -> S 'TypeIndex (Set DirtyFree)
-> S 'TypeData FreeTree -> S 'TypeIndex FreeTree
-> WriterEnv hnd
newWriter hnd tx readers
numDataPages numIndexPages
dataDirtyFree indexDirtyFree
dataFreeTree indexFreeTree =
WriterEnv {
writerHnds = hnd
, writerTxId = tx
, writerReaders = readers
, writerIndexFileState = newFileState numIndexPages indexDirtyFree indexFreeTree
, writerDataFileState = newFileState numDataPages dataDirtyFree dataFreeTree
, writerReusablePagesOn = True
, writerDirtyOverflows = S.empty
, writerOverflowCounter = 0
, writerRemovedOverflows = []
}
where
newFileState numPages dirtyFree freeTree = FileState {
fileStateNewlyFreedPages = []
, fileStateOriginalNumPages = numPages
, fileStateNewNumPages = numPages
, fileStateFreedDirtyPages = dirtyFree
, fileStateFreeTree = freeTree
, fileStateDirtyReusablePages = S.empty
, fileStateReusablePages = []
, fileStateReusablePagesTxId = Nothing
}
newtype Fresh = Fresh PageId deriving (Eq, Ord, Show)
newtype NewlyFreed = NewlyFreed PageId deriving (Eq, Ord, Show)
newtype Dirty = Dirty PageId deriving (Eq, Ord, Show)
newtype DirtyFree = DirtyFree PageId deriving (Binary, Eq, Ord, Show)
newtype OldFree = OldFree PageId deriving (Eq, Ord, Show)
newtype DirtyOldFree = DirtyOldFree PageId deriving (Eq, Ord, Show)
data SomeFreePage = FreshFreePage Fresh
| DirtyFreePage DirtyFree
| OldFreePage OldFree
getSomeFreePageId :: SomeFreePage -> PageId
getSomeFreePageId (FreshFreePage (Fresh pid)) = pid
getSomeFreePageId (DirtyFreePage (DirtyFree pid)) = pid
getSomeFreePageId (OldFreePage (OldFree pid)) = pid
freePage :: (Functor m, MonadState (WriterEnv hnd) m) => S stateType PageId -> m ()
freePage pid@(DataState pid') = do
dirty' <- dirty pid
dirtyOldFree' <- dirtyOldFree pid
modify' $ \e ->
e { writerDataFileState =
updateFileState (writerDataFileState e) DataState
dirty' dirtyOldFree' pid'
}
freePage pid@(IndexState pid') = do
dirty' <- dirty pid
dirtyOldFree' <- dirtyOldFree pid
modify' $ \e ->
e { writerIndexFileState =
updateFileState (writerIndexFileState e) IndexState
dirty' dirtyOldFree' pid'
}
updateFileState :: FileState t
-> (forall a. a -> S t a)
-> Maybe Dirty
-> Maybe DirtyOldFree
-> PageId
-> FileState t
updateFileState e cons dirty' dirtyOldFree' pid' =
if | Just (Dirty p) <- dirty' ->
e { fileStateFreedDirtyPages =
cons $ S.insert (DirtyFree p) (getSValue $ fileStateFreedDirtyPages e) }
| Just (DirtyOldFree p) <- dirtyOldFree' ->
e { fileStateReusablePages =
OldFree p : fileStateReusablePages e }
| p <- pid' ->
e { fileStateNewlyFreedPages =
NewlyFreed p : fileStateNewlyFreedPages e }
dirty :: (Functor m, MonadState (WriterEnv hnd) m) => S stateType PageId -> m (Maybe Dirty)
dirty pid = case pid of
DataState p -> (page p . fileStateOriginalNumPages . writerDataFileState) <$> get
IndexState p -> (page p . fileStateOriginalNumPages . writerIndexFileState) <$> get
where
page p origNumPages
| p >= getSValue origNumPages = Just (Dirty p)
| otherwise = Nothing
dirtyOldFree :: (Functor m, MonadState (WriterEnv hnd) m) => S stateType PageId -> m (Maybe DirtyOldFree)
dirtyOldFree pid = case pid of
DataState p -> (page p . fileStateDirtyReusablePages . writerDataFileState) <$> get
IndexState p -> (page p . fileStateDirtyReusablePages . writerIndexFileState) <$> get
where
page p dirty'
| S.member (DirtyOldFree p) dirty' = Just (DirtyOldFree p)
| otherwise = Nothing
touchPage :: MonadState (WriterEnv hnd) m => S stateType SomeFreePage -> m ()
touchPage (DataState (DirtyFreePage _)) = return()
touchPage (IndexState (DirtyFreePage _)) = return ()
touchPage (DataState (FreshFreePage (Fresh pid))) = modify' $ \e ->
case fileStateNewNumPages (writerDataFileState e) of
DataState numPages ->
if numPages < pid + 1
then e { writerDataFileState = (writerDataFileState e) {
fileStateNewNumPages = DataState (pid + 1) }
}
else e
touchPage (IndexState (FreshFreePage (Fresh pid))) = modify' $ \e ->
case fileStateNewNumPages (writerIndexFileState e) of
IndexState numPages ->
if numPages < pid + 1
then e { writerIndexFileState = (writerIndexFileState e) {
fileStateNewNumPages = IndexState (pid + 1) }
}
else e
touchPage (DataState (OldFreePage (OldFree pid))) = modify' $ \e ->
let s = fileStateDirtyReusablePages (writerDataFileState e) in
e { writerDataFileState = (writerDataFileState e) {
fileStateDirtyReusablePages = S.insert (DirtyOldFree pid) s }
}
touchPage (IndexState (OldFreePage (OldFree pid))) = modify' $ \e ->
let s = fileStateDirtyReusablePages (writerIndexFileState e) in
e { writerIndexFileState = (writerIndexFileState e) {
fileStateDirtyReusablePages = S.insert (DirtyOldFree pid) s }
}
newtype DirtyOverflow = DirtyOverflow OverflowId deriving (Eq, Ord, Show)
newtype OldOverflow = OldOverflow OverflowId deriving (Eq, Ord, Show)
touchOverflow :: MonadState (WriterEnv hnd) m => OverflowId -> m ()
touchOverflow i = modify' $
\e -> e { writerDirtyOverflows =
S.insert (DirtyOverflow i) (writerDirtyOverflows e) }
overflowType :: MonadState (WriterEnv hnd) m => OverflowId -> m (Either DirtyOverflow OldOverflow)
overflowType i = do
dirty' <- gets $ \e -> S.member (DirtyOverflow i) (writerDirtyOverflows e)
if dirty' then return $ Left (DirtyOverflow i)
else return $ Right (OldOverflow i)
removeOldOverflow :: MonadState (WriterEnv hdn) m => OldOverflow -> m ()
removeOldOverflow i =
modify' $ \e -> e { writerRemovedOverflows = i : writerRemovedOverflows e }