{-# LANGUAGE BangPatterns, ScopedTypeVariables, GADTs #-} -- -- Copyright (c) 2009 Alex Mason - http://axman6.homeip.net/blog/ -- BSD licence - http://www.opensource.org/licenses/bsd-license.php -- -- |AVars are a form of transactional variables. They internally use a tail -- recursive function to carry the 'state' of the variable, and allow for -- use in concurrent systems, where actions are guaranteed to happen. They -- are designed to cope with exceptions thrown by any modifying functions; -- any exception thrown during a transaction will either be passed back to -- the caller or ignored, and the variable keeps on running. -- -- They are handy for applications like keeping track of resources by -- incrementing and decrementing the variable. They should not be used in -- a way which you would read the variable, then modify it based on the -- 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 ( AVar, putAVar, modAVar, modAVar', justModAVar, getAVar, condModAVar, swapAVar, newAVar) where import Data.AVar.Internal import Control.Concurrent import Control.Concurrent.MVar import Control.Concurrent.Chan import qualified Control.Exception as E -- |'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 returns Nothing if nothing went -- wrong, or Just e, where e is an exception thrown by the function. modAVar :: AVar a -> (a -> a) -> IO (Maybe E.SomeException) modAVar (AVar chan) f = do res <- newEmptyMVar writeChan chan (Mod f res) takeMVar res -- |'modAVar'' is like modAVar, but it modifies the variable, along with -- returning a result of type b, within an Either e b. modAVar' :: AVar a -> (a -> (a,b)) -> IO (Either E.SomeException b) modAVar' (AVar chan) f = do res <- newEmptyMVar writeChan chan (Mod' f res) takeMVar res -- |'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 if true 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 (Either E.SomeException Bool) condModAVar (AVar chan) p t f = do res <- newEmptyMVar writeChan chan (Atom p t f res) takeMVar res -- |'swapAVar' takes a new value, puts it into the AVar, and returns the old value. swapAVar :: (AVar a) -> a -> IO (Either E.SomeException a) swapAVar (AVar chan) new = do res <- newEmptyMVar writeChan chan (Mod' (\old -> (new, old)) res) takeMVar res