{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE BangPatterns #-} module Counter ( startCounter, getCount, incCount, resetCount, wait, waitTimeout ) where import Control.Distributed.Process hiding (call) import Control.Distributed.Process.Async import Control.Distributed.Process.Extras import Control.Distributed.Process.Extras.Time import Control.Distributed.Process.ManagedProcess import Data.Binary import Data.Typeable (Typeable) import GHC.Generics -------------------------------------------------------------------------------- -- Types -- -------------------------------------------------------------------------------- -- Call and Cast request types. Response types are unnecessary as the GenProcess -- API uses the Async API, which in turn guarantees that an async handle can -- /only/ give back a reply for that *specific* request through the use of an -- anonymous middle-man (as the sender and receiver in our case). data Increment = Increment deriving (Typeable, Generic, Eq, Show) instance Binary Increment where data Fetch = Fetch deriving (Typeable, Generic, Eq, Show) instance Binary Fetch where data Reset = Reset deriving (Typeable, Generic, Eq, Show) instance Binary Reset where type State = Int -------------------------------------------------------------------------------- -- API -- -------------------------------------------------------------------------------- -- | Increment count incCount :: ProcessId -> Process Int incCount sid = call sid Increment -- | Get the current count - this is replicating what 'call' actually does getCount :: ProcessId -> Process Int getCount sid = call 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 $ serve startCount init' server where init' :: InitHandler Int Int init' count = return $ InitOk count Infinity -------------------------------------------------------------------------------- -- Implementation -- -------------------------------------------------------------------------------- serverDefinition :: ProcessDefinition State serverDefinition = defaultProcess { apiHandlers = [ handleCallIf (condition (\count Increment -> count >= 10))-- invariant (\_ (_ :: Increment) -> haltMaxCount) , handleCall handleIncrement , handleCall (\count Fetch -> reply count count) , handleCast (\_ Reset -> continue 0) ] } :: ProcessDefinition State haltMaxCount :: Reply Int State haltMaxCount = haltNoReply_ (ExitOther "Count > 10") handleIncrement :: CallHandler State Increment Int handleIncrement count Increment = let next = count + 1 in continue next >>= replyWith next