conduit 0.2.1 → 0.2.2
raw patch · 5 files changed
+93/−28 lines, 5 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Control.Monad.Trans.Resource: InvalidAccess :: String -> InvalidAccess
+ Control.Monad.Trans.Resource: data InvalidAccess
+ Control.Monad.Trans.Resource: functionName :: InvalidAccess -> String
+ Control.Monad.Trans.Resource: instance Exception InvalidAccess
+ Control.Monad.Trans.Resource: instance Show InvalidAccess
+ Control.Monad.Trans.Resource: instance Typeable InvalidAccess
+ Control.Monad.Trans.Resource: resourceActive :: Resource m => ResourceT m Bool
+ Data.Conduit.List: zip :: Resource m => Source m a -> Source m b -> Source m (a, b)
- Control.Monad.Trans.Resource: transResourceT :: Base m ~ Base n => (m a -> n a) -> ResourceT m a -> ResourceT n a
+ Control.Monad.Trans.Resource: transResourceT :: Base m ~ Base n => (m a -> n b) -> ResourceT m a -> ResourceT n b
- Data.Conduit.Lazy: lazyConsume :: MonadBaseControl IO m => Source m a -> ResourceT m [a]
+ Data.Conduit.Lazy: lazyConsume :: (Resource m, MonadBaseControl IO m) => Source m a -> ResourceT m [a]
Files
- Control/Monad/Trans/Resource.hs +52/−19
- Data/Conduit/Lazy.hs +13/−8
- Data/Conduit/List.hs +18/−0
- conduit.cabal +1/−1
- test/main.hs +9/−0
Control/Monad/Trans/Resource.hs view
@@ -43,12 +43,14 @@ , ResourceThrow (..) -- ** Low-level , HasRef (..)+ , InvalidAccess (..)+ , resourceActive ) where import Data.Typeable import Data.IntMap (IntMap) import qualified Data.IntMap as IntMap-import Control.Exception (SomeException)+import Control.Exception (SomeException, throw, Exception) import Control.Monad.Trans.Control ( MonadTransControl (..), MonadBaseControl (..) , ComposeSt, defaultLiftBaseWith, defaultRestoreM@@ -260,6 +262,7 @@ data ReleaseMap base = ReleaseMap !NextKey !RefCount !(IntMap (base ()))+ | ReleaseMapClosed -- | The Resource transformer. This transformer keeps track of all registered -- actions, and calls them upon exit (via 'runResourceT'). Actions may be@@ -317,11 +320,26 @@ => Ref base (ReleaseMap base) -> base () -> base ReleaseKey-register' istate rel = atomicModifyRef' istate $ \(ReleaseMap key rf m) ->- ( ReleaseMap (key + 1) rf (IntMap.insert key rel m)- , ReleaseKey key- )+register' istate rel = atomicModifyRef' istate $ \rm ->+ case rm of+ ReleaseMap key rf m ->+ ( ReleaseMap (key + 1) rf (IntMap.insert key rel m)+ , ReleaseKey key+ )+ ReleaseMapClosed -> throw $ InvalidAccess "register'" +data InvalidAccess = InvalidAccess { functionName :: String }+ deriving Typeable++instance Show InvalidAccess where+ show (InvalidAccess f) = concat+ [ "Control.Monad.Trans.Resource."+ , f+ , ": The mutable state is being accessed after cleanup. Please contact the maintainers."+ ]++instance Exception InvalidAccess+ -- | Call a release action early, and deregister it from the list of cleanup -- actions to be performed. release :: Resource m@@ -344,25 +362,30 @@ ( ReleaseMap next rf $ IntMap.delete key m , Just action )+ lookupAction ReleaseMapClosed = throw $ InvalidAccess "release'" stateAlloc :: HasRef m => Ref m (ReleaseMap m) -> m () stateAlloc istate = do- atomicModifyRef' istate $ \(ReleaseMap nk rf m) ->- (ReleaseMap nk (rf + 1) m, ())+ atomicModifyRef' istate $ \rm ->+ case rm of+ ReleaseMap nk rf m ->+ (ReleaseMap nk (rf + 1) m, ())+ ReleaseMapClosed -> throw $ InvalidAccess "stateAlloc" stateCleanup :: HasRef m => Ref m (ReleaseMap m) -> m () stateCleanup istate = mask_ $ do- (rf, m) <- atomicModifyRef' istate $ \(ReleaseMap nk rf m) ->- (ReleaseMap nk (rf - 1) m, (rf - 1, m))- if rf == minBound- then do+ mm <- atomicModifyRef' istate $ \rm ->+ case rm of+ ReleaseMap nk rf m ->+ let rf' = rf - 1+ in if rf' == minBound+ then (ReleaseMapClosed, Just m)+ else (ReleaseMap nk rf' m, Nothing)+ ReleaseMapClosed -> throw $ InvalidAccess "stateCleanup"+ case mm of+ Just m -> mapM_ (\x -> try x >> return ()) $ IntMap.elems m- -- Trigger an exception consistently for one race condition:- -- let's put an undefined value in the state. If somehow- -- another thread is still able to access it, at least we get- -- clearer error messages.- writeRef' istate $ error "Control.Monad.Trans.Resource.stateCleanup: There is a bug in the implementation. The mutable state is being accessed after cleanup. Please contact the maintainers."- else return ()+ Nothing -> return () -- | Unwrap a 'ResourceT' transformer, and call all registered release actions. --@@ -382,9 +405,9 @@ -- strip or add new transformers to a stack, e.g. to run a @ReaderT@. Note that -- the original and new monad must both have the same 'Base' monad. transResourceT :: (Base m ~ Base n)- => (m a -> n a)+ => (m a -> n b) -> ResourceT m a- -> ResourceT n a+ -> ResourceT n b transResourceT f (ResourceT mx) = ResourceT (\r -> f (mx r)) -------- All of our monad et al instances@@ -523,3 +546,13 @@ (return ()) (stateCleanup r) (restore $ f r))++-- | Determine if the current @ResourceT@ is still active. This is necessary+-- for such cases as lazy I\/O, where an unevaluated thunk may still refer to a+-- closed @ResourceT@.+resourceActive :: Resource m => ResourceT m Bool+resourceActive = ResourceT $ \rmMap -> do+ rm <- resourceLiftBase $ readRef' rmMap+ case rm of+ ReleaseMapClosed -> return False+ _ -> return True
Data/Conduit/Lazy.hs view
@@ -8,20 +8,25 @@ import Data.Conduit import System.IO.Unsafe (unsafeInterleaveIO)-import Control.Monad.Trans.Control+import Control.Monad.Trans.Control (MonadBaseControl, liftBaseOp_)+import Control.Monad.Trans.Resource (resourceActive) -- | Use lazy I\/O to consume all elements from a @Source@. -- -- Since 0.2.0-lazyConsume :: MonadBaseControl IO m => Source m a -> ResourceT m [a]+lazyConsume :: (Resource m, MonadBaseControl IO m) => Source m a -> ResourceT m [a] lazyConsume src0 = do go src0 where go src = liftBaseOp_ unsafeInterleaveIO $ do- res <- sourcePull src- case res of- Closed -> return []- Open src' x -> do- y <- go src'- return $ x : y+ ra <- resourceActive+ if ra+ then do+ res <- sourcePull src+ case res of+ Closed -> return []+ Open src' x -> do+ y <- go src'+ return $ x : y+ else return []
Data/Conduit/List.hs view
@@ -18,6 +18,7 @@ , take , drop , head+ , zip , peek , consume , sinkNull@@ -322,3 +323,20 @@ -- Since 0.2.0 sourceNull :: Resource m => Source m a sourceNull = mempty++-- | Combines two sources. The new source will stop producing once either+-- source has been exhausted.+--+-- Since 0.2.2+zip :: Resource m => Source m a -> Source m b -> Source m (a, b)+zip sa sb = Source pull close+ where+ pull = do ra <- sourcePull sa+ case ra of+ Closed -> return Closed+ Open ra' a -> do rb <- sourcePull sb+ case rb of+ Closed -> return Closed+ Open rb' b -> return $ Open (zip ra' rb') (a, b)+ close = sourceClose sa >> sourceClose sb+
conduit.cabal view
@@ -1,5 +1,5 @@ Name: conduit-Version: 0.2.1+Version: 0.2.2 Synopsis: Streaming data processing library. Description: Conduits are an approach to the streaming data problem. It is meant as an alternative to enumerators\/iterators, hoping to address the same issues with different trade-offs based on real-world experience with enumerators. For more information, see <http://www.yesodweb.com/book/conduit>.
test/main.hs view
@@ -121,6 +121,11 @@ bs1 @=? bs2 bs1 @=? bs3 + describe "zipping" $ do+ it "zipping two small lists" $ do+ res <- runResourceT $ CL.zip (CL.sourceList [1..10]) (CL.sourceList [11..12]) C.$$ CL.consume+ res @=? zip [1..10 :: Int] [11..12 :: Int]+ describe "Monad instance for Sink" $ do it "binding" $ do x <- runResourceT $ CL.sourceList [1..10] C.$$ do@@ -242,6 +247,10 @@ ) nums <- CLazy.lazyConsume $ mconcat $ map incr [1..10] liftIO $ nums @?= [1..10]++ it' "returns nothing outside ResourceT" $ do+ bss <- runResourceT $ CLazy.lazyConsume $ CB.sourceFile "test/main.hs"+ bss @?= [] describe "sequence" $ do it "simple sink" $ do