{-# LANGUAGE TypeSynonymInstances, MultiParamTypeClasses, RankNTypes, FlexibleContexts #-} -- | Convenience module for exporting usually useful stuff -- ALL "unsafe*" functions must be deleted and turned into "primitives" -- in this module. module Halfs.FSState (StateHandle(..) -- FIX: might get rid of FSState or StateHandle. -- writing stuff: ,FSWrite, unsafeLiftIOWrite, runFSWrite ,evalFSWriteIO, evalFSWriteIOMV ,runFSWriteIO, readToWrite, readToWriteCont, writeToBuffer ,unsafeLiftIOWriteWithRoot, unsafeWriteGet, putStrLnWriteRead ,updateInodeCacheWrite ,modifyFSWrite -- reading stuff: ,FSRead, unsafeLiftIORead, runFSRead ,runFSReadIO, evalFSReadIOMV, unsafeReadGet ,unsafeModifyFSRead ,unsafeWriteToRead -- general monad capabilities. move elsewhere? ,newReadRef, readReadRef, modifyReadRef, writeReadRef ,takeMVarRW, readMVarRW, putMVarRW, modifyMVarRW_ ,modifyMVarRW_' -- other stuff: ,module Control.Monad.Trans ,module Control.Monad.Error ,module Control.Monad.State -- read-write ,FSRW, unsafeLiftIORW -- FIX: don't export put & get: ,Control.Monad.State.put ,Control.Monad.State.get ,alreadyExistsEx ,doesNotExistEx ,eofEx ,illegalOpEx ,decLinksForInode ,forkWithMVar ,forkFSWrite ) where import Binary (Binary, BinHandle, put_) import Halfs.FSRoot (FSRoot(..), FSStatus(..) ,fsRootUpdateInodeCache) import Halfs.FSRW import Halfs.Inode(Inode(..), InodeMetadata(..)) -- import System.RawDevice(RawDevice) -- FIX: get rid of FSState data type; we only need it because it works -- on the StateT monad. We can rool it ourselves and get rid of -- FSState, probably. -- base import Control.Concurrent(MVar, ThreadId, modifyMVar_, tryPutMVar, modifyMVar, putMVar, newMVar, readMVar, forkIO, takeMVar, withMVar) import Control.Exception(assert) import Control.Monad.Error(throwError, catchError, MonadError) import Control.Monad.State(StateT(..), modify, evalStateT, execStateT, MonadState) import qualified Control.Monad.State (get, put) import Control.Monad.Trans(liftIO) import Data.IORef (IORef, newIORef, readIORef, modifyIORef, writeIORef) import System.IO.Error (alreadyExistsErrorType, doesNotExistErrorType, illegalOperationErrorType, eofErrorType, mkIOError) -- | Read-write state data StateHandle = StateHandle { {- any function needing access to the FSRoot needs to get this mvar: -} stateFineMVar :: MVar FSRoot {- clients can use this MVar to block by calling the evalFS{Read,Write}IOMV functions: -} , stateCoarseMVar :: Maybe (MVar ()) } deriving Eq type FSState a = StateT StateHandle IO a alreadyExistsEx :: FilePath -> IOError alreadyExistsEx path = mkIOError alreadyExistsErrorType "already exists" Nothing (Just path) doesNotExistEx :: FilePath -> IOError doesNotExistEx path = mkIOError doesNotExistErrorType "does not exist" Nothing (Just path) illegalOpEx :: FilePath -> IOError illegalOpEx path = mkIOError illegalOperationErrorType "illegal operation" Nothing (Just path) eofEx :: FilePath -> IOError eofEx path = mkIOError eofErrorType "EOF" Nothing (Just path) -- ------------------------------------------------------------ -- * FSState Read Only -- ------------------------------------------------------------ newtype FSRead a = FSR (FSState a) instance Monad FSRead where return a = FSR (return a) (FSR m) >>= k = FSR $ do a <- m runFSRead $ k a -- | Make this read-only monad into a read-write monad. runFSRead :: FSRead a -> FSState a runFSRead (FSR f) = f -- | Returns the FSRoot since things like Inode cache may change. runFSReadIO' :: FSRead a -> FSRoot -> IO (a, FSRoot) runFSReadIO' (FSR f) s = do mv <- newMVar s -- same mvar, so ignore it on output: (r,_) <- runStateT f (StateHandle mv Nothing) fsroot <- readMVar mv return (r, fsroot) runFSReadIO :: FSRead a -> FSRoot -> (IOError -> IO (a, FSRoot)) -- exception handler -> IO (a, FSRoot) runFSReadIO f s handler = catch (runFSReadIO' f s) handler -- | evalFSReadIOMV :: FSRead a -> StateHandle -> IO a evalFSReadIOMV (FSR f) mv@(StateHandle _ (Just blockOn)) = do (r,_) <- withMVar blockOn $ \ _ -> runStateT f mv return r evalFSReadIOMV (FSR f) mv@(StateHandle _ Nothing) = do (r,_) <- runStateT f mv return r -- | Unsafe because it uses readMVar. unsafeReadGet :: FSRead FSRoot unsafeReadGet = FSR $ do (StateHandle mv _) <- Control.Monad.State.get liftIO $ readMVar mv -- | FIX: Unsafe? unsafeModifyFSRead :: (FSRoot -> (FSRoot, a)) -> FSRead a unsafeModifyFSRead f = FSR $ do (StateHandle mv _) <- Control.Monad.State.get retVal <- liftIO $ modifyMVar mv (\fsroot -> return (f fsroot)) return retVal -- | FIX: Delete this function; it's temporary during refactoring. It -- violates safety requirements. unsafeLiftIORead :: IO a -> FSRead a unsafeLiftIORead action = FSR $ liftIO action -- | unsafe since this allows a reader to write. unsafeWriteToRead :: FSWrite a -> FSRead a unsafeWriteToRead (FSW a) = FSR a -- These are mostly for handles. The types aren't what we would like -- since we can't import FileHandle. -- This synonym is for documentation purposes: type ReadRef a = IORef a newReadRef :: a -> FSRead (ReadRef a) newReadRef = unsafeLiftIORead . newIORef readReadRef :: (ReadRef a) -> FSRead a readReadRef = unsafeLiftIORead . readIORef modifyReadRef :: (ReadRef a) -> (a -> a) -> FSRead () modifyReadRef f h = unsafeLiftIORead $ modifyIORef f h writeReadRef :: (ReadRef a) -> a -> FSRead () writeReadRef f h = unsafeLiftIORead $ writeIORef f h takeMVarRW :: (FSRW m) => MVar a -> m a takeMVarRW = unsafeLiftIORW . takeMVar putMVarRW :: (FSRW m) => MVar a -> a -> m () putMVarRW m a = unsafeLiftIORW (putMVar m a) readMVarRW :: (FSRW m) => MVar a -> m a readMVarRW = unsafeLiftIORW . readMVar modifyMVarRW_ :: (FSRW m) => MVar a -> (a -> IO a) -> m () modifyMVarRW_ m f = unsafeLiftIORW (modifyMVar_ m f) modifyMVarRW_' :: (FSRW m, MonadError IOError m) => MVar a -> (a -> m a) -> m () modifyMVarRW_' m f = do s <- takeMVarRW m catchError (f s >>= putMVarRW m) (\e -> putMVarRW m s >> unsafeLiftIORW (throwError e)) instance MonadError IOError FSRead where throwError = FSR . throwError catchError (FSR m) f = FSR $ catchError m (\e -> runFSRead $ f e) -- ------------------------------------------------------------ -- * FSState Write Only -- ------------------------------------------------------------ newtype FSWrite a = FSW (FSState a) instance Monad FSWrite where return a = FSW (return a) (FSW m) >>= k = FSW $ do a <- m runFSWrite $ k a -- | Make this write-only monad into a read-write monad. runFSWrite :: FSWrite a -> FSState a runFSWrite (FSW f) = f -- Forks a new thread which uses the mvar from this state. Whew. Sorta doesn't make sense to be in FSWrite, maybe should just be in IO; we have two versions of the state available here, and that's no good... one is really just for syncronizeation. forkWithMVar :: (StateHandle -> FSWrite ()) -> FSWrite ThreadId forkWithMVar f = do mv <- FSW Control.Monad.State.get forkFSWrite (f mv) forkFSWrite :: FSWrite () -> FSWrite ThreadId forkFSWrite (FSW f) = FSW (do mv <- Control.Monad.State.get -- safe to ignore output since its held in the mvar liftIO $ forkIO $ (runStateT f mv >> return ())) runFSWriteIO :: FSWrite a -> FSRoot -> IO (a, FSRoot) runFSWriteIO (FSW f) s = do mv <- newMVar s -- same mvar, so ignore it on output: (r,_) <- runStateT f (StateHandle mv Nothing) fsroot <- readMVar mv return (r, fsroot) evalFSWriteIOMV :: FSWrite a -> StateHandle -> IO a evalFSWriteIOMV (FSW f) mv@(StateHandle _ (Just blockOn)) = do (r,_) <- withMVar blockOn $ \ _ -> runStateT f mv return r evalFSWriteIOMV (FSW f) mv@(StateHandle _ Nothing) = do (r,_) <- runStateT f mv return r -- | evalFSWriteIO :: FSWrite a -> FSRoot -> IO a evalFSWriteIO (FSW f) s = do mv <- newMVar s evalStateT f (StateHandle mv Nothing) putStrLnWriteRead :: String -> FSWrite () putStrLnWriteRead = unsafeLiftIOWrite . putStrLn -- | FIX: Delete this function; it's temporary during refactoring. It -- violates safety requirements. unsafeLiftIOWrite :: IO a -> FSWrite a unsafeLiftIOWrite action = FSW $ liftIO action unsafeLiftIOWriteWithRoot :: (FSRoot -> IO a) -> FSWrite a unsafeLiftIOWriteWithRoot action = FSW $ do (StateHandle mv _) <- Control.Monad.State.get fsroot <- liftIO $ readMVar mv liftIO (action fsroot) {- UNUSED: -- | Blocks on the MVar while performing this action. unsafeLiftIOWriteModifyRoot :: (FSRoot -> IO FSRoot) -> FSWrite () unsafeLiftIOWriteModifyRoot action = FSW $ do (StateHandle mv v) <- Control.Monad.State.get _newRoot <- liftIO $ withMVar mv action -- unnecessary: Control.Monad.State.put (StateHandle mv v) -} writeToBuffer :: (Binary a) => BinHandle -> a -> FSWrite () writeToBuffer bh a = FSW $ liftIO (Binary.put_ bh a) instance MonadError IOError FSWrite where throwError = FSW . throwError catchError (FSW m) f = FSW $ catchError m (\e -> runFSWrite $ f e) instance MonadState FSRoot FSWrite where get = FSW (do (StateHandle mv _) <- Control.Monad.State.get -- liftIO $ readMVar mv) -- liftIO $ putStrLn "monadState get!" fsroot <- liftIO $ takeMVar mv return $ assert (fsStatus fsroot /= FsUnmounted) fsroot {- maybeRoot <- liftIO $ tryTakeMVar mv case maybeRoot of -- FIX: remove below when using multi-threads: Nothing -> liftIO $ putStrLn "DEADLOCK GET" >> error "deadlock get" Just a -> return a -} ) put s = FSW (do (StateHandle mv _) <- Control.Monad.State.get maybeFoo <- liftIO $ tryPutMVar mv s case maybeFoo of True -> return () False -> liftIO $ putStrLn "DEADLOCK PUT" >> error "deadlock put" ) -- | Unsafe since it uses readMVar. For get-only situations, for -- reading and not writing; no mvar block. unsafeWriteGet :: FSWrite FSRoot unsafeWriteGet = FSW $ do (StateHandle mv _) <- Control.Monad.State.get liftIO $ readMVar mv -- | FIX: This 'f' should probably be in the FSRW monad, since that doesn't have state. modifyFSWrite :: (FSRoot -> FSWrite (FSRoot, a)) -> FSWrite a modifyFSWrite f = do fsroot <- Control.Monad.State.get (newRoot, retVal) <- f fsroot Control.Monad.State.put newRoot return retVal -- | Safe since writers can be readers. readToWrite :: FSRead a -> FSWrite a readToWrite (FSR a) = FSW a -- | Safe since writers can be readers. readToWriteCont :: ((forall s . m s -> FSRead a) -> FSRead a) -> (forall s . m s -> FSWrite a) -> FSWrite a readToWriteCont f cont = case f (w2r cont) of FSR a -> FSW a where w2r :: (forall s . m s -> FSWrite a) -> (forall s . m s -> FSRead a) w2r g s = case g s of FSW a -> FSR a updateInodeCacheWrite :: Inode -> FSWrite () updateInodeCacheWrite inode = modify (\f -> fsRootUpdateInodeCache f inode) decLinksForInode :: Inode -> FSWrite () decLinksForInode inode@Inode{metaData=md@InodeMetadata{hard_links=hl}} = updateInodeCacheWrite inode{metaData=md{hard_links=hl-1}} -- ------------------------------------------------------------ -- * FSReadWrite -- ------------------------------------------------------------ -- FSRW functions don't carry state unless it's passed explicitly as -- input and output. instance FSRW FSRead where unsafeLiftIORW = unsafeLiftIORead instance FSRW FSWrite where unsafeLiftIORW = unsafeLiftIOWrite