{-# LANGUAGE TypeFamilies #-} module System.IO.RandomAccessFile.Common where import Control.Monad import Control.Concurrent import Control.Concurrent.STM import qualified Control.Concurrent.ReadWriteLock as RWL import Control.Exception import qualified Data.Map as M import qualified Data.ByteString as B import Data.List import Data.Word type Offset = Word64 type Size = Word64 class FileAccess a where data AccessParams a initFile :: AccessParams a -> FilePath -> IO a readBytes :: a -> Offset -> Size -> IO B.ByteString writeBytes :: a -> Offset -> B.ByteString -> IO () currentFileSize :: a -> IO Size syncFile :: a -> IO () syncFile _ = return () closeFile :: a -> IO () writeZeros :: FileAccess a => a -> Size -> IO () writeZeros h size = writeBytes h 0 $ B.replicate (fromIntegral size) 0 data AccessType = ReadAccess | WriteAccess type FileLocks = M.Map Offset RWL.RWLock withQSem :: QSem -> IO a -> IO a withQSem sem = bracket_ (waitQSem sem) (signalQSem sem) withLock :: RWL.RWLock -> AccessType -> IO a -> IO a withLock lock ReadAccess action = bracket_ (RWL.acquireRead lock) (RWL.releaseRead lock) action withLock lock WriteAccess action = bracket_ (RWL.acquireWrite lock) (RWL.releaseWrite lock) action withLocks :: [RWL.RWLock] -> AccessType -> IO a -> IO a withLocks locks ReadAccess action = bracket_ (forM_ locks RWL.acquireRead) (forM_ locks RWL.releaseRead) action withLocks locks WriteAccess action = bracket_ (forM_ locks RWL.acquireWrite) (forM_ locks RWL.releaseWrite) action withLock_ :: Bool -> RWL.RWLock -> AccessType -> IO a -> IO a withLock_ False _ _ action = action withLock_ True lock access action = withLock lock access action underBlockLock :: TVar FileLocks -> AccessType -> Offset -> IO a -> IO a underBlockLock locksVar access n action = do newLock <- RWL.new lock <- atomically $ do locks <- readTVar locksVar case M.lookup n locks of Just lock -> return lock Nothing -> do writeTVar locksVar $ M.insert n newLock locks return newLock withLock lock access action underBlockLocks :: TVar FileLocks -> AccessType -> [Offset] -> IO a -> IO a underBlockLocks locksVar access ns action = do newLocks <- replicateM (length ns) RWL.new locks <- atomically $ do locks <- readTVar locksVar forM (zip [0..] $ sort ns) $ \(idx,n) -> case M.lookup n locks of Just lock -> return lock Nothing -> do let newLock = newLocks !! idx writeTVar locksVar $ M.insert n newLock locks return newLock withLocks locks access action