{-# LANGUAGE DeriveDataTypeable, BangPatterns #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Data.Acid.Memory
-- Copyright   :  PublicDomain
--
-- Maintainer  :  lemmih@gmail.com
-- Portability :  non-portable (uses GHC extensions)
--
-- AcidState container without a transaction log. Mostly used for testing.
-- Supports Atomicity, Consistency and Isolation, but not Durability.
--

module Data.Acid.Memory
    ( openMemoryState
    ) where

import Data.Acid.Core
import Data.Acid.Common
import Data.Acid.Abstract

import Control.Concurrent             ( newEmptyMVar, putMVar, MVar )
import Control.Monad.State            ( runState )
import Data.ByteString.Lazy           ( ByteString )
import Data.Typeable                  ( Typeable )
import Data.IORef                     ( IORef, newIORef, readIORef, writeIORef )


{-| State container offering full ACID (Atomicity, Consistency, Isolation and Durability)
    guarantees.

    [@Atomicity@]  State changes are all-or-nothing. This is what you'd expect of any state
                   variable in Haskell and AcidState doesn't change that.

    [@Consistency@] No event or set of events will break your data invariants.

    [@Isolation@] Transactions cannot interfere with each other even when issued in parallel.

    [@Durability@] Successful transaction are guaranteed to survive system failure (both
                   hardware and software).
-}
data MemoryState st
    = MemoryState { forall st. MemoryState st -> Core st
localCore    :: Core st
                  , forall st. MemoryState st -> IORef st
localCopy    :: IORef st
                  } deriving (Typeable)

-- | Create an 'AcidState' given an initial value.  The state is kept only in
-- memory, so it is not durable.
openMemoryState :: (IsAcidic st)
              => st                          -- ^ Initial state value.
              -> IO (AcidState st)
openMemoryState :: forall st. IsAcidic st => st -> IO (AcidState st)
openMemoryState st
initialState
    = do Core st
core <- forall st. [MethodContainer st] -> st -> IO (Core st)
mkCore (forall st. [Event st] -> [MethodContainer st]
eventsToMethods forall st. IsAcidic st => [Event st]
acidEvents) st
initialState
         IORef st
ref  <- forall a. a -> IO (IORef a)
newIORef st
initialState
         forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall st. IsAcidic st => MemoryState st -> AcidState st
toAcidState MemoryState { localCore :: Core st
localCore = Core st
core, localCopy :: IORef st
localCopy = IORef st
ref }


-- | Issue an Update event and return immediately. The order of events is honored.
--   The behavior in case of exceptions is exactly the same as for 'update'.
--
--   If EventA is scheduled before EventB, EventA /will/ be executed before EventB:
--
--   @
--do scheduleUpdate acid EventA
--   scheduleUpdate acid EventB
--   @
scheduleMemoryUpdate :: UpdateEvent event => MemoryState (EventState event) -> event -> IO (MVar (EventResult event))
scheduleMemoryUpdate :: forall event.
UpdateEvent event =>
MemoryState (EventState event)
-> event -> IO (MVar (EventResult event))
scheduleMemoryUpdate MemoryState (EventState event)
acidState event
event
    = do MVar (EventResult event)
mvar <- forall a. IO (MVar a)
newEmptyMVar
         forall st. Core st -> (st -> IO st) -> IO ()
modifyCoreState_ (forall st. MemoryState st -> Core st
localCore MemoryState (EventState event)
acidState) forall a b. (a -> b) -> a -> b
$ \EventState event
st ->
           do let !(EventResult event
result, !EventState event
st') = forall s a. State s a -> s -> (a, s)
runState State (EventState event) (EventResult event)
hotMethod EventState event
st
              forall a. IORef a -> a -> IO ()
writeIORef (forall st. MemoryState st -> IORef st
localCopy MemoryState (EventState event)
acidState) EventState event
st'
              forall a. MVar a -> a -> IO ()
putMVar MVar (EventResult event)
mvar EventResult event
result
              forall (m :: * -> *) a. Monad m => a -> m a
return EventState event
st'
         forall (m :: * -> *) a. Monad m => a -> m a
return MVar (EventResult event)
mvar
    where hotMethod :: State (EventState event) (EventResult event)
hotMethod = forall method.
Method method =>
MethodMap (MethodState method)
-> method -> State (MethodState method) (MethodResult method)
lookupHotMethod (forall st. Core st -> MethodMap st
coreMethods (forall st. MemoryState st -> Core st
localCore MemoryState (EventState event)
acidState)) event
event

scheduleMemoryColdUpdate :: MemoryState st -> Tagged ByteString -> IO (MVar ByteString)
scheduleMemoryColdUpdate :: forall st.
MemoryState st -> Tagged ByteString -> IO (MVar ByteString)
scheduleMemoryColdUpdate MemoryState st
acidState Tagged ByteString
event
    = do MVar ByteString
mvar <- forall a. IO (MVar a)
newEmptyMVar
         forall st. Core st -> (st -> IO st) -> IO ()
modifyCoreState_ (forall st. MemoryState st -> Core st
localCore MemoryState st
acidState) forall a b. (a -> b) -> a -> b
$ \st
st ->
           do let !(ByteString
result, !st
st') = forall s a. State s a -> s -> (a, s)
runState State st ByteString
coldMethod st
st
              forall a. IORef a -> a -> IO ()
writeIORef (forall st. MemoryState st -> IORef st
localCopy MemoryState st
acidState) st
st'
              forall a. MVar a -> a -> IO ()
putMVar MVar ByteString
mvar ByteString
result
              forall (m :: * -> *) a. Monad m => a -> m a
return st
st'
         forall (m :: * -> *) a. Monad m => a -> m a
return MVar ByteString
mvar
    where coldMethod :: State st ByteString
coldMethod = forall st. Core st -> Tagged ByteString -> State st ByteString
lookupColdMethod (forall st. MemoryState st -> Core st
localCore MemoryState st
acidState) Tagged ByteString
event

-- | Issue a Query event and wait for its result. Events may be issued in parallel.
memoryQuery  :: QueryEvent event  => MemoryState (EventState event) -> event -> IO (EventResult event)
memoryQuery :: forall event.
QueryEvent event =>
MemoryState (EventState event) -> event -> IO (EventResult event)
memoryQuery MemoryState (EventState event)
acidState event
event
    = do EventState event
st <- forall a. IORef a -> IO a
readIORef (forall st. MemoryState st -> IORef st
localCopy MemoryState (EventState event)
acidState)
         let (EventResult event
result, EventState event
_st) = forall s a. State s a -> s -> (a, s)
runState State (EventState event) (EventResult event)
hotMethod EventState event
st
         forall (m :: * -> *) a. Monad m => a -> m a
return EventResult event
result
    where hotMethod :: State (EventState event) (EventResult event)
hotMethod = forall method.
Method method =>
MethodMap (MethodState method)
-> method -> State (MethodState method) (MethodResult method)
lookupHotMethod (forall st. Core st -> MethodMap st
coreMethods (forall st. MemoryState st -> Core st
localCore MemoryState (EventState event)
acidState)) event
event

memoryQueryCold  :: MemoryState st -> Tagged ByteString -> IO ByteString
memoryQueryCold :: forall st. MemoryState st -> Tagged ByteString -> IO ByteString
memoryQueryCold MemoryState st
acidState Tagged ByteString
event
    = do st
st <- forall a. IORef a -> IO a
readIORef (forall st. MemoryState st -> IORef st
localCopy MemoryState st
acidState)
         let (ByteString
result, st
_st) = forall s a. State s a -> s -> (a, s)
runState State st ByteString
coldMethod st
st
         forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
result
    where coldMethod :: State st ByteString
coldMethod = forall st. Core st -> Tagged ByteString -> State st ByteString
lookupColdMethod (forall st. MemoryState st -> Core st
localCore MemoryState st
acidState) Tagged ByteString
event

-- | This is a nop with the memory backend.
createMemoryCheckpoint :: MemoryState st -> IO ()
createMemoryCheckpoint :: forall st. MemoryState st -> IO ()
createMemoryCheckpoint MemoryState st
acidState
    = forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | This is a nop with the memory backend.
createMemoryArchive :: MemoryState st -> IO ()
createMemoryArchive :: forall st. MemoryState st -> IO ()
createMemoryArchive MemoryState st
acidState
    = forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | Close an AcidState and associated logs.
--   Any subsequent usage of the AcidState will throw an exception.
closeMemoryState :: MemoryState st -> IO ()
closeMemoryState :: forall st. MemoryState st -> IO ()
closeMemoryState MemoryState st
acidState
    = forall st. Core st -> IO ()
closeCore (forall st. MemoryState st -> Core st
localCore MemoryState st
acidState)

toAcidState :: IsAcidic st => MemoryState st -> AcidState st
toAcidState :: forall st. IsAcidic st => MemoryState st -> AcidState st
toAcidState MemoryState st
memory
  = AcidState { _scheduleUpdate :: forall event.
(UpdateEvent event, EventState event ~ st) =>
event -> IO (MVar (EventResult event))
_scheduleUpdate    = forall event.
UpdateEvent event =>
MemoryState (EventState event)
-> event -> IO (MVar (EventResult event))
scheduleMemoryUpdate MemoryState st
memory
              , scheduleColdUpdate :: Tagged ByteString -> IO (MVar ByteString)
scheduleColdUpdate = forall st.
MemoryState st -> Tagged ByteString -> IO (MVar ByteString)
scheduleMemoryColdUpdate MemoryState st
memory
              , _query :: forall event.
(QueryEvent event, EventState event ~ st) =>
event -> IO (EventResult event)
_query             = forall event.
QueryEvent event =>
MemoryState (EventState event) -> event -> IO (EventResult event)
memoryQuery MemoryState st
memory
              , queryCold :: Tagged ByteString -> IO ByteString
queryCold          = forall st. MemoryState st -> Tagged ByteString -> IO ByteString
memoryQueryCold MemoryState st
memory
              , createCheckpoint :: IO ()
createCheckpoint   = forall st. MemoryState st -> IO ()
createMemoryCheckpoint MemoryState st
memory
              , createArchive :: IO ()
createArchive      = forall st. MemoryState st -> IO ()
createMemoryArchive MemoryState st
memory
              , closeAcidState :: IO ()
closeAcidState     = forall st. MemoryState st -> IO ()
closeMemoryState MemoryState st
memory
              , acidSubState :: AnyState st
acidSubState       = forall (sub_st :: * -> *) st.
Typeable sub_st =>
sub_st st -> AnyState st
mkAnyState MemoryState st
memory
              }