public
Created

Simple in-process key/value cache for Haskell

  • Download Gist
Cache.hs
Haskell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
-- | Simple in-process key/value cache
--
-- Of course, for really simple stuff you could probably use unsafeInterleaveIO
module Cache (Cache, newCache, fromCache) where
 
import Control.Monad (void)
 
-- Could also use STM instead
import Control.Concurrent (forkIO, Chan, newChan, readChan, writeChan, MVar, newEmptyMVar, putMVar, takeMVar)
 
import qualified Data.Map as Map
 
data CacheMsg k v = CacheMsg k (IO v) (MVar v)
 
-- | A single cache
newtype Cache k v = Cache (Chan (CacheMsg k v))
 
cacheThread :: (Ord k) => Chan (CacheMsg k v) -> IO ()
cacheThread chan = next Map.empty
where
next m = readChan chan >>= go m
 
go m (CacheMsg k io reply) = case Map.lookup k m of
Just v -> putMVar reply v >> next m
Nothing -> do
v <- io
putMVar reply v
next (Map.insert k v m)
 
-- | Create a new cache
newCache :: (Ord k) => IO (Cache k v)
newCache = do
chan <- newChan
-- This cache thread never terminates, so this is for a process-life cache
-- That would be easy to change, but I won't bother here
void $ forkIO (cacheThread chan)
return (Cache chan)
 
syncCall :: Chan a -> (MVar r -> a) -> IO r
syncCall chan msg = do
r <- newEmptyMVar
writeChan chan (msg r)
takeMVar r
 
-- | Get a value from the cache, or compute it and cache it
fromCache :: Cache k v -> k -> IO v -> IO v
fromCache (Cache chan) k io = syncCall chan (CacheMsg k io)

So, if I understand this correctly, this cache is supposed to be thread-safe and it locks the cache upon every operation? That means that even read-read operations can not be performed simultaneously, doesn't it?

Also I think it would make more sense to use strict MVars and strict Map, but I am not sure. Am I wrong on this one?

Yes, this design means only one operation can happen at a time, and that includes reads.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.