#if __GLASGOW_HASKELL__ >= 704
#endif
module Control.Monad.Trans.Resource
(
ResourceT
, ResIO
, ReleaseKey
, runResourceT
, resourceForkIO
, transResourceT
, joinResourceT
, ExceptionT (..)
, runExceptionT_
, runException
, runException_
, allocate
, register
, release
, unprotect
, resourceMask
, MonadResource (..)
, MonadUnsafeIO (..)
, MonadThrow (..)
, MonadActive (..)
, MonadResourceBase
, InvalidAccess (..)
, MonadBaseControl
, InternalState
, getInternalState
, runInternalState
, withInternalState
, createInternalState
, closeInternalState
, Resource
, mkResource
, with
, allocateResource
) where
import qualified Data.IntMap as IntMap
import Control.Exception (SomeException, throw)
import Control.Monad.Trans.Control
( MonadBaseControl (..), liftBaseDiscard, control )
import qualified Data.IORef as I
import Control.Monad.Base (MonadBase, liftBase)
import Control.Applicative (Applicative (..))
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad (liftM)
import qualified Control.Exception as E
import Data.Monoid (Monoid)
import qualified Control.Exception.Lifted as L
import Control.Monad.Trans.Identity ( IdentityT)
import Control.Monad.Trans.List ( ListT )
import Control.Monad.Trans.Maybe ( MaybeT )
import Control.Monad.Trans.Error ( ErrorT, Error)
import Control.Monad.Trans.Reader ( ReaderT )
import Control.Monad.Trans.State ( StateT )
import Control.Monad.Trans.Writer ( WriterT )
import Control.Monad.Trans.Resource.Internal
import Control.Monad.Trans.RWS ( RWST )
import qualified Control.Monad.Trans.RWS.Strict as Strict ( RWST )
import qualified Control.Monad.Trans.State.Strict as Strict ( StateT )
import qualified Control.Monad.Trans.Writer.Strict as Strict ( WriterT )
import Control.Concurrent (ThreadId, forkIO)
import Control.Monad.ST (ST)
import qualified Control.Monad.ST.Lazy as Lazy
import Data.Functor.Identity (Identity, runIdentity)
import Control.Monad.Morph
register :: MonadResource m => IO () -> m ReleaseKey
register = liftResourceT . registerRIO
release :: MonadIO m => ReleaseKey -> m ()
release (ReleaseKey istate rk) = liftIO $ release' istate rk
(maybe (return ()) id)
unprotect :: MonadIO m => ReleaseKey -> m (Maybe (IO ()))
unprotect (ReleaseKey istate rk) = liftIO $ release' istate rk return
allocate :: MonadResource m
=> IO a
-> (a -> IO ())
-> m (ReleaseKey, a)
allocate a = liftResourceT . allocateRIO a
allocateResource :: MonadResource m => Resource a -> m (ReleaseKey, a)
allocateResource = liftResourceT . allocateResourceRIO
resourceMask :: MonadResource m => ((forall a. ResourceT IO a -> ResourceT IO a) -> ResourceT IO b) -> m b
resourceMask = liftResourceT . resourceMaskRIO
allocateRIO :: IO a -> (a -> IO ()) -> ResourceT IO (ReleaseKey, a)
allocateRIO acquire rel = ResourceT $ \istate -> liftIO $ E.mask $ \restore -> do
a <- restore acquire
key <- register' istate $ rel a
return (key, a)
allocateResourceRIO :: Resource a -> ResourceT IO (ReleaseKey, a)
allocateResourceRIO (Resource f) = ResourceT $ \istate -> liftIO $ E.mask $ \restore -> do
Allocated a free <- f restore
key <- register' istate free
return (key, a)
registerRIO :: IO () -> ResourceT IO ReleaseKey
registerRIO rel = ResourceT $ \istate -> liftIO $ register' istate rel
resourceMaskRIO :: ((forall a. ResourceT IO a -> ResourceT IO a) -> ResourceT IO b) -> ResourceT IO b
resourceMaskRIO f = ResourceT $ \istate -> liftIO $ E.mask $ \restore ->
let ResourceT f' = f (go restore)
in f' istate
where
go :: (forall a. IO a -> IO a) -> (forall a. ResourceT IO a -> ResourceT IO a)
go r (ResourceT g) = ResourceT (\i -> r (g i))
register' :: I.IORef ReleaseMap
-> IO ()
-> IO ReleaseKey
register' istate rel = I.atomicModifyIORef istate $ \rm ->
case rm of
ReleaseMap key rf m ->
( ReleaseMap (key 1) rf (IntMap.insert key rel m)
, ReleaseKey istate key
)
ReleaseMapClosed -> throw $ InvalidAccess "register'"
release' :: I.IORef ReleaseMap
-> Int
-> (Maybe (IO ()) -> IO a)
-> IO a
release' istate key act = E.mask $ \restore -> do
maction <- I.atomicModifyIORef istate lookupAction
restore (act maction)
where
lookupAction rm@(ReleaseMap next rf m) =
case IntMap.lookup key m of
Nothing -> (rm, Nothing)
Just action ->
( ReleaseMap next rf $ IntMap.delete key m
, Just action
)
lookupAction ReleaseMapClosed = (ReleaseMapClosed, Nothing)
runResourceT :: MonadBaseControl IO m => ResourceT m a -> m a
runResourceT (ResourceT r) = do
istate <- createInternalState
r istate `finally` stateCleanup istate
bracket_ :: MonadBaseControl IO m => IO () -> IO () -> m a -> m a
bracket_ alloc cleanup inside =
control $ \run -> E.bracket_ alloc cleanup (run inside)
finally :: MonadBaseControl IO m => m a -> IO () -> m a
finally action cleanup =
control $ \run -> E.finally (run action) cleanup
joinResourceT :: ResourceT (ResourceT m) a
-> ResourceT m a
joinResourceT (ResourceT f) = ResourceT $ \r -> unResourceT (f r) r
runExceptionT_ :: Monad m => ExceptionT m a -> m a
runExceptionT_ = liftM (either E.throw id) . runExceptionT
runException :: ExceptionT Identity a -> Either SomeException a
runException = runIdentity . runExceptionT
runException_ :: ExceptionT Identity a -> a
runException_ = runIdentity . runExceptionT_
resourceForkIO :: MonadBaseControl IO m => ResourceT m () -> ResourceT m ThreadId
resourceForkIO (ResourceT f) = ResourceT $ \r -> L.mask $ \restore ->
bracket_
(stateAlloc r)
(return ())
(liftBaseDiscard forkIO $ bracket_
(return ())
(stateCleanup r)
(restore $ f r))
class Monad m => MonadActive m where
monadActive :: m Bool
instance (MonadIO m, MonadActive m) => MonadActive (ResourceT m) where
monadActive = ResourceT $ \rmMap -> do
rm <- liftIO $ I.readIORef rmMap
case rm of
ReleaseMapClosed -> return False
_ -> monadActive
instance MonadActive Identity where
monadActive = return True
instance MonadActive IO where
monadActive = return True
instance MonadActive (ST s) where
monadActive = return True
instance MonadActive (Lazy.ST s) where
monadActive = return True
#define GO(T) instance MonadActive m => MonadActive (T m) where monadActive = lift monadActive
#define GOX(X, T) instance (X, MonadActive m) => MonadActive (T m) where monadActive = lift monadActive
GO(IdentityT)
GO(ListT)
GO(MaybeT)
GOX(Error e, ErrorT e)
GO(ReaderT r)
GO(StateT s)
GOX(Monoid w, WriterT w)
GOX(Monoid w, RWST r w s)
GOX(Monoid w, Strict.RWST r w s)
GO(Strict.StateT s)
GOX(Monoid w, Strict.WriterT w)
#undef GO
#undef GOX
#if __GLASGOW_HASKELL__ >= 704
type MonadResourceBase m = (MonadBaseControl IO m, MonadThrow m, MonadUnsafeIO m, MonadIO m, Applicative m)
#else
class (MonadBaseControl IO m, MonadThrow m, MonadUnsafeIO m, MonadIO m, Applicative m) => MonadResourceBase m
instance (MonadBaseControl IO m, MonadThrow m, MonadUnsafeIO m, MonadIO m, Applicative m) => MonadResourceBase m
#endif
createInternalState :: MonadBase IO m => m InternalState
createInternalState = liftBase
$ I.newIORef
$ ReleaseMap maxBound (minBound + 1) IntMap.empty
closeInternalState :: MonadBase IO m => InternalState -> m ()
closeInternalState = liftBase . stateCleanup
getInternalState :: Monad m => ResourceT m InternalState
getInternalState = ResourceT return
type InternalState = I.IORef ReleaseMap
runInternalState :: ResourceT m a -> InternalState -> m a
runInternalState = unResourceT
withInternalState :: (InternalState -> m a) -> ResourceT m a
withInternalState = ResourceT