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 +15/−3
- Data/AVar.hs +4/−105
- Data/AVar/Internal.hs +116/−0
- Data/AVar/Unsafe.hs +91/−0
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