{-# OPTIONS -fglasgow-exts -XUndecidableInstances -XBangPatterns #-} {- | Data.TCache.Dynamic: A dynamic interface for TCache so that mixed datatypes can be managed participating in a single transaction. The objects are encapsulated in a 'IDynamic' datatype, that is d Dynamic type that is serializable and indexable Dynamic present essentially the same methods than Data.TCache. The added functionality is the management of IDynamic types. Any datatype that is instance of IResource and Typeable can be handled mixed with any other datatype. TCache.Dynamic is essentially a TCache working with a single datatype: IDynamic that is indexable and serializable. You donĀ“t need to do anything special except to define Typeable besides the IResource instance for your particular datatype. Also, before use, your datatype must be registered (with 'registerType', see example in the package). there are basically two types of methods in this module: * @with(STM)Resource(s)@ calls: manage one single type of data, in the same way than the naked @Data.TCache@ module, Are the same than Data.TCache. The marsalling to and from IDynamic is managed internally. These calls do exactly the same than the TCache calls with the same name these cals allows different modules to handle their particular kind of data without regard that it is being handled in the same cache with other datatypes. * @wthD(STM)Resource(s)@: are new, and handle the IDynamic type. The user must wrap your datatypes (with toIDyn) and unwap it (with fromIDyn) These call permts to handle arbitrary types at the same time and partticipate in transactions. There is also a useful 'Key' object whose purpose is to retrieve any objecto fo any datatype by its sting key Also the parameter @refcache@ has been dropped from the methods that used it (the syncronization methods) -} module Data.TCache.Dynamic( T.IResource(..) -- from TCache ,T.Resources(..) ,T.resources ,T.setCache ,T.refcache ,T.defaultCheck,T.readFileStrict ,IDynamic(..) -- serializable/indexable existential datatype ,T.Cache ,DynamicInterface ( toIDyn -- :: x -> IDynamic ,registerType -- :: x ,fromIDyn -- :: IDynamic -> x ,unsafeFromIDyn -- :: IDynamic -> x ,safeFromIDyn ---- :: IDynamic -> Maybe x ) --,ofType ,Key(..) {- Key datatype can be used to read any object trough the Dynamic interface let key= mst <- getDResource $ Key (ofType :: Type) key case mst of Nothing -> error $ "getResource: not found "++ key Just (idyn) -> do let st = fromIDyn idyn :: .... -} -- same access interface than TCache , this time for handling the Dynamic type. See Data.TCache for their equivalent definitions -- to use it you have to wrap (with toIDyn) and unwrap(with fromIDyn) your data in a IDynamic object ,getTVars, releaseTVars, getTVarsIO,withDResource, withDResources, withDSTMResources, getDResource, getDResources, deleteDResource, deleteDResources -- syncache has no parameters now (see Data.TCache.syncCache). ,syncCache -- Same than Data.TCache but without Cache parameter ,clearSyncCacheProc -- the same interface for any datatype. wrapping and unwrapping are made internally. --have the same functionalities than the Data.TCache primitives with the same name. , withResource, withResources, withSTMResources, getResource, getResources, deleteResource, deleteResources ) where import System.IO.Unsafe import Data.Typeable import qualified Data.TCache as T import Data.TCache.IDynamic as I import Debug.Trace import Control.Concurrent.STM(atomically,STM) import Control.Concurrent.STM.TVar import Control.Concurrent(forkIO) import Control.Exception(finally) import Data.TCache.IDynamic import Control.Concurrent(ThreadId) debug a b= trace b a -- | handles Dynamic objects using @Data.TCache.withResource@ -- -- @withDResource = Data.TCache..withResource @ withDResource :: IDynamic-> (Maybe IDynamic-> IDynamic)-> IO () withDResource = T.withResource -- | @withDResources = Data.TCache.withResources@ withDResources:: [IDynamic]-> ([Maybe IDynamic]-> [IDynamic])-> IO () withDResources = T.withResources -- | this is the main function for the *Resource calls. All the rest derive from it. The results are kept in the STM monad -- so it can be part of a larger STM transaction involving other TVars -- The @Resources@ register returned by the user-defined function is interpreted as such: -- -- @toAdd@: additional resources not read in the first parameter of withSTMResources are created/updated with toAdd -- -- @toDelete@: from the cache and from permanent storage -- -- @toReturn@: will be returned by withSTMResources withDSTMResources :: [IDynamic] -- ^ The list of resources to be retrieved -> ([Maybe IDynamic] -> T.Resources IDynamic x) -- ^ The function that process the resources found and return a Resources structure -> STM x -- ^ The return value in the STM monad. withDSTMResources = T.withSTMResources -- | @getDResource = Data.TCache.getResource@ getDResource :: IDynamic -> IO (Maybe IDynamic) getDResource = T.getResource -- | @getDResources = Data.TCache.getResources@ getDResources :: [IDynamic] -> IO [Maybe IDynamic] getDResources = T.getResources -- | getTVars return the TVar that wraps the resources for which the keys are given . -- | it return @Nothing@ if a TVar with this object has not been allocated -- These TVars can be used as usual in explicit user constructed atomic blocks -- Additionally, the retrieved TVars remain in the cache and can be accessed and updated by the rest -- of the TCache methods. -- to keep the consistence in the serialized data, the content of the TVars are written every time the cache is syncronized with the storage until releaseTVars is called -- See 'Data.TCache.getTVars' getTVars :: [IDynamic] -> STM [Maybe (TVar IDynamic)] getTVars= T.getTVars releaseTVars :: [IDynamic] -> STM () releaseTVars= T.releaseTVars getTVarsIO :: [IDynamic] -> IO [TVar IDynamic] getTVarsIO= T.getTVarsIO -- | retrieve a list of objects and return error if any resource is not found. instead of Nothing justGetDResources rs=do mrs <- getDResources rs return $ map process $ zip mrs rs where process (Nothing, r) = error ("\""++T.keyResource r ++ "\" does not exist") process (Just r', _) = r' justGetDResource r= do [r']<- justGetDResources [r] return r' -- | delete a resource from the cache and the storage deleteDResource :: IDynamic -> IO () deleteDResource= T.deleteResource -- | delete a list of resources from the cache and the storage deleteDResources :: [IDynamic] -> IO () deleteDResources= T.deleteResources -- syncronize the cache with the permanent storage syncCache :: IO () syncCache= T.syncCache (T.refcache :: T.Cache IDynamic) -- | Start the thread that clean and writes on the persistent storage. -- Otherwise, syncCache must be invoked explicitly or no persistence will exist clearSyncCacheProc :: Int -- ^ number of seconds betwen checks. objects not written to disk are written -> (Integer -> Integer-> Integer-> Bool) -- ^ The user-defined check-for-cleanup-from-cache for each object. 'defaultCheck' is an example -> Int -- ^ The max number of objects in the cache, if more, the cleanup starts -> IO ThreadId -- ^ Identifier of the thread created clearSyncCacheProc= T.clearSyncCacheProc (T.refcache :: T.Cache IDynamic) {- | methods that handle a single datatype. -} -- | similar to @Data.TCache.withResource@. -- The fact that this method may return a type different that the source type permits to use ' Key' objects withResource ::(Typeable a, Typeable b, T.IResource a, T.IResource b) => a-> (Maybe a-> b)-> IO () withResource r f= withResources [r] (\[mr]-> [f mr]) -- | similar to @Data.TCache.withResources@. -- The fact that this method may return a type different that the source type permits to use ' Key' objects withResources::(Typeable a, Typeable b, T.IResource a, T.IResource b) => [a]-> ([Maybe a]-> [b])-> IO () withResources rs f= withDResources (map toIDyn rs) (\mrs-> f' mrs) where f' = map toIDyn . f . map g g Nothing= Nothing g (Just x)= Just (fromIDyn x) -- | similar to @Data.TCache.withSTMResources@. -- The return in the STM monad permits to participate in larger STM transactions -- The fact that this method may return a type different that the source type permits to use ' Key' objects withSTMResources :: forall x.forall a.forall b.(Typeable a, Typeable b, T.IResource a, T.IResource b) => [a] -- ^ the list of resources to be retrieved -> ([Maybe a]-> T.Resources b x) -- ^ The function that process the resources found and return a Resources structure -> STM x -- ^ The return value in the STM monad. withSTMResources rs f= withDSTMResources (map toIDyn rs) f' where f' :: [Maybe IDynamic]-> T.Resources IDynamic x f' = h . f . map g g (Just x)= Just $ fromIDyn x g Nothing = Nothing h T.Retry = T.Retry h (T.Resources a d r)= T.Resources (map toIDyn a) (map toIDyn d) r -- | similar to Data.@TCache.getResource@. -- The fact that this method may return a type different that the source type permits to use ' Key' objects getResource ::(Typeable a, Typeable b, T.IResource a, T.IResource b) => a -> IO (Maybe b) getResource x= getDResource (toIDyn x) >>= return . g where g Nothing= Nothing g (Just x)= Just (fromIDyn x) -- | similar to @Data.TCache.getResources@. -- The fact that this method may return a type different that the source type permits to use ' Key' objects getResources ::(Typeable a, Typeable b, T.IResource a, T.IResource b) => [a] -> IO [Maybe b] getResources rs = getDResources (map toIDyn rs) >>= return . map g where g Nothing= Nothing g (Just x)= Just (fromIDyn x) -- | similar to @Data.TCache.deleteResource@ deleteResource ::(Typeable a, T.IResource a) => a -> IO () deleteResource x= deleteDResource (toIDyn x) -- | similar to @Data.TCache.deleteResource@ deleteResources ::(Typeable a, T.IResource a) => [a] -> IO () deleteResources xs= deleteDResources (map toIDyn xs)