@@ -79,7 +79,8 @@ import Control.Monad.Class.MonadThrow (Exception (..), Handler (..),
7979import Control.Monad.IOSim
8080import Control.Monad.Primitive
8181import Control.Monad.Reader (ReaderT (.. ))
82- import Control.RefCount (RefException , checkForgottenRefs )
82+ import Control.RefCount (RefException , checkForgottenRefs ,
83+ ignoreForgottenRefs )
8384import Control.Tracer (Tracer , nullTracer )
8485import Data.Bifunctor (Bifunctor (.. ))
8586import Data.Constraint (Dict (.. ))
@@ -2251,15 +2252,19 @@ runActionsBracket p init cleanup runner tagger actions =
22512252 $ QLS. runActionsBracket p init cleanup' runner actions
22522253 where
22532254 cleanup' st = do
2254- x <- cleanup st
2255- pure (x QC. .&&. propCheckForgottenRefs)
2256-
2257- propCheckForgottenRefs :: Property
2258- propCheckForgottenRefs = QC. ioProperty $ do
2259- eith <- Control.Exception. try checkForgottenRefs
2260- pure $ case eith of
2261- Left (e :: RefException ) -> QC. counterexample (show e) False
2262- Right () -> QC. property True
2255+ x <- cleanup st `onException` ignoreForgottenRefs
2256+ -- We want to do checkForgottenRefs after cleanup, since cleanup itself
2257+ -- may lead to forgotten refs. And checkForgottenRefs has the crucial
2258+ -- side effect of reseting the forgotten refs state. If we don't do this
2259+ -- then the next test run (e.g. during shrinking) will encounter a
2260+ -- false/stale forgotten refs exception. But we also have to make sure
2261+ -- that if cleanup itself fails, that we reset the forgotten refs state!
2262+ e <- Control.Exception. try checkForgottenRefs
2263+ pure (x QC. .&&. propCheckForgottenRefs e)
2264+
2265+ propCheckForgottenRefs :: Either RefException () -> Property
2266+ propCheckForgottenRefs (Left e) = QC. counterexample (show e) False
2267+ propCheckForgottenRefs (Right () ) = QC. property True
22632268
22642269tagFinalState ::
22652270 forall state . StateModel (Lockstep state )
0 commit comments