{-| Module : Data.LruCache.IO Copyright : (c) Moritz Kiefer, 2016 (c) Jasper Van der Jeugt, 2015 License : BSD3 Maintainer : moritz.kiefer@purelyfunctional.org Convenience module for the common case of caching results of IO actions. See 'Data.LruCache.IO.Finalizer' if you want to run finalizers automatically when cache entries are evicted -} module Data.LruCache.IO ( LruHandle(..) , cached , newLruHandle , StripedLruHandle(..) , stripedCached , newStripedLruHandle ) where import Control.Applicative ((<$>)) import Data.Hashable (Hashable, hash) import Data.IORef (IORef, atomicModifyIORef', newIORef) import Data.Vector (Vector) import qualified Data.Vector as Vector import Prelude hiding (lookup) import Data.LruCache -- | Store a LRU cache in an 'IORef to be able to conveniently update it. newtype LruHandle k v = LruHandle (IORef (LruCache k v)) -- | Create a new LRU cache of the given size. newLruHandle :: Int -> IO (LruHandle k v) newLruHandle capacity = LruHandle <$> newIORef (empty capacity) -- | Return the cached result of the action or, in the case of a cache -- miss, execute the action and insert it in the cache. cached :: (Hashable k, Ord k) => LruHandle k v -> k -> IO v -> IO v cached (LruHandle ref) k io = do lookupRes <- atomicModifyIORef' ref $ \c -> case lookup k c of Nothing -> (c, Nothing) Just (v, c') -> (c', Just v) case lookupRes of Just v -> return v Nothing -> do v <- io atomicModifyIORef' ref $ \c -> (insert k v c, ()) return v -- | Using a stripe of multiple handles can improve the performance in -- the case of concurrent accesses since several handles can be -- accessed in parallel. newtype StripedLruHandle k v = StripedLruHandle (Vector (LruHandle k v)) -- | Create a new 'StripedHandle' with the given number of stripes and -- the given capacity for each stripe. newStripedLruHandle :: Int -> Int -> IO (StripedLruHandle k v) newStripedLruHandle numStripes capacityPerStripe = StripedLruHandle <$> Vector.replicateM numStripes (newLruHandle capacityPerStripe) -- | Striped version of 'cached'. stripedCached :: (Hashable k, Ord k) => StripedLruHandle k v -> k -> IO v -> IO v stripedCached (StripedLruHandle v) k = cached (v Vector.! idx) k where idx = hash k `mod` Vector.length v