teardown 0.4.0.0 → 0.4.1.0
raw patch · 7 files changed
+97/−73 lines, 7 filesdep +gaugedep −criterionPVP ok
version bump matches the API change (PVP)
Dependencies added: gauge
Dependencies removed: criterion
API changes (from Hackage documentation)
Files
- CHANGELOG.md +10/−0
- README.md +1/−1
- benchmark/Main.hs +12/−2
- src/Control/Teardown/Internal/Core.hs +52/−24
- src/Control/Teardown/Internal/Printer.hs +3/−1
- teardown.cabal +7/−8
- test/tasty/TeardownTest.hs +12/−37
CHANGELOG.md view
@@ -7,6 +7,16 @@ [1]: http://semver.org/spec/v2.0.0.html [2]: https://github.com/roman/Haskell-teardown/libraries/teardown/CHANGELOG.md +## v0.4.1.0++* Ensure that all `IO ()` sub-routines on `runTeardown` get executed inside a+ `MaskedUninterruptible` masking state+* Deprecate the `[(Text, IO ())]` instance of `IResource` in favor of creating a+ teardown record per de-allocated resource.+* Replace `criterion` in favor of `gauge`+* Bump `rio` to v0.1.1.0+* Improve upon documentation+ ## v0.4.0.0 **BREAKING CHANGES**
README.md view
@@ -39,7 +39,7 @@ ## Development [](https://travis-ci.org/roman/Haskell-teardown)-[](https://img.shields.io/github/commits-since/roman/haskell-teardown/v0.1.0.1.svg)+[](https://img.shields.io/github/commits-since/roman/haskell-teardown/v0.4.0.0.svg) [](http://packdeps.haskellers.com/feed?needle=teardown) Follow the [developer guidelines](https://romanandreg.gitbooks.io/teardown/content/CONTRIBUTING.html)
benchmark/Main.hs view
@@ -1,11 +1,16 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Main where import RIO -import Criterion-import Criterion.Main+#if MIN_VERSION_gauge(0,2,0)+import Gauge+#else+import Gauge+import Gauge.Main (defaultMain)+#endif import Control.Teardown (newTeardown, runTeardown_) @@ -20,4 +25,9 @@ bench "with teardown" (whnfIO $ runTeardown_ unitTeardown) ) ]+ , env+ (sequence (replicate 1000 (newTeardown "benchmark" (return () :: IO ())))+ >>= newTeardown "parent")+ (\composedTeardown ->+ bench "teardown list" (whnfIO $ runTeardown_ composedTeardown)) ]
src/Control/Teardown/Internal/Core.hs view
@@ -1,4 +1,6 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE NoImplicitPrelude #-} module Control.Teardown.Internal.Core@@ -10,6 +12,7 @@ , runTeardown , runTeardown_+ , newTeardown ) where @@ -17,6 +20,10 @@ import RIO.Time (NominalDiffTime, diffUTCTime, getCurrentTime) +#if MIN_VERSION_base(4,11,0)+import qualified GHC.TypeLits as Ty+#endif+ import Control.Teardown.Internal.Types --------------------------------------------------------------------------------@@ -42,7 +49,7 @@ EmptyResult{} -> False --- | Creates a new "Teardown" sub-routine from a cleanup "IO" action, the+-- | Creates a new "Teardown" record from a cleanup "IO" action, the -- side-effects from this action are guaranteed to be executed only once, and -- also it is guaranteed to be thread-safe in the scenario of multiple threads -- executing the same teardown procedure.@@ -80,11 +87,6 @@ return $ BranchResult desc elapsed teardownFailed teardownResults --- | Creates a "Teardown" sub-routine that is composed of inner sub-routines--- that are allocated at runtime. This is useful if allocations are being--- created and being hold on a Mutable variable of some sort (e.g. "IORef",--- "TVar", etc) so that on cleanup this Mutable variable is read and the--- results of the teardown operation are returned. newDynTeardown :: Description -> IO [TeardownResult] -> Teardown newDynTeardown desc action = Teardown $ do teardownResults <- action@@ -99,6 +101,7 @@ -- expects a teardown return but there is no allocation being made emptyTeardown :: Description -> Teardown emptyTeardown desc = Teardown (return $ emptyTeardownResult desc)+{-# INLINE emptyTeardown #-} -------------------------------------------------------------------------------- @@ -120,13 +123,14 @@ let result = map (foldTeardownResult leafStep branchStep acc) results in branchStep result desc --- | Returns number of sub-routines executed at teardown+-- | Returns number of released resources from a "runTeardown" execution toredownCount :: TeardownResult -> Int toredownCount = foldTeardownResult (\acc _ _ -> acc + 1) (\results _ -> sum results) 0+{-# INLINE toredownCount #-} -- | Returns number of sub-routines that threw an exception on execution of--- teardown+-- "runTeardown" failedToredownCount :: TeardownResult -> Int failedToredownCount = foldTeardownResult (\acc _ mErr -> acc + maybe 0 (const 1) mErr)@@ -137,26 +141,45 @@ instance HasTeardown Teardown where getTeardown = id+ {-# INLINE getTeardown #-} --- | Creates a Teardown record from a simple `IO ()` sub-routine+-- | Creates a new "Teardown" record from a cleanup "IO ()" sub-routine; the+-- Teardown API guarantees:+--+-- * The execution of given "IO ()" sub-routine happens exactly once+-- * The execution is thread-safe when multiple threads try to call "runTeardown"+--+-- IMPORTANT: The @IO ()@ sub-routine _must not_ block or take a long time; this+-- sub-routine cannot be stopped by an async exception instance IResource (IO ()) where newTeardown = newTeardownIO+ {-# INLINE newTeardown #-} --- | Creates a Teardown record from a simple list of cleanup sub-routines--- (creating a Teardown record for each), ideal when one single component has--- many resources allocated and need to be cleaned out all at once.+-- | Deprecated instance that creates a Teardown record from a list of cleanup+-- sub-routines (creating a Teardown record for each). --+-- WARNING: This function assumes you are creating many sub-resources at once;+-- this approach has a major risk of leaking resources, and that is why is+-- deprecated; execute newTeardown for every resource you allocate.+-- -- NOTE: The @IO ()@ sub-routines given are going to be executed in reverse -- order at teardown time. --+-- Since 0.4.1.0+#if MIN_VERSION_base(4,11,0)+instance Ty.TypeError ('Ty.Text "DEPRECATED: Execute a 'newTeardown' call per allocated resource")+ => IResource [(Text, IO ())] where+ newTeardown desc actionList =+ concatTeardown desc <$> mapM (uncurry newTeardown) actionList+#else instance IResource [(Text, IO ())] where newTeardown desc actionList = concatTeardown desc <$> mapM (uncurry newTeardown) actionList+#endif --- | Wraps a "Teardown" record; the new record will have one extra level of--- description.---+-- | Wraps an existing "Teardown" record; the wrapper "Teardown" record represents+-- a "parent resource" on the "TeardownResult" instance IResource Teardown where newTeardown desc = return . concatTeardown desc . return@@ -168,6 +191,7 @@ instance IResource [Teardown] where newTeardown desc = return . concatTeardown desc+ {-# INLINE newTeardown #-} -- | Wraps an IO action that returns a list of "Teardown" record; the new record -- will have one extra level of description. Same behaviour as the @[(Text, IO@@ -177,24 +201,28 @@ instance IResource (IO [Teardown]) where newTeardown desc getTeardownList = concatTeardown desc <$> getTeardownList+ {-# INLINE newTeardown #-} --- | Creates a "Teardown" sub-routine that is composed of inner sub-routines--- that are allocated at runtime. This is useful if allocations are being--- created and being hold on a Mutable variable of some sort (e.g. "IORef",--- "TVar", etc) so that on teardown time this Mutable variable is read and--- executed and the results are returned.+-- | Creates a "Teardown" record from executing a sub-routine that releases+-- short-lived "Teardown" records. This is useful when short-lived "Teardown"+-- are accumulated on a collection inside a mutable variable (e.g. @IORef@,+-- @TVar@, etc) and we want to release them instance IResource (IO [TeardownResult]) where newTeardown desc = return . newDynTeardown desc+ {-# INLINE newTeardown #-} -------------------------------------------------------------------------------- --- | Executes all composed "Teardown" sub-routines safely, and returns a Tree--- data structure wich can be used to gather facts from the cleanup process.+-- | Executes all composed "Teardown" sub-routines safely. This version returns+-- a Tree data structure wich can be used to gather facts from the resource+-- cleanup runTeardown :: HasTeardown t => t -> IO TeardownResult runTeardown t0 =- let (Teardown teardownAction) = getTeardown t0 in teardownAction+ let (Teardown teardownAction) = getTeardown t0 in uninterruptibleMask_ teardownAction+{-# INLINE runTeardown #-} --- | Executes all composed "Teardown" sub-routines safely.+-- | Executes all composed "Teardown" sub-routines safely runTeardown_ :: HasTeardown t => t -> IO () runTeardown_ = void . runTeardown+{-# INLINE runTeardown_ #-}
src/Control/Teardown/Internal/Printer.hs view
@@ -2,12 +2,14 @@ {-# LANGUAGE OverloadedStrings #-} module Control.Teardown.Internal.Printer where -import RIO+import Data.Monoid ((<>))+import RIO hiding ((<>)) import Data.Typeable (typeOf) import qualified RIO.Text as Text import Text.PrettyPrint.ANSI.Leijen hiding ((<>))+ import Control.Teardown.Internal.Types
teardown.cabal view
@@ -1,5 +1,5 @@ name: teardown-version: 0.4.0.0+version: 0.4.1.0 cabal-version: >=1.10 build-type: Simple license: MIT@@ -9,16 +9,15 @@ stability: alpha (experimental) homepage: https://github.com/roman/Haskell-teardown#readme bug-reports: https://github.com/roman/Haskell-teardown/issues-synopsis: Build composable components for your application with clear teardown semantics+synopsis: Build safe and composable teardown sub-routines for resources description: The teardown library allows you to reliably execute cleanup sub-routines for- allocated resources when a program is initialized; it:+ allocated resources. When a program is initialized, it: .- * Ensures that teardown sub-routines are executed /exactly/ once, even on the- scenario where cleanup is invoked multiple times+ * Ensures that teardown sub-routines are executed /exactly/ once .- * Ensures that if errors occur on the execution of one teardown, the error- does not propagate to other sub-routines; effectively keeping them isolated.+ * Ensures that if errors occur on the execution of a Teardown sub-routine, the+ error does not propagate to others; bulkheading failure on cleanup. . * Requires every sub-routine to be documented at creation time; thus helping tracing your application structure.@@ -76,7 +75,7 @@ main-is: Main.hs build-depends: base >=4.8 && <5,- criterion >=1.1,+ gauge >=0.1.3, rio >=0.0.0.0, teardown -any, typed-process >=0.1.0.0,
test/tasty/TeardownTest.hs view
@@ -4,9 +4,10 @@ import RIO -import Control.Monad (replicateM)-import qualified Control.Teardown as SUT-import Test.Tasty (TestTree, testGroup)+import Control.Exception (MaskingState (..), getMaskingState)+import Control.Monad (replicateM)+import qualified Control.Teardown as SUT+import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit @@ -87,38 +88,12 @@ assertEqual "failed teardown action must be correct" 5 (SUT.failedToredownCount toredownResult)- , testCase "teardown with list of description and actions executes correctly"- $ do- callCountRef <- newIORef (0 :: Int)- teardownAction <- SUT.newTeardown- "bigger-system"- [ ("1" :: Text, modifyIORef callCountRef (+ 1) :: IO ())- , ("2" , modifyIORef callCountRef (+ 1))- , ("3" , modifyIORef callCountRef (+ 1))- , ("4" , modifyIORef callCountRef (+ 1))- , ("5" , modifyIORef callCountRef (+ 1))- , ("6" , error "nope")- , ("7" , error "nope")- , ("8" , error "nope")- , ("9" , error "nope")- ]--- -- Execute multiple times to assert idempotency- toredownResult <- SUT.runTeardown teardownAction- replicateM_ 9 (SUT.runTeardown teardownAction)-- assertEqual "teardown action count must be correct"- 9- (SUT.toredownCount toredownResult)-- assertEqual "failed teardown must be correct"- 4- (SUT.failedToredownCount toredownResult)-- callCount <- readIORef callCountRef- assertEqual- "side-effects were executed despite errors on other teardown operations"- 5- callCount+ , testCase "Teardown sub-routine executes on an uninterruptedMask" $ do+ resultVar <- newEmptyMVar+ teardown <- SUT.newTeardown "test" (getMaskingState >>= putMVar resultVar)+ SUT.runTeardown_ teardown+ masking <- takeMVar resultVar+ assertEqual "Expecting Teardown masked state is Uninterruptible"+ MaskedUninterruptible+ masking ]