-- | -- Module : Simulation.Aivika.Distributed.Optimistic.Internal.UndoableLog -- Copyright : Copyright (c) 2015-2016, David Sorokin -- License : BSD3 -- Maintainer : David Sorokin -- Stability : experimental -- Tested with: GHC 7.10.3 -- -- This module defines an output message queue. -- module Simulation.Aivika.Distributed.Optimistic.Internal.UndoableLog (UndoableLog, newUndoableLog, writeLog, rollbackLog, reduceLog, logSize) where import Data.IORef import Control.Monad import Control.Monad.Trans import qualified Simulation.Aivika.DoubleLinkedList as DLL import Simulation.Aivika.Trans.Comp import Simulation.Aivika.Trans.Internal.Types import Simulation.Aivika.Distributed.Optimistic.Internal.Priority import Simulation.Aivika.Distributed.Optimistic.Internal.DIO import Simulation.Aivika.Distributed.Optimistic.Internal.IO -- | Specified an undoable log with ability to rollback the operations. data UndoableLog = UndoableLog { logItems :: DLL.DoubleLinkedList UndoableItem -- ^ The items that can be undone. } data UndoableItem = UndoableItem { itemTime :: Double, -- ^ The time at which the operation had occured. itemUndo :: DIO () -- ^ Undo the operation } -- | Create an undoable log. newUndoableLog :: DIO UndoableLog newUndoableLog = do xs <- liftIOUnsafe DLL.newList return UndoableLog { logItems = xs } -- | Write a new undoable operation. writeLog :: UndoableLog -> DIO () -> Event DIO () {-# INLINE writeLog #-} writeLog log h = Event $ \p -> do let x = UndoableItem { itemTime = pointTime p, itemUndo = h } --- --- logDIO DEBUG $ "Writing the log at t = " ++ show (itemTime x) --- liftIOUnsafe $ do f <- DLL.listNull (logItems log) if f then DLL.listAddLast (logItems log) x else do x0 <- DLL.listLast (logItems log) when (itemTime x < itemTime x0) $ error $ "The logging data are not sorted by time (" ++ (show $ itemTime x) ++ " < " ++ (show $ itemTime x0) ++ "): writeLog" DLL.listAddLast (logItems log) x -- | Rollback the log till the specified time either including that one or not. rollbackLog :: UndoableLog -> Double -> Bool -> DIO () rollbackLog log t including = do --- --- logDIO DEBUG $ "Rolling the log back to t = " ++ show t --- loop where loop = do f <- liftIOUnsafe $ DLL.listNull (logItems log) unless f $ do x <- liftIOUnsafe $ DLL.listLast (logItems log) when ((t < itemTime x) || (including && t == itemTime x)) $ do liftIOUnsafe $ DLL.listRemoveLast (logItems log) itemUndo x loop -- | Reduce the log removing all items older than the specified time. reduceLog :: UndoableLog -> Double -> IO () reduceLog log t = do f <- DLL.listNull (logItems log) unless f $ do x <- DLL.listFirst (logItems log) when (itemTime x < t) $ do DLL.listRemoveFirst (logItems log) reduceLog log t -- | Return the log size. logSize :: UndoableLog -> IO Int logSize log = DLL.listCount (logItems log)