module Control.Concurrent.TFile(
TFile,
newEmptyIO,
newIO,
new,
read,
write,
delete,
isEmpty
)
where
import Control.Concurrent.TBox.Class
import Control.Concurrent.TBox.Operations
import Data.Binary
import Data.Typeable
import Control.Concurrent.AdvSTM.TMVar
import Control.Concurrent.AdvSTM
import Control.Concurrent.AdvSTM.TVar
import Control.Monad
import Control.Exception
import System.Directory
import System.IO.Error hiding(catch)
import System.IO.Cautious(writeFileL)
import Prelude hiding(lookup,catch,null,read,readIO,writeFile)
data TFile a = TF
{ filepath :: FilePath
, tfileTVar :: TVar (Maybe a)
, dirtyTVar :: TVar Bool
, fileLock :: TMVar ()
}
deriving(Typeable)
newEmptyIO :: Binary a => FilePath -> IO (TFile a)
newEmptyIO fp =
TF fp `liftM` newTVarIO Nothing
`ap` (newTVarIO =<< doesFileExist fp)
`ap` newTMVarIO ()
newIO :: Binary a => FilePath -> a -> IO (TFile a)
newIO fp = atomically . new fp
new :: Binary a => FilePath -> a -> AdvSTM (TFile a)
new fp a = do
tbox <- TF fp `liftM` newTVar (Just a)
`ap` newTVar False
`ap` newTMVar ()
write tbox a
return tbox
instance Binary a => TBox TFile a where
writeSTM tbox = writeTVar (tfileTVar tbox) . Just
writeIO (TF fp _ _ lock) = withTMVar lock . const . writeFileL fp . encode
readSTM = readTVar . tfileTVar
readIO (TF fp _ _ lock) = (do
a <- withTMVar lock $ const $ decodeFile fp
return $ Just a)
`catch` \(e::IOException) ->
if isDoesNotExistError e then return Nothing else throw e
deleteSTM tbox = writeTVar (tfileTVar tbox) Nothing
deleteIO (TF fp _ _ lock) = withTMVar lock $ const $ removeFile fp
`catch` \(e::IOException) -> unless (isDoesNotExistError e) $ throw e
isDirty = readTVar . dirtyTVar
setDirty = writeTVar . dirtyTVar
withTMVar :: TMVar a -> (a -> IO b) -> IO b
withTMVar tmvar ioac = do
a <- atomically $ takeTMVar tmvar
res <- ioac a
atomically $ putTMVar tmvar a
return res