hactor 1.0.0.1 → 1.1.0.0
raw patch · 3 files changed
+217/−154 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Control.Concurrent.Actor: instance ActorMessage ()
+ Control.Concurrent.Actor: receiveSTM :: ActorMessage msg => ActorM msg (STM msg)
+ Control.Concurrent.Actor.Internal: ActorContext :: MailBox msg -> ActorContext msg
+ Control.Concurrent.Actor.Internal: ActorHandle :: ActorContext msg -> ThreadId -> ActorHandle msg
+ Control.Concurrent.Actor.Internal: acMailBox :: ActorContext msg -> MailBox msg
+ Control.Concurrent.Actor.Internal: actorThread :: ActorMessage msg => ActorHandle msg -> ThreadId
+ Control.Concurrent.Actor.Internal: ahContext :: ActorHandle msg -> ActorContext msg
+ Control.Concurrent.Actor.Internal: ahThread :: ActorHandle msg -> ThreadId
+ Control.Concurrent.Actor.Internal: class ActorMessage msg
+ Control.Concurrent.Actor.Internal: data ActorMessage msg => ActorContext msg
+ Control.Concurrent.Actor.Internal: data ActorMessage msg => ActorHandle msg
+ Control.Concurrent.Actor.Internal: getContext :: ActorMessage msg => ActorM msg (ActorContext msg)
+ Control.Concurrent.Actor.Internal: getMailBox :: ActorMessage msg => ActorM msg (MailBox msg)
+ Control.Concurrent.Actor.Internal: instance ActorMessage ()
+ Control.Concurrent.Actor.Internal: receive :: ActorMessage msg => ActorM msg (msg)
+ Control.Concurrent.Actor.Internal: receiveMaybe :: ActorMessage msg => ActorM msg (Maybe msg)
+ Control.Concurrent.Actor.Internal: receiveSTM :: ActorMessage msg => ActorM msg (STM msg)
+ Control.Concurrent.Actor.Internal: runActor :: ActorMessage msg => ActorM msg () -> IO ()
+ Control.Concurrent.Actor.Internal: self :: ActorMessage msg => ActorM msg (ActorHandle msg)
+ Control.Concurrent.Actor.Internal: send :: (ActorMessage msg, ActorMessage msg') => ActorHandle msg -> msg -> ActorM msg' ()
+ Control.Concurrent.Actor.Internal: sendIO :: ActorMessage msg => ActorHandle msg -> msg -> IO ()
+ Control.Concurrent.Actor.Internal: spawnActor :: ActorMessage msg => ActorM msg () -> IO (ActorHandle msg)
+ Control.Concurrent.Actor.Internal: type ActorM msg = ReaderT (ActorContext msg) IO
+ Control.Concurrent.Actor.Internal: type MailBox msg = TChan msg
Files
- Control/Concurrent/Actor.hs +2/−152
- Control/Concurrent/Actor/Internal.hs +202/−0
- hactor.cabal +13/−2
Control/Concurrent/Actor.hs view
@@ -20,6 +20,7 @@ -- * Receiving Messages , receive , receiveMaybe+ , receiveSTM -- * Spawning Actors , spawnActor , runActor@@ -28,156 +29,5 @@ , actorThread ) where -import Control.Concurrent-import Control.Concurrent.STM-import Control.Monad.Reader----- {{{ Types---- {{{ Message---- | The `ActorMessage` class must be implemented by any type that will be sent--- as a message to actors.--- Any given type of actor will have one `ActorMessage` type that is sent to--- that actor. This ensures type safety.--- Currently this is simply a dummy class with nothing in it, but things may be--- added in the future.-class ActorMessage msg---- Allow actors that don't take messages.-instance ActorMessage ()---- }}}---- {{{ Handle and context---- | An `ActorHandle` acts as a reference to a specific actor.-data ActorMessage msg => ActorHandle msg = ActorHandle- { ahContext :: ActorContext msg -- Context for this handle's actor.- , ahThread :: ThreadId -- The actor's thread ID.- }---- | The `ActorContext` holds shared information about a given actor.--- This is information such as the actor's mail box, the list of actors it's--- linked to, etc.-data ActorMessage msg => ActorContext msg = ActorContext- { acMailBox :: MailBox msg -- Channel for the actor's messages.- }---- | The type for the actor's mail box.-type MailBox msg = TChan msg---- }}}---- }}}---- | The base actor monad.-type ActorM msg = ReaderT (ActorContext msg) IO---- {{{ Get info---- | Gets a handle to the current actor.-self :: ActorMessage msg => ActorM msg (ActorHandle msg)-self = do- context <- ask- thread <- liftIO $ myThreadId- return $ ActorHandle context thread---- | Retrieves the mail box for the current actor.-myMailBox :: ActorMessage msg => ActorM msg (MailBox msg)-myMailBox = asks acMailBox---- }}}---- {{{ Receiving---- | Reads a message from the actor's mail box.--- If there are no messages, blocks until one is received. If you don't want--- this, use `receiveMaybe` instead.-receive :: ActorMessage msg => ActorM msg (msg)-receive = do- chan <- myMailBox- -- Read from the channel, retrying if there is nothing to read.- liftIO $ atomically $ readTChan chan---- | Reads a message from the actor's mail box.--- If there are no messages, returns `Nothing`.-receiveMaybe :: ActorMessage msg => ActorM msg (Maybe msg)-receiveMaybe = do- chan <- myMailBox- liftIO $ atomically $ tryReadTChan chan---- }}}---- {{{ Sending---- | Sends a message to the given actor handle.--- This is secretly just `sendIO` lifted into an actor monad.-send :: (ActorMessage msg, ActorMessage msg') =>- ActorHandle msg -> msg -> ActorM msg' ()-send hand msg = liftIO $ sendIO hand msg---- | Sends a message to the given actor handle from within the IO monad.-sendIO :: ActorMessage msg => ActorHandle msg -> msg -> IO ()-sendIO hand msg =- atomically $ writeTChan mailBox $ msg- where- mailBox = handleMailBox hand---- }}}---- {{{ Spawning---- | Internal function for starting actors.--- This takes an `ActorM` action, makes a channel for it, wraps it in exception--- handling stuff, and turns it into an IO monad. The function returns a tuple--- containing the actor's context and the IO action to execute the actor.-wrapActor :: ActorMessage msg => ActorM msg () -> IO (IO (), ActorContext msg)-wrapActor actorAction = do- -- TODO: Exception handling.- -- First, create a channel for the actor.- chan <- atomically newTChan- -- Next, create the context and run the ReaderT action.- let context = ActorContext chan- ioAction = runReaderT actorAction context- -- Return the information.- return (ioAction, context)----- | Spawns the given actor on another thread and returns a handle to it.-spawnActor :: ActorMessage msg => ActorM msg () -> IO (ActorHandle msg)-spawnActor actorAction = do- -- Wrap the actor action.- (ioAction, context) <- wrapActor actorAction- -- Fork the actor's IO action to another thread.- thread <- forkIO ioAction- -- Return the handle.- return $ ActorHandle context thread---- | Runs the given actor on the current thread.--- This function effectively turns the current thread into the actor's thread.--- Obviously, this means that this function will block until the actor exits.--- You probably want to use this for your "main" actor.-runActor :: ActorMessage msg => ActorM msg () -> IO ()-runActor actorAction = do- -- Wrap the actor action. We discard the context, because we won't be- -- returning a handle to this actor.- (ioAction, _) <- wrapActor actorAction- -- Execute the IO action on the current thread.- ioAction---- }}}---- {{{ Utility functions---- | Gets the mail box for the given handle.-handleMailBox :: ActorMessage msg => ActorHandle msg -> MailBox msg-handleMailBox = acMailBox . ahContext---- | Gets the thread ID for the given actor handle.-actorThread :: ActorMessage msg => ActorHandle msg -> ThreadId-actorThread = ahThread---- }}}+import Control.Concurrent.Actor.Internal
+ Control/Concurrent/Actor/Internal.hs view
@@ -0,0 +1,202 @@+-- |+-- Module : Control.Concurrent.Actor.Internal+-- Copyright : (c) 2014 Forkk+-- License : MIT+-- Maintainer : forkk@forkk.net+-- Stability : experimental+-- Portability : GHC only (requires throwTo)+--+-- Module exposing more of hactor's internals. Use with caution.+--+module Control.Concurrent.Actor.Internal+ (+ -- * Types+ ActorHandle (..)+ , ActorMessage+ , ActorM+ -- * Sending Messages+ , send+ , sendIO+ -- * Receiving Messages+ , receive+ , receiveMaybe+ , receiveSTM+ -- * Spawning Actors+ , spawnActor+ , runActor+ -- * Getting Information+ , self+ , actorThread+ -- * Internals+ , ActorContext (..)+ , MailBox+ , getContext+ , getMailBox+ ) where++import Control.Concurrent+import Control.Concurrent.STM+import Control.Monad.Reader+++-- {{{ Types++-- {{{ Message++-- | The @ActorMessage@ class must be implemented by any type that will be sent+-- as a message to actors.+-- Any given type of actor will have one @ActorMessage@ type that is sent to+-- that actor. This ensures type safety.+-- Currently this is simply a dummy class with nothing in it, but things may be+-- added in the future.+class ActorMessage msg++-- Allow actors that don't take messages.+instance ActorMessage ()++-- }}}++-- {{{ Handle and context++-- | An @ActorHandle@ acts as a reference to a specific actor.+data ActorMessage msg => ActorHandle msg = ActorHandle+ { ahContext :: ActorContext msg -- Context for this handle's actor.+ , ahThread :: ThreadId -- The actor's thread ID.+ }++-- | The @ActorContext@ holds shared information about a given actor.+-- This is information such as the actor's mail box, the list of actors it's+-- linked to, etc.+data ActorMessage msg => ActorContext msg = ActorContext+ { acMailBox :: MailBox msg -- Channel for the actor's messages.+ }++-- | The type for the actor's mail box.+type MailBox msg = TChan msg++-- }}}++-- }}}++-- | The base actor monad.+type ActorM msg = ReaderT (ActorContext msg) IO++-- {{{ Get info++-- | Gets a handle to the current actor.+self :: ActorMessage msg => ActorM msg (ActorHandle msg)+self = do+ context <- ask+ thread <- liftIO $ myThreadId+ return $ ActorHandle context thread++-- | Retrieves the mail box for the current actor.+-- This is an internal function and may be dangerous. Use with caution.+getMailBox :: ActorMessage msg => ActorM msg (MailBox msg)+getMailBox = asks acMailBox+++-- | Gets the internal context object for the current actor.+-- This is an internal function and may be dangerous. Use with caution.+getContext :: ActorMessage msg => ActorM msg (ActorContext msg)+getContext = ask++-- }}}++-- {{{ Receiving++-- | Reads a message from the actor's mail box.+-- If there are no messages, blocks until one is received. If you don't want+-- this, use @receiveMaybe@ instead.+receive :: ActorMessage msg => ActorM msg (msg)+receive = do+ chan <- getMailBox+ -- Read from the channel, retrying if there is nothing to read.+ liftIO $ atomically $ readTChan chan++-- | Reads a message from the actor's mail box.+-- If there are no messages, returns @Nothing@.+receiveMaybe :: ActorMessage msg => ActorM msg (Maybe msg)+receiveMaybe = do+ chan <- getMailBox+ liftIO $ atomically $ tryReadTChan chan++-- | An @ActorM@ action which returns an @STM@ action to receive a message.+receiveSTM :: ActorMessage msg => ActorM msg (STM msg)+receiveSTM = do+ chan <- getMailBox+ return $ readTChan chan++-- }}}++-- {{{ Sending++-- | Sends a message to the given actor handle.+-- This is secretly just @sendIO@ lifted into an actor monad.+send :: (ActorMessage msg, ActorMessage msg') =>+ ActorHandle msg -> msg -> ActorM msg' ()+send hand msg = liftIO $ sendIO hand msg++-- | Sends a message to the given actor handle from within the IO monad.+sendIO :: ActorMessage msg => ActorHandle msg -> msg -> IO ()+sendIO hand msg =+ atomically $ writeTChan mailBox $ msg+ where+ mailBox = handleMailBox hand++-- }}}++-- {{{ Spawning++-- | Internal function for starting actors.+-- This takes an @ActorM@ action, makes a channel for it, wraps it in exception+-- handling stuff, and turns it into an IO monad. The function returns a tuple+-- containing the actor's context and the IO action to execute the actor.+wrapActor :: ActorMessage msg => ActorM msg () -> IO (IO (), ActorContext msg)+wrapActor actorAction = do+ -- TODO: Exception handling.+ -- First, create a channel for the actor.+ chan <- atomically newTChan+ -- Next, create the context and run the ReaderT action.+ let context = ActorContext chan+ ioAction = runReaderT actorAction context+ -- Return the information.+ return (ioAction, context)+++-- | Spawns the given actor on another thread and returns a handle to it.+spawnActor :: ActorMessage msg => ActorM msg () -> IO (ActorHandle msg)+spawnActor actorAction = do+ -- Wrap the actor action.+ (ioAction, context) <- wrapActor actorAction+ -- Fork the actor's IO action to another thread.+ thread <- forkIO ioAction+ -- Return the handle.+ return $ ActorHandle context thread++-- | Runs the given actor on the current thread.+-- This function effectively turns the current thread into the actor's thread.+-- Obviously, this means that this function will block until the actor exits.+-- You probably want to use this for your "main" actor.+runActor :: ActorMessage msg => ActorM msg () -> IO ()+runActor actorAction = do+ -- Wrap the actor action. We discard the context, because we won't be+ -- returning a handle to this actor.+ (ioAction, _) <- wrapActor actorAction+ -- Execute the IO action on the current thread.+ ioAction++-- }}}++-- {{{ Utility functions++-- | Gets the mail box for the given handle.+handleMailBox :: ActorMessage msg => ActorHandle msg -> MailBox msg+handleMailBox = acMailBox . ahContext++-- | Gets the thread ID for the given actor handle.+actorThread :: ActorMessage msg => ActorHandle msg -> ThreadId+actorThread = ahThread++-- }}}+
hactor.cabal view
@@ -8,7 +8,7 @@ -- +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 1.0.0.1+version: 1.1.0.0 -- A short (one-line) description of the package. synopsis: Lightweight Erlang-style actors for Haskell.@@ -47,9 +47,20 @@ -- Constraint on the version of Cabal needed to build this package. cabal-version: >=1.20 +source-repository head+ type: git+ location: https://github.com/Forkk/hactor.git++source-repository this+ type: git+ location: https://github.com/Forkk/hactor.git+ tag: 1.1.0.0+ library -- Modules exported by the library.- exposed-modules: Control.Concurrent.Actor+ exposed-modules:+ Control.Concurrent.Actor+ , Control.Concurrent.Actor.Internal -- Packages needed in order to build this package. build-depends: