packages feed

resource-simple (empty) → 0.1

raw patch · 5 files changed

+341/−0 lines, 5 filesdep +basedep +containersdep +monad-controlsetup-changed

Dependencies added: base, containers, monad-control, monad-fork, mtl, transformers, transformers-base

Files

+ CONTRIBUTORS view
@@ -0,0 +1,2 @@+Michael Snoyman <michael@snoyman.com>+Robin Banks <anarchomorphism@seomraspraoi.org>
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2011, Michael Snoyman++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Michael Snoyman nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ resource-simple.cabal view
@@ -0,0 +1,48 @@+name:                resource-simple+version:             0.1+synopsis:            Allocate resources which are guaranteed to be released.+license:             BSD3+license-file:        LICENSE+author:              Robin Banks+maintainer:          anarchomorphism@seomraspraoi.org+stability:           Experimental+category:            Control+cabal-version:       >= 1.2+build-type:          Simple+description:+  This is a simplified, standalone version of the @ResourceT@ transformer that+  was originally developed as part of the @conduit@ package. That version of+  @ResourceT@ was supported by a complicated hierarchy of type classes, the+  main purpose of which was to enable the usage of @ResourceT@ on top of the+  @ST@ monad. However, this doesn't really make much sense conceptually, and+  the reason it was done is because conduits are very closely tied to+  @ResourceT@, and an instance for @ST@ would enable the usage of @ResourceT@+  in pure code.+  .+  This package completely does away with the supporting type class hierarchy,+  and as such, this version of @ResourceT@ can only be used with @IO@ or+  @IO@-like monads.+  .+  This package is motivated by a belief that the iteratee problem and the+  resource finalization problem are orthogonal. This package is ideal for+  usage with the @pipes@ library.++extra-source-files:+  CONTRIBUTORS,+  LICENSE++Library+  hs-source-dirs:+    src++  exposed-modules:+    Control.Monad.Resource++  build-depends:+    base > 4 && < 5,+    containers < 1,+    monad-control > 0.3 && < 0.4,+    monad-fork < 0.2,+    mtl > 2 && < 3,+    transformers > 0.2 && < 0.3,+    transformers-base < 0.5
+ src/Control/Monad/Resource.hs view
@@ -0,0 +1,259 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}++{-|++Allocate resources which are guaranteed to be released.++One point to note: all register cleanup actions live in IO, not the main+monad. This allows both more efficient code, and for monads to be transformed.++-}++module Control.Monad.Resource+    (  -- * Data types+      ResourceT+    , ReleaseKey+      -- * Run+    , runResourceT+      -- * Resource allocation+    , with+    , register+    , release+      -- * Monad transformation+    , transResourceT+    )+where++import           Data.IntMap (IntMap)+import qualified Data.IntMap as IntMap+import           Data.IORef+                     ( IORef+                     , newIORef+                     , readIORef+                     , writeIORef+                     , atomicModifyIORef+                     )+import           Data.Word (Word)+import           Control.Applicative (Applicative (..))+import           Control.Exception (SomeException, mask, mask_, try, finally)+import           Control.Monad (liftM, when)+import           Control.Monad.Base (MonadBase (..))+import           Control.Monad.Cont.Class (MonadCont(..))+import           Control.Monad.Error.Class (MonadError (..))+import           Control.Monad.Fork.Class (MonadFork (..))+import           Control.Monad.IO.Class (MonadIO (..))+import           Control.Monad.Reader.Class (MonadReader (..))+import           Control.Monad.RWS.Class (MonadRWS (..))+import           Control.Monad.State.Class (MonadState (..))+import           Control.Monad.Writer.Class (MonadWriter (..))+import           Control.Monad.Trans.Class (MonadTrans (..))+import           Control.Monad.Trans.Control+                     ( MonadBaseControl (..)+                     , MonadTransControl (..)+                     , control+                     )+++------------------------------------------------------------------------------+-- | A lookup key for a specific release action. This value is returned by+-- 'register' and 'with' and is passed to 'release'.+newtype ReleaseKey = ReleaseKey Int+++------------------------------------------------------------------------------+data ReleaseMap = ReleaseMap !Int !Word !(IntMap (IO ()))+++------------------------------------------------------------------------------+-- | The Resource transformer. This transformer keeps track of all registered+-- actions, and calls them upon exit (via 'runResourceT'). Actions may be+-- registered via 'register', or resources may be allocated atomically via+-- 'with'. The with function corresponds closely to @bracket@.+--+-- Releasing may be performed before exit via the 'release' function. This is+-- a highly recommended optimization, as it will ensure that scarce resources+-- are freed early. Note that calling @release@ will deregister the action, so+-- that a release action will only ever be called once.+newtype ResourceT m a = ResourceT (IORef ReleaseMap -> m a)+++------------------------------------------------------------------------------+instance MonadTrans ResourceT where+    lift = ResourceT . const+++------------------------------------------------------------------------------+instance MonadTransControl ResourceT where+    newtype StT ResourceT a = StReader {unStReader :: a}+    liftWith f = ResourceT $ \r -> f $ \(ResourceT t) -> liftM StReader $ t r+    restoreT = ResourceT . const . liftM unStReader+++------------------------------------------------------------------------------+instance Functor m => Functor (ResourceT m) where+    fmap f (ResourceT m) = ResourceT $ \r -> fmap f (m r)+++------------------------------------------------------------------------------+instance Applicative m => Applicative (ResourceT m) where+    pure = ResourceT . const . pure+    ResourceT mf <*> ResourceT ma = ResourceT $ \r -> mf r <*> ma r+++------------------------------------------------------------------------------+instance Monad m => Monad (ResourceT m) where+    return = ResourceT . const . return+    ResourceT m >>= f = ResourceT $ \r -> m r >>= \a ->+        let ResourceT m' = f a in m' r+++------------------------------------------------------------------------------+instance MonadIO m => MonadIO (ResourceT m) where+    liftIO = lift . liftIO+++------------------------------------------------------------------------------+instance MonadBase b m => MonadBase b (ResourceT m) where+    liftBase = lift . liftBase+++------------------------------------------------------------------------------+instance MonadBaseControl b m => MonadBaseControl b (ResourceT m) where+     newtype StM (ResourceT m) a = StMT (StM m a)+     liftBaseWith f = ResourceT $ \reader ->+         liftBaseWith $ \runInBase ->+             f $ liftM StMT . runInBase . (\(ResourceT r) -> r reader)+     restoreM (StMT base) = ResourceT $ const $ restoreM base+++------------------------------------------------------------------------------+instance (MonadFork m, MonadBaseControl IO m) => MonadFork (ResourceT m) where+    fork (ResourceT f) = ResourceT $ \istate ->+        control $ \run -> mask $ \unmask -> do+            stateAlloc istate+            run . fork $ control $ \run' -> do+                unmask (run' $ f istate) `finally` stateCleanup istate+++------------------------------------------------------------------------------+instance MonadCont m => MonadCont (ResourceT m) where+    callCC = liftCallCC callCC+      where+        liftCallCC ccc f = ResourceT $ \r -> ccc $ \ c ->+            let ResourceT m = f (ResourceT . const . c) in m r+    ++------------------------------------------------------------------------------+instance MonadError e m => MonadError e (ResourceT m) where+    throwError = lift . throwError+    catchError = liftCatch catchError+      where+        liftCatch f (ResourceT m) h = ResourceT $ \r ->+            f (m r) (\e -> let ResourceT m' = h e in m' r)+++------------------------------------------------------------------------------+instance MonadReader r m => MonadReader r (ResourceT m) where+    ask = lift ask+    local f (ResourceT m) = ResourceT $ local f . m+++------------------------------------------------------------------------------+instance MonadRWS r w s m => MonadRWS r w s (ResourceT m)+++------------------------------------------------------------------------------+instance MonadState s m => MonadState s (ResourceT m) where+    get = lift get+    put s = lift $ put s+++------------------------------------------------------------------------------+instance MonadWriter w m => MonadWriter w (ResourceT m) where+    tell w = lift $ tell w+    listen = transResourceT listen+    pass = transResourceT pass+++------------------------------------------------------------------------------+-- | Perform some allocation, and automatically register a cleanup action.+with :: MonadBase IO m+     => IO a -- ^ allocate+     -> (a -> IO ()) -- ^ free resource+     -> ResourceT m (ReleaseKey, a)+with acquire m = ResourceT $ \istate -> liftBase $ mask $ \unmask -> do+    a <- unmask acquire+    key <- register' istate $ m a+    return (key, a)+++------------------------------------------------------------------------------+-- | Register some action that will be called precisely once, either when+-- 'runResourceT' is called, or when the 'ReleaseKey' is passed to 'release'.+register :: MonadBase IO m => IO () -> ResourceT m ReleaseKey+register m = ResourceT $ \istate -> liftBase $ register' istate m++register' :: IORef ReleaseMap -> IO () -> IO ReleaseKey+register' istate m = atomicModifyIORef istate $ \(ReleaseMap key ref im) ->+    (ReleaseMap (key + 1) ref (IntMap.insert key m im), ReleaseKey key)+++------------------------------------------------------------------------------+-- | Call a release action early, and deregister it from the list of cleanup+-- actions to be performed.+release :: MonadBase IO m => ReleaseKey -> ResourceT m ()+release key = ResourceT $ \istate -> liftBase $ release' istate key++release' istate (ReleaseKey key) = mask $ \unmask -> do+    atomicModifyIORef istate lookupAction >>= maybe (return ()) unmask+  where+    lookupAction rm@(ReleaseMap key' ref im) =+        case IntMap.lookup key im of+            Nothing -> (rm, Nothing)+            Just m -> (ReleaseMap key' ref $ IntMap.delete key im, Just m)+++------------------------------------------------------------------------------+-- | Transform the monad a @ResourceT@ lives in. This is most often used to+-- strip or add new transformers to a stack, e.g. to run a @ReaderT@.+transResourceT :: (m a -> n b) -> ResourceT m a -> ResourceT n b+transResourceT f (ResourceT mx) = ResourceT (\r -> f (mx r))+++------------------------------------------------------------------------------+-- | Unwrap a 'ResourceT' transformer, and call all registered release+-- actions.+--+-- Note that there is some reference counting involved due to the 'MonadFork'+-- instance. If multiple threads are sharing the same collection of resources,+-- only the last call to @runResourceT@ will deallocate the resources.+runResourceT :: MonadBaseControl IO m => ResourceT m a -> m a+runResourceT (ResourceT r) = do+    istate <- liftBase $ newIORef $ ReleaseMap 0 0 IntMap.empty+    control $ \run -> mask $ \unmask -> do+        stateAlloc istate+        unmask (run $ r istate) `finally` stateCleanup istate+++------------------------------------------------------------------------------+stateAlloc :: IORef ReleaseMap -> IO ()+stateAlloc istate = atomicModifyIORef istate $ \(ReleaseMap key ref im) ->+    (ReleaseMap key (ref + 1) im, ())+++------------------------------------------------------------------------------+stateCleanup :: IORef ReleaseMap -> IO ()+stateCleanup istate = mask_ $ do+    (ref, im) <- atomicModifyIORef istate $ \(ReleaseMap key ref im) ->+        (ReleaseMap key (ref - 1) im, (ref - 1, im))+    when (ref == 0) $ do+        mapM_ (\x -> try' x >> return ()) $ IntMap.elems im+        writeIORef istate $ error "Control.Monad.Resource.Trans.stateCleanup:\+            \ There is a bug in the implementation. The mutable state is\+            \ being accessed after cleanup. Please contact the maintainers."+  where+    try' = try :: IO a -> IO (Either SomeException a)