packages feed

hactors (empty) → 0.0.3.1

raw patch · 9 files changed

+415/−0 lines, 9 filesdep +basedep +stmsetup-changed

Dependencies added: base, stm

Files

+ Control/Concurrent/Actor.hs view
@@ -0,0 +1,165 @@++-- |+-- Implementation of the actor model on top of the GHC's concurrency.+-- +-- The API mimics Erlang's concurrency primitives, with slight differences.+-- +module Control.Concurrent.Actor where++import Control.Monad+import Control.Monad.STM+import Control.Concurrent+import Control.Concurrent.STM.TChan+import Control.Exception++-- -----------------------------------------------------------------------------+-- * Processes++-- | The process is a VM's thread.+type Process = ThreadId++-- | Get the current process.+self :: IO Process+self = myThreadId++-- | Kill the process.+kill :: IO Process -> IO ()+kill = (>>= killThread)++-- | Finish the current process.+exit :: IO ()+exit = kill self++-- | Perform non busy waiting for a given number of microseconds in the current+-- process.+sleep :: Int -> IO ()+sleep = threadDelay++-- | Perform an infinite non busy waiting in the current process.+wait :: IO ()+wait = forever $ sleep slice+  where slice = maxBound :: Int++-- | Print string to @stdout@ with the current process ID prefix.+say :: String -> IO ()+say s = do+  me <- self+  putStrLn $ show me ++ ": " ++ s++-- -----------------------------------------------------------------------------+-- * The message box++-- | The message box is represented by the STM's channel.+type MBox m = TChan m++-- -----------------------------------------------------------------------------+-- * Actors++-- | The actor is a process associated with the message box.+-- +-- Note that the actor is parameterized by the type of message that it can+-- accept.+-- +data Actor m = Actor+  { proc :: Process                     -- ^ Actor's process (a thread).+  , mbox :: MBox m                      -- ^ Actor's message box (a channel).+  }++instance Eq (Actor m) where+  (Actor pid _) == (Actor pid' _) = pid == pid'++instance Ord (Actor m) where+  (Actor pid _) < (Actor pid' _) = pid < pid'++instance Show (Actor m) where+  show (Actor pid _) = show pid++-- | Create a new actor from a function, send the initial argument and the+-- message box to this actor via function arguments.+-- +-- This function calls @forkIO@.+-- +actor :: t -> (t -> MBox m -> IO a) -> IO (Actor m)+actor i f = do+  m <- newTChanIO+  p <- forkIO $ f i m >> return ()+  return $ Actor p m++-- | Create a new actor from a function, send the message box to this actor via+-- function argument.+-- +-- This function calls @forkIO@.+-- +spawn :: (MBox m -> IO a) -> IO (Actor m)+spawn = actor () . const++-- -----------------------------------------------------------------------------+-- * Messages++infixl 1 ?, <?+infixr 2 !, <!, !>, <!>++-- | Wait for an asynchronous message in the message box.+receive :: MBox m -> (m -> IO a) -> IO b+receive mb f = forever $ atomically (readTChan mb) >>= f++-- | Infix variant of @receive@.+(?) :: MBox m -> (m -> IO a) -> IO b+(?) = receive++-- | Variant of (?) with the message box inside the IO.+(<?) :: IO (MBox m) -> (m -> IO a) -> IO b+mb <? f = mb >>= (? f)++-- | Send a message to the actor.+send :: Actor m -> m -> IO m+send a m = atomically $ mbox a `writeTChan` m >> return m++-- | Infix variant of @send@.+(!) :: Actor m -> m -> IO m+(!) = send++-- | Variant of (!) with the actor inside the IO.+(<!) :: IO (Actor m) -> m -> IO m+a <! m = a >>= (! m)++-- | Variant of (!) with the message inside the IO.+(!>) :: Actor m -> IO m -> IO m+a !> m = m >>= (a !)++-- | Variant of (!) with the actor and the message inside the IO.+(<!>) :: IO (Actor m) -> IO m -> IO m+a <!> m = a >>= \a' -> m >>= (a' !)++-- -----------------------------------------------------------------------------+-- * Combine actors with messages++-- | Create a new receiving actor.+-- +-- This function calls @forkIO@.+-- +spawn_receive :: (m -> IO a) -> IO (Actor m)+spawn_receive f = spawn (? f)++-- -----------------------------------------------------------------------------+-- * Fault tolerance+-- +-- Where "fault" means having an exception in the thread. Still, AFAIK GHC's+-- runtime can't survive if some thread has segmentation fault (e.g. perform+-- (unsafeCoerce id)).+--++-- | Perform an action, on exceptions perform a given action @f@.+on_exception :: IO a -> IO a -> IO a+on_exception f = handle $ \e -> let _ = e :: SomeException in f++-- | Perform an action ignoring any exceptions in it.+tolerant :: IO a -> IO a+tolerant = on_exception $ return undefined++-- | Perform an action, do @exit@ on exceptions.+-- +-- XXX Bad name?+-- +faultable :: IO () -> IO ()+faultable = on_exception exit
+ Control/Concurrent/Actor/Debug.hs view
@@ -0,0 +1,22 @@++-- |+-- This module reimplement some functions from the @Control.Concurrent.Actor@+-- module with debug features.+-- +module Control.Concurrent.Actor.Debug where++import Control.Concurrent.Actor hiding ( receive, spawn_receive )++import Control.Monad+import Control.Monad.STM+import Control.Concurrent.STM.TChan++-- | Variant of @receive@ with the test printing.+receive :: MBox m -> (m -> IO a) -> IO b+receive a f = forever $ do+  say "receiving..." +  atomically (readTChan a) >>= f++-- | Variant of @spawn_receive@ with the test printing.+spawn_receive :: (m -> IO a) -> IO (Actor m)+spawn_receive f = spawn $ \m -> receive m f
+ Control/Concurrent/Actor/Examples.hs view
@@ -0,0 +1,83 @@++module Control.Concurrent.Actor.Examples where++import System.IO+import Control.Concurrent.Actor++-- -----------------------------------------------------------------------------+-- * Spawning new processes++-- | Using @spawn@.+spawn_1 :: IO ()+spawn_1 = do+  hSetBuffering stdout LineBuffering+  child <- spawn $ const $ say $ "  Hi, I'am the child."+  say $ "I was create a child with PID = " ++ show child++-- > spawn_1+-- ThreadId 44:   Hi, I'am the child.+-- ThreadId 43: I was create a child with PID = ThreadId 44++-- | Make an initial argument type.+data Child = Child String Process++-- | Using @actor@.+actor_1 :: IO ()+actor_1 = do+  hSetBuffering stdout LineBuffering+  me <- self+  say $ "Hi, I'am the parrent."+  child <- actor (Child "child" me) $+    \(Child name parent) _ -> do+      say $ "  Hi, I'am the one who was called " ++ name+      say $ "  My parrent's PID = " ++ show parent+  say $ "I was create a child with PID = " ++ show child++-- > actor_1+-- ThreadId 46: Hi, I'am the parrent.+-- ThreadId 47:   Hi, I'am the one who was called child+-- ThreadId 46: I was create a child with PID = ThreadId 47+-- ThreadId 47:   My parrent's PID = ThreadId 46++-- -----------------------------------------------------------------------------+-- * Communicate+    +-- |+-- There is some Erlang code from the \"Learn You Some Erlang\" book:+-- +-- @+-- dolphin() ->+--   receive+--     do_a_flip ->+--       io:format(\"How about no?~n\");+--     fish ->+--       io:format(\"So long and thanks for all the fish!~n\");+--     _ ->+--       io:format(\"Heh, we're smarter than you humans.~n\")+--   end+-- @+-- +dolphin :: String -> IO ()+dolphin "do a flip" = say "How about no?"+dolphin "fish"      = say "So long and thanks for all the fish!"+dolphin _           = say "Heh, we're smarter than you humans."++test_dolphin :: IO ()+test_dolphin = do+  hSetBuffering stdout LineBuffering+  dol <- spawn_receive dolphin+  dol ! "do a flip"+  dol ! "fish"+  dol ! "oh, hello dolphin!"+  return ()++-- > test_dolphin+-- ThreadId 58: How about no?+-- ThreadId 58: So long and thanks for all the fish!+-- ThreadId 58: Heh, we're smarter than you humans.++-- XXX can't send messages to ourselves like this:+-- +-- send_1 = self <! "hello"+-- +-- send_2 = self <!> self <! "hello"
+ Control/Concurrent/Actor/Tests.hs view
@@ -0,0 +1,52 @@++module Control.Concurrent.Actor.Tests where++import Control.Concurrent.Actor hiding ( receive, spawn_receive )+import Control.Concurrent.Actor.Debug++-- -----------------------------------------------------------------------------+-- * @receive@ is non busy++test_receive_1 :: IO ()+test_receive_1 = do+  act <- spawn_receive $+    \msg -> case msg of+      "ok?" -> putStrLn "ok"+      _     -> putStrLn "nothing"+  act ! "ok?"+  act ! "ok?"+  act ! "what?"+  return ()++-- > test_receive_1+-- ThreadId 39: receiving...+-- ok+-- ThreadId 39: receiving...+-- ok+-- ThreadId 39: receiving...+-- nothing+-- ThreadId 39: receiving...++-- Thus, the @receive@ function don't perform busy waiting.++-- -----------------------------------------------------------------------------+-- * @tolerant@ handle exceptions++test_tolerant_1 :: IO ()+test_tolerant_1 = do+  act <- spawn_receive $+    \msg -> tolerant $ case msg of+      True  -> putStrLn "ok"+      False -> putStrLn $ tail []+  act ! False+  act ! True+  act ! True+  return ()++-- > test_tolerant_1+-- ThreadId 31: receiving...+-- ThreadId 31: receiving...+-- ok+-- ThreadId 31: receiving...+-- ok+-- ThreadId 31: receiving...
+ LICENSE view
@@ -0,0 +1,19 @@++Copyright (c) 2012, Heka Treep++Permission is hereby granted, free of charge, to any person obtaining a copy of+this software and associated documentation files (the "Software"), to deal in+the Software without restriction, including without limitation the rights to+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of+the Software, and to permit persons to whom the Software is furnished to do so,+subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Makefile view
@@ -0,0 +1,17 @@++all:+	runhaskell Setup configure --user+	runhaskell Setup build++install: all+	runhaskell Setup install++doc: all+	cabal haddock+	mv ./dist/doc/html/hactors ./doc++tar:+	cabal sdist++clean:+	rm -rf dist
+ README.md view
@@ -0,0 +1,20 @@++About+=====++This library is about to implement the actor model on top of the GHC's+concurrency. Actors works as VM's lightweight threads and messages works with+STM's channels.++Usage+=====++The *Control.Concurrent.Actor* module provides the API that mimics Erlang's+concurrency primitives. See the haddocks for more details.++Issues+======++* Implement the *flush* funtion (to show already received messages)?++* Write more examples.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ hactors.cabal view
@@ -0,0 +1,35 @@++name:                                   hactors+version:                                0.0.3.1+synopsis:                               Practical actors for Haskell.+category:                               Concurrency+description:++  This library is about to implement the Erlang-like actor model on top of the+  GHC's concurrency.++author:                                 Heka Treep+maintainer:                             Heka Treep <zena.treep@gmail.com>+homepage:                               https://github.com/treep/hactors+bug-reports:                            https://github.com/treep/hactors/issues+license:                                MIT+license-file:                           LICENSE++stability:                              Experimental+cabal-version:                          >= 1.6+build-type:                             Simple++extra-source-files:                     Makefile+                                        README.md++source-repository                       head+  type:                                 git+  location:                             git://github.com/treep/hactors.git++library+  ghc-options:                          -Wall -fno-warn-unused-do-bind+  build-depends:                        base == 4.*, stm == 2.2.*+  exposed-modules:                      Control.Concurrent.Actor+                                        Control.Concurrent.Actor.Debug+                                        Control.Concurrent.Actor.Tests+                                        Control.Concurrent.Actor.Examples