1{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE MultiParamTypeClasses #-}
4{-# LANGUAGE UndecidableInstances #-}
5{-# LANGUAGE TypeFamilies #-}
6{-# LANGUAGE RankNTypes #-}
7{-# LANGUAGE CPP #-}
8{-# LANGUAGE DeriveDataTypeable #-}
9{-# LANGUAGE ConstraintKinds #-}
10-- | Allocate resources which are guaranteed to be released.
11--
12-- For more information, see <https://github.com/snoyberg/conduit/tree/master/resourcet#readme>.
13--
14-- One point to note: all register cleanup actions live in the @IO@ monad, not
15-- the main monad. This allows both more efficient code, and for monads to be
16-- transformed.
17module Control.Monad.Trans.Resource
18    ( -- * Data types
19      ResourceT
20    , ResIO
21    , ReleaseKey
22      -- * Unwrap
23    , runResourceT
24      -- ** Check cleanup exceptions
25    , runResourceTChecked
26    , ResourceCleanupException (..)
27      -- * Special actions
28    , resourceForkWith
29    , resourceForkIO
30      -- * Monad transformation
31    , transResourceT
32    , joinResourceT
33      -- * Registering/releasing
34    , allocate
35    , allocate_
36    , register
37    , release
38    , unprotect
39    , resourceMask
40      -- * Type class/associated types
41    , MonadResource (..)
42    , MonadResourceBase
43      -- ** Low-level
44    , InvalidAccess (..)
45      -- * Re-exports
46    , MonadUnliftIO
47      -- * Internal state
48      -- $internalState
49    , InternalState
50    , getInternalState
51    , runInternalState
52    , withInternalState
53    , createInternalState
54    , closeInternalState
55      -- * Reexport
56    , MonadThrow (..)
57    ) where
58
59import qualified Data.IntMap as IntMap
60import qualified Data.IORef as I
61import Control.Monad.IO.Unlift (MonadIO (..), MonadUnliftIO, withRunInIO)
62import qualified Control.Exception as E
63
64import Control.Monad.Trans.Resource.Internal
65
66import Control.Concurrent (ThreadId, forkIO)
67
68import Control.Monad.Catch (MonadThrow, throwM)
69import Data.Acquire.Internal (ReleaseType (..))
70
71
72
73-- | Register some action that will be called precisely once, either when
74-- 'runResourceT' is called, or when the 'ReleaseKey' is passed to 'release'.
75--
76-- Since 0.3.0
77register :: MonadResource m => IO () -> m ReleaseKey
78register = liftResourceT . registerRIO
79
80-- | Call a release action early, and deregister it from the list of cleanup
81-- actions to be performed.
82--
83-- Since 0.3.0
84release :: MonadIO m => ReleaseKey -> m ()
85release (ReleaseKey istate rk) = liftIO $ release' istate rk
86    (maybe (return ()) id)
87
88-- | Unprotect resource from cleanup actions; this allows you to send
89-- resource into another resourcet process and reregister it there.
90-- It returns a release action that should be run in order to clean
91-- resource or Nothing in case if resource is already freed.
92--
93-- Since 0.4.5
94unprotect :: MonadIO m => ReleaseKey -> m (Maybe (IO ()))
95unprotect (ReleaseKey istate rk) = liftIO $ release' istate rk return
96
97-- | Perform some allocation, and automatically register a cleanup action.
98--
99-- This is almost identical to calling the allocation and then
100-- @register@ing the release action, but this properly handles masking of
101-- asynchronous exceptions.
102--
103-- Since 0.3.0
104allocate :: MonadResource m
105         => IO a -- ^ allocate
106         -> (a -> IO ()) -- ^ free resource
107         -> m (ReleaseKey, a)
108allocate a = liftResourceT . allocateRIO a
109
110-- | Perform some allocation where the return value is not required, and
111-- automatically register a cleanup action.
112--
113-- @allocate_@ is to @allocate@ as @bracket_@ is to @bracket@
114--
115-- This is almost identical to calling the allocation and then
116-- @register@ing the release action, but this properly handles masking of
117-- asynchronous exceptions.
118--
119-- @since 1.2.4
120allocate_ :: MonadResource m
121          => IO a -- ^ allocate
122          -> IO () -- ^ free resource
123          -> m ReleaseKey
124allocate_ a = fmap fst . allocate a . const
125
126-- | Perform asynchronous exception masking.
127--
128-- This is more general then @Control.Exception.mask@, yet more efficient
129-- than @Control.Exception.Lifted.mask@.
130--
131-- Since 0.3.0
132resourceMask :: MonadResource m => ((forall a. ResourceT IO a -> ResourceT IO a) -> ResourceT IO b) -> m b
133resourceMask r = liftResourceT (resourceMaskRIO r)
134
135allocateRIO :: IO a -> (a -> IO ()) -> ResourceT IO (ReleaseKey, a)
136allocateRIO acquire rel = ResourceT $ \istate -> liftIO $ E.mask_ $ do
137    a <- acquire
138    key <- register' istate $ rel a
139    return (key, a)
140
141registerRIO :: IO () -> ResourceT IO ReleaseKey
142registerRIO rel = ResourceT $ \istate -> liftIO $ register' istate rel
143
144resourceMaskRIO :: ((forall a. ResourceT IO a -> ResourceT IO a) -> ResourceT IO b) -> ResourceT IO b
145resourceMaskRIO f = ResourceT $ \istate -> liftIO $ E.mask $ \restore ->
146    let ResourceT f' = f (go restore)
147     in f' istate
148  where
149    go :: (forall a. IO a -> IO a) -> (forall a. ResourceT IO a -> ResourceT IO a)
150    go r (ResourceT g) = ResourceT (\i -> r (g i))
151
152
153
154release' :: I.IORef ReleaseMap
155         -> Int
156         -> (Maybe (IO ()) -> IO a)
157         -> IO a
158release' istate key act = E.mask_ $ do
159    maction <- I.atomicModifyIORef istate lookupAction
160    act maction
161  where
162    lookupAction rm@(ReleaseMap next rf m) =
163        case IntMap.lookup key m of
164            Nothing -> (rm, Nothing)
165            Just action ->
166                ( ReleaseMap next rf $ IntMap.delete key m
167                , Just (action ReleaseEarly)
168                )
169    -- We tried to call release, but since the state is already closed, we
170    -- can assume that the release action was already called. Previously,
171    -- this threw an exception, though given that @release@ can be called
172    -- from outside the context of a @ResourceT@ starting with version
173    -- 0.4.4, it's no longer a library misuse or a library bug.
174    lookupAction ReleaseMapClosed = (ReleaseMapClosed, Nothing)
175
176
177
178-- | Unwrap a 'ResourceT' transformer, and call all registered release actions.
179--
180-- Note that there is some reference counting involved due to 'resourceForkIO'.
181-- If multiple threads are sharing the same collection of resources, only the
182-- last call to @runResourceT@ will deallocate the resources.
183--
184-- /NOTE/ Since version 1.2.0, this function will throw a
185-- 'ResourceCleanupException' if any of the cleanup functions throw an
186-- exception.
187--
188-- @since 0.3.0
189runResourceT :: MonadUnliftIO m => ResourceT m a -> m a
190runResourceT (ResourceT r) = withRunInIO $ \run -> do
191    istate <- createInternalState
192    E.mask $ \restore -> do
193        res <- restore (run (r istate)) `E.catch` \e -> do
194            stateCleanupChecked (Just e) istate
195            E.throwIO e
196        stateCleanupChecked Nothing istate
197        return res
198
199-- | Backwards compatible alias for 'runResourceT'.
200--
201-- @since 1.1.11
202runResourceTChecked :: MonadUnliftIO m => ResourceT m a -> m a
203runResourceTChecked = runResourceT
204{-# INLINE runResourceTChecked #-}
205
206bracket_ :: MonadUnliftIO m
207         => IO () -- ^ allocate
208         -> IO () -- ^ normal cleanup
209         -> IO () -- ^ exceptional cleanup
210         -> m a
211         -> m a
212bracket_ alloc cleanupNormal cleanupExc inside =
213    withRunInIO $ \run -> E.mask $ \restore -> do
214        alloc
215        res <- restore (run inside) `E.onException` cleanupExc
216        cleanupNormal
217        return res
218
219-- | This function mirrors @join@ at the transformer level: it will collapse
220-- two levels of @ResourceT@ into a single @ResourceT@.
221--
222-- Since 0.4.6
223joinResourceT :: ResourceT (ResourceT m) a
224              -> ResourceT m a
225joinResourceT (ResourceT f) = ResourceT $ \r -> unResourceT (f r) r
226
227-- | Introduce a reference-counting scheme to allow a resource context to be
228-- shared by multiple threads. Once the last thread exits, all remaining
229-- resources will be released.
230--
231-- The first parameter is a function which will be used to create the
232-- thread, such as @forkIO@ or @async@.
233--
234-- Note that abuse of this function will greatly delay the deallocation of
235-- registered resources. This function should be used with care. A general
236-- guideline:
237--
238-- If you are allocating a resource that should be shared by multiple threads,
239-- and will be held for a long time, you should allocate it at the beginning of
240-- a new @ResourceT@ block and then call @resourceForkWith@ from there.
241--
242-- @since 1.1.9
243resourceForkWith
244  :: MonadUnliftIO m
245  => (IO () -> IO a)
246  -> ResourceT m ()
247  -> ResourceT m a
248resourceForkWith g (ResourceT f) =
249  ResourceT $ \r -> withRunInIO $ \run -> E.mask $ \restore ->
250    -- We need to make sure the counter is incremented before this call
251    -- returns. Otherwise, the parent thread may call runResourceT before
252    -- the child thread increments, and all resources will be freed
253    -- before the child gets called.
254    bracket_
255        (stateAlloc r)
256        (return ())
257        (return ())
258        (g $ bracket_
259            (return ())
260            (stateCleanup ReleaseNormal r)
261            (stateCleanup ReleaseException r)
262            (restore $ run $ f r))
263
264-- | Launch a new reference counted resource context using @forkIO@.
265--
266-- This is defined as @resourceForkWith forkIO@.
267--
268-- @since 0.3.0
269resourceForkIO :: MonadUnliftIO m => ResourceT m () -> ResourceT m ThreadId
270resourceForkIO = resourceForkWith forkIO
271
272-- | Just use 'MonadUnliftIO' directly now, legacy explanation continues:
273--
274-- A @Monad@ which can be used as a base for a @ResourceT@.
275--
276-- A @ResourceT@ has some restrictions on its base monad:
277--
278-- * @runResourceT@ requires an instance of @MonadUnliftIO@.
279-- * @MonadResource@ requires an instance of @MonadIO@
280--
281-- Note that earlier versions of @conduit@ had a typeclass @ResourceIO@. This
282-- fulfills much the same role.
283--
284-- Since 0.3.2
285type MonadResourceBase = MonadUnliftIO
286{-# DEPRECATED MonadResourceBase "Use MonadUnliftIO directly instead" #-}
287
288-- $internalState
289--
290-- A @ResourceT@ internally is a modified @ReaderT@ monad transformer holding
291-- onto a mutable reference to all of the release actions still remaining to be
292-- performed. If you are building up a custom application monad, it may be more
293-- efficient to embed this @ReaderT@ functionality directly in your own monad
294-- instead of wrapping around @ResourceT@ itself. This section provides you the
295-- means of doing so.
296
297-- | Create a new internal state. This state must be closed with
298-- @closeInternalState@. It is your responsibility to ensure exception safety.
299-- Caveat emptor!
300--
301-- Since 0.4.9
302createInternalState :: MonadIO m => m InternalState
303createInternalState = liftIO
304                    $ I.newIORef
305                    $ ReleaseMap maxBound (minBound + 1) IntMap.empty
306
307-- | Close an internal state created by @createInternalState@.
308--
309-- Since 0.4.9
310closeInternalState :: MonadIO m => InternalState -> m ()
311closeInternalState = liftIO . stateCleanup ReleaseNormal
312
313-- | Get the internal state of the current @ResourceT@.
314--
315-- Since 0.4.6
316getInternalState :: Monad m => ResourceT m InternalState
317getInternalState = ResourceT return
318
319-- | The internal state held by a @ResourceT@ transformer.
320--
321-- Since 0.4.6
322type InternalState = I.IORef ReleaseMap
323
324-- | Unwrap a @ResourceT@ using the given @InternalState@.
325--
326-- Since 0.4.6
327runInternalState :: ResourceT m a -> InternalState -> m a
328runInternalState = unResourceT
329
330-- | Run an action in the underlying monad, providing it the @InternalState@.
331--
332-- Since 0.4.6
333withInternalState :: (InternalState -> m a) -> ResourceT m a
334withInternalState = ResourceT
335