{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveGeneric #-}
module SafeCounter
( startCounter,
getCount,
getCountAsync,
incCount,
resetCount,
wait,
waitTimeout,
Fetch(..),
Increment(..),
Reset(..)
) where
import Control.Distributed.Process hiding (call, say)
import Control.Distributed.Process.Extras
import Control.Distributed.Process.Async
import Control.Distributed.Process.ManagedProcess
( ProcessDefinition(..)
, InitHandler
, InitResult(..)
, defaultProcess
, condition
)
import qualified Control.Distributed.Process.ManagedProcess as ManagedProcess (serve)
import Control.Distributed.Process.ManagedProcess.Client
import Control.Distributed.Process.ManagedProcess.Server.Restricted
import Control.Distributed.Process.Extras.Time
import Control.Distributed.Process.Serializable
import Data.Binary
import Data.Typeable (Typeable)
import GHC.Generics
--------------------------------------------------------------------------------
-- Types --
--------------------------------------------------------------------------------
data Increment = Increment
deriving (Show, Typeable, Generic)
instance Binary Increment where
data Fetch = Fetch
deriving (Show, Typeable, Generic)
instance Binary Fetch where
data Reset = Reset deriving (Show, Typeable, Generic)
instance Binary Reset where
--------------------------------------------------------------------------------
-- API --
--------------------------------------------------------------------------------
-- | Increment count
incCount :: ProcessId -> Process Int
incCount sid = call sid Increment
-- | Get the current count
getCount :: ProcessId -> Process Int
getCount sid = call sid Fetch
-- | Get the current count asynchronously
getCountAsync :: ProcessId -> Process (Async Int)
getCountAsync sid = callAsync sid Fetch
-- | Reset the current count
resetCount :: ProcessId -> Process ()
resetCount sid = cast sid Reset
-- | Start a counter server
startCounter :: Int -> Process ProcessId
startCounter startCount =
let server = serverDefinition
in spawnLocal $ ManagedProcess.serve startCount init' server
where init' :: InitHandler Int Int
init' count = return $ InitOk count Infinity
--------------------------------------------------------------------------------
-- Implementation --
--------------------------------------------------------------------------------
serverDefinition :: ProcessDefinition Int
serverDefinition = defaultProcess {
apiHandlers = [
handleCallIf
(condition (\count Increment -> count >= 10)) -- invariant
(\Increment -> halt :: RestrictedProcess Int (Result Int))
, handleCall handleIncrement
, handleCall (\Fetch -> getState >>= reply)
, handleCast (\Reset -> putState (0 :: Int) >> continue)
]
} :: ProcessDefinition Int
halt :: forall s r . Serializable r => RestrictedProcess s (Result r)
halt = haltNoReply (ExitOther "Count > 10")
handleIncrement :: Increment -> RestrictedProcess Int (Result Int)
handleIncrement _ = modifyState (+1) >> getState >>= reply