packages feed

AVar 0.0.3 → 0.0.4

raw patch · 4 files changed

+226/−108 lines, 4 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Data.AVar: Atom :: (a -> Bool) -> (a -> a) -> (a -> a) -> (MVar (Either SomeException Bool)) -> Transaction a
- Data.AVar: Get :: (MVar a) -> Transaction a
- Data.AVar: JustMod :: (a -> a) -> Transaction a
- Data.AVar: Mod :: (a -> a) -> (MVar (Maybe SomeException)) -> Transaction a
- Data.AVar: Mod' :: (a -> (a, b)) -> (MVar (Either SomeException b)) -> Transaction a
- Data.AVar: Put :: a -> Transaction a
- Data.AVar: data Transaction a
- Data.AVar: newAVar :: a -> IO (AVar a)
- Data.AVar: putMVar :: MVar a -> a -> IO ()
+ Data.AVar.Internal: AVar :: (Chan (Transaction a)) -> AVar a
+ Data.AVar.Internal: Atom :: (a -> Bool) -> (a -> a) -> (a -> a) -> (MVar (Either SomeException Bool)) -> Transaction a
+ Data.AVar.Internal: Get :: (MVar a) -> Transaction a
+ Data.AVar.Internal: JustMod :: (a -> a) -> Transaction a
+ Data.AVar.Internal: Mod :: (a -> a) -> (MVar (Maybe SomeException)) -> Transaction a
+ Data.AVar.Internal: Mod' :: (a -> (a, b)) -> (MVar (Either SomeException b)) -> Transaction a
+ Data.AVar.Internal: Put :: a -> Transaction a
+ Data.AVar.Internal: data AVar a
+ Data.AVar.Internal: data Transaction a
+ Data.AVar.Internal: newAVar :: a -> IO (AVar a)
+ Data.AVar.Unsafe: OK :: Result
+ Data.AVar.Unsafe: condModAVar :: AVar a -> (a -> Bool) -> (a -> a) -> (a -> a) -> IO Bool
+ Data.AVar.Unsafe: data AVar a
+ Data.AVar.Unsafe: data Result
+ Data.AVar.Unsafe: getAVar :: AVar a -> IO a
+ Data.AVar.Unsafe: justModAVar :: AVar a -> (a -> a) -> IO ()
+ Data.AVar.Unsafe: modAVar :: AVar a -> (a -> a) -> IO ()
+ Data.AVar.Unsafe: modAVar' :: AVar a -> (a -> (a, b)) -> IO b
+ Data.AVar.Unsafe: putAVar :: AVar a -> a -> IO ()
+ Data.AVar.Unsafe: swapAVar :: (AVar a) -> a -> IO a

Files

AVar.cabal view
@@ -1,17 +1,29 @@ Name:                   AVar-Version:                0.0.3+Version:                0.0.4 Category:               Concurrency Synopsis:               Mutable variables with Exception handling and concurrency support.-Description:            AVars emulate mutable variables, by providing a queue based interface to interacting with the variable. Each variable runs a 'handler' function, which reads requests from a queue and processes them one by one. They can be used in concurrent systems safely, and should handle exceptions thrown by modifying functions gracefully.+Description:            AVars emulate mutable variables, by providing a queue based+                        interface to interacting with the variable. Each variable runs+                        a 'handler' function, which reads requests from a queue and+                        processes them one by one. They can be used in concurrent+                        systems safely, and should handle exceptions thrown by+                        modifying functions gracefully.+                        +                        There is also an unsafe interface through Data.AVar.Unsafe,+                        which will throw any errors encountered while modifying+                        the variable. License:                BSD3 License-file:           LICENSE.txt Author:                 Alex Mason Maintainer:             Alex Mason <axman6@gmail.com> build-type:             Simple Cabal-Version:          >= 1.2+Extra-Source-Files:+        Data/AVar/Internal.hs  Library         Build-Depends:                 base >= 4.0.0.0         Exposed-modules:-                Data.AVar+                Data.AVar, Data.AVar.Unsafe+
Data/AVar.hs view
@@ -17,125 +17,24 @@ -- result recieved, but rather using the provided functions. If this was -- not done, the variable's value is very likely to have changed in the -- mean time.+--+-- If you're after a more unsafe interface to AVars, see Data.AVar.Unsafe,+-- which will throw the errors returned fromt he variable.  module Data.AVar (-    -- * Types     AVar,-    Transaction(..),-    -- * functions on AVars     putAVar,-    newAVar,-    putMVar,     modAVar,     modAVar',     justModAVar,     getAVar,     condModAVar,     swapAVar) where+import Data.AVar.Internal import Control.Concurrent import Control.Concurrent.MVar import Control.Concurrent.Chan import qualified Control.Exception as E---- * Types---- |A 'Transaction' describes what should happen to a variable.--- They are only used internally, and are here just for reference.-data Transaction a =-      Put a-    -- ^puts the a into the variable-    | Get (MVar a)-    -- ^reads the variable-    | Mod (a -> a) (MVar (Maybe E.SomeException))-    -- ^modifies the variable-    | JustMod (a -> a)-    -- ^ Just modifies the variable (unless an exception occurs)-    | forall b. Mod' (a -> (a,b)) (MVar (Either E.SomeException b))-    -- ^ modifies the variable, returning the b result to the caller-    | Atom (a -> Bool) (a -> a) (a -> a) (MVar (Either E.SomeException Bool))-    -- ^conditionally modifies a variable--    --Swap a (MVar a)---- |'AVar's are the means through communication with the variable are conducted.--- They contain a Chan that is 'connected' to the variable, and is read by the--- variable's 'handler' function.-data AVar a = AVar (Chan (Transaction a)) ---- * Functions on AVars---- |'newAVar' creates a new variable. It forks off the 'handler' that does the--- work for the variable itself and creates a new AVar.-newAVar :: a -> IO (AVar a)-newAVar x = do-    E.evaluate x-    chan <- newChan :: IO (Chan (Transaction a))-    forkIO (handler chan x)-    return (AVar chan)---- |'handler' stores the state of the variable in an accumulatin parameter.--- It reads the chan it was forked with, and takes action depending on the--- Transaction is recieves. Handler is not something to be used outside of--- an AVar, and is not exported.-handler :: Chan (Transaction a) -> a -> IO b -handler chan !x = do-  req <- readChan chan-  case req of-    Put a         -> handler chan a-    Get mvar      -> do-        putMVar mvar x-        handler chan x-    -    Mod f mvar    -> do-        let x' = f x-        p <- E.catch (E.evaluate x' >> return Nothing)-                     (\e -> return (Just e))-        putMVar mvar p-        case p of-          Nothing -> handler chan x'-          _       -> handler chan x-    JustMod f     -> do-        let x' = f x-        res <- E.try (E.evaluate x')-        case res of-            Right _                    -> handler chan x'-            Left  (_::E.SomeException) -> handler chan x-      -    Mod' f mvar    -> do-          let y@(a,b) = f x-          p <- E.try (E.evaluate a >> E.evaluate b)-          case p of-              Right _  -> do-                  putMVar mvar (Right b)-                  handler chan a-              (Left e) -> do-                  putMVar mvar (Left e)-                  handler chan x-    -    Atom test y n res -> do-        let t' = test x-            y' = y x-            n' = n x-        tres <- E.try (E.evaluate t')-        case tres of-            rT@(Right True) -> do-                run <- E.try (E.evaluate y')-                case run of-                    Right x' -> putMVar res rT >> handler chan x'-                    Left e   -> putMVar res (Left e) >> handler chan x-            rF@(Right False) -> do-                run <- E.try (E.evaluate n')-                case run of -                    Right x' -> putMVar res rF >> handler chan x'-                    Left e   -> putMVar res (Left e) >> handler chan x-            Left e           -> putMVar res (Left e) >> handler chan x-        -- if test x-        --     then do-        --         putMVar res True-        --         handler chan (y x)-        --       else do-        --           putMVar res False-        --           handler chan (n x)   -- |'getAVar' reads the current value inside the AVar.
+ Data/AVar/Internal.hs view
@@ -0,0 +1,116 @@+{-# LANGUAGE BangPatterns, ScopedTypeVariables, GADTs #-}++-- | The guts of how AVars work.++module Data.AVar.Internal (+    -- * Types+    AVar(..),+    Transaction(..),+    -- * functions on AVars+    newAVar,+    ) where+++import Control.Concurrent+import Control.Concurrent.MVar+import Control.Concurrent.Chan+import qualified Control.Exception as E++-- |A 'Transaction' describes what should happen to a variable.+-- They are only used internally, and are here just for reference.+data Transaction a =+      Put a+    -- ^puts the a into the variable+    | Get (MVar a)+    -- ^reads the variable+    | Mod (a -> a) (MVar (Maybe E.SomeException))+    -- ^modifies the variable+    | JustMod (a -> a)+    -- ^ Just modifies the variable (unless an exception occurs)+    | forall b. Mod' (a -> (a,b)) (MVar (Either E.SomeException b))+    -- ^ modifies the variable, returning the b result to the caller+    | Atom (a -> Bool) (a -> a) (a -> a) (MVar (Either E.SomeException Bool))+    -- ^conditionally modifies a variable++    --Swap a (MVar a)++-- |'AVar's are the means through which communication with the variable are+-- conducted. They contain a Chan that is 'connected' to the variable, and+-- is read by the variable's 'handler' function.+data AVar a = AVar (Chan (Transaction a)) ++-- * Functions on AVars++-- |'newAVar' creates a new variable. It forks off the 'handler' that does the+-- work for the variable itself and creates a new AVar.+newAVar :: a -> IO (AVar a)+newAVar x = do+    E.evaluate x+    chan <- newChan :: IO (Chan (Transaction a))+    forkIO (handler chan x)+    return (AVar chan)+++-- |'handler' stores the state of the variable in an accumulatin parameter.+-- It reads the chan it was forked with, and takes action depending on the+-- Transaction is recieves. Handler is not something to be used outside of+-- an AVar, and is not exported.+handler :: Chan (Transaction a) -> a -> IO b +handler chan !x = do+  req <- readChan chan+  case req of+    Put a         -> handler chan a+    Get mvar      -> do+        putMVar mvar x+        handler chan x++    Mod f mvar    -> do+        let x' = f x+        p <- E.catch (E.evaluate x' >> return Nothing)+                     (\e -> return (Just e))+        putMVar mvar p+        case p of+          Nothing -> handler chan x'+          _       -> handler chan x+    JustMod f     -> do+        let x' = f x+        res <- E.try (E.evaluate x')+        case res of+            Right _                    -> handler chan x'+            Left  (_::E.SomeException) -> handler chan x++    Mod' f mvar    -> do+          let y@(a,b) = f x+          p <- E.try (E.evaluate a >> E.evaluate b)+          case p of+              Right _  -> do+                  putMVar mvar (Right b)+                  handler chan a+              (Left e) -> do+                  putMVar mvar (Left e)+                  handler chan x++    Atom test y n res -> do+        let t' = test x+            y' = y x+            n' = n x+        tres <- E.try (E.evaluate t')+        case tres of+            rT@(Right True) -> do+                run <- E.try (E.evaluate y')+                case run of+                    Right x' -> putMVar res rT >> handler chan x'+                    Left e   -> putMVar res (Left e) >> handler chan x+            rF@(Right False) -> do+                run <- E.try (E.evaluate n')+                case run of +                    Right x' -> putMVar res rF >> handler chan x'+                    Left e   -> putMVar res (Left e) >> handler chan x+            Left e           -> putMVar res (Left e) >> handler chan x+        -- if test x+        --     then do+        --         putMVar res True+        --         handler chan (y x)+        --       else do+        --           putMVar res False+        --           handler chan (n x)
+ Data/AVar/Unsafe.hs view
@@ -0,0 +1,91 @@+-- |Data.AVar.Unsafe has a similar interface to Data.AVar, but instead of letting+-- the user handle exceptions from Eithers, it will throw exceptions caught by+-- the variable.++module Data.AVar.Unsafe (+    AVar,+    Result(..),+    getAVar,+    putAVar,+    modAVar,+    modAVar',+    justModAVar,+    condModAVar,+    swapAVar+    ) where++import Data.AVar.Internal+import Control.Concurrent+import Control.Concurrent.MVar+import Control.Concurrent.Chan+import qualified Control.Exception as E++data Result = OK++-- |'getAVar' reads the current value inside the AVar.+getAVar :: AVar a -> IO a+getAVar (AVar chan)   = do+    res <- newEmptyMVar+    writeChan chan (Get res)+    takeMVar res+++-- |'putAVar' replaces the currect value in the variable with the given x+putAVar :: AVar a -> a -> IO ()+putAVar (AVar chan) x = writeChan chan (Put x)++-- |'modAVar' takes a function from a to a, and modifies the variable. It will+-- throw any exceptions caught by the variable when applying the function.+modAVar :: AVar a -> (a -> a) -> IO ()+modAVar (AVar chan) f = do+    res <- newEmptyMVar+    writeChan chan (Mod f res)+    r <- takeMVar res+    case r of+        Nothing -> return ()+        Just e  -> E.throw e++-- |'modAVar'' is like modAVar, but it modifies the variable, along with+-- returning a result of type b. It also throws any errors caugh by the variable.+modAVar' :: AVar a -> (a -> (a,b)) -> IO b+modAVar' (AVar chan) f = do+    res <- newEmptyMVar+    writeChan chan (Mod' f res)+    r <- takeMVar res+    case r of+        Right b -> return b+        Left e  -> E.throw e++-- |'justModAVar' will attempt to run the given function on the variable.+-- It does not report back on its sucess or failure, and if the function+-- produces an exception, the variable is left unchanged. It should be+-- used when you just want to modify the variable, and keep running,+-- without waiting for the action to complete.+justModAVar :: AVar a -> (a -> a) -> IO ()+justModAVar (AVar chan) f = writeChan chan (JustMod f)++-- |'condModAVar' applies the first finction to the current value in the+-- AVar, and will modify the value using the second function if+-- it results in 'True', or the third function if it results in 'Fasle'.+condModAVar :: AVar a+            -> (a -> Bool)+            -> (a -> a)+            -> (a -> a)+            -> IO Bool+condModAVar (AVar chan) p t f = do+    res <- newEmptyMVar+    writeChan chan (Atom p t f res)+    r <- takeMVar res+    case r of+        Right x -> return x+        Left e  -> E.throw e++-- |'swapAVar' takes a new value, puts it into the AVar, and returns the old value.+swapAVar :: (AVar a) -> a -> IO a+swapAVar (AVar chan) new = do+    res <- newEmptyMVar+    writeChan chan (Mod' (\old -> (new, old)) res)+    r <- takeMVar res+    case r of+        Right a -> return a+        Left e  -> E.throw e