Skip to content

Commit c8c627e

Browse files
authored
Gate noThunksInvariant behind expensive-invariants build flag (#1651)
This PR closes #1649 by: * Defining the `expensive-invariants` build flag (disabled by default) * Gating the use of `unsafeNoThunks` (the expensive check to avoid) behind the `ENABLE_EXPENSIVE_INVARIANTS` CPP flag (implied by `expensive-invariants`) * Breaking `noThunkInvariants` into its own module to reduce the amount of CPP needed to compile without warnings * Tweaking the cabal overrides used in CI to: + Run with `+checktvarinvariants` and `+checkmvarinvariants` by default (via `asserts.cabal`) + Run `no-thunks` CI with `+expensive-invariants` in addition to the default flags
2 parents 55f7524 + 2de10b2 commit c8c627e

File tree

8 files changed

+62
-19
lines changed

8 files changed

+62
-19
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import: .github/workflows/cabal.project.default.local
22

3-
package strict-checked-vars
4-
flags: +checktvarinvariants +checkmvarinvariants
3+
package ouroboros-consensus
4+
flags: +expensive-invariants

asserts.cabal

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,7 @@ package strict-stm
5656

5757
package text-short
5858
flags: +asserts
59+
60+
package strict-checked-vars
61+
flags: +checktvarinvariants +checkmvarinvariants
62+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
### Non-Breaking
2+
3+
- Gate `NoThunks` invariant checks behind the `expensive-invariants` build to allow for:
4+
+ No invariant checking in production
5+
+ Cheap (domain-specific) invariant checking in regular CI
6+
+ Cheap and expensive invariant checking in nightly CI

ouroboros-consensus/ouroboros-consensus.cabal

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ flag asserts
2828
manual: False
2929
default: False
3030

31+
flag expensive-invariants
32+
description: Enable checks for expensive invariants
33+
manual: True
34+
default: False
35+
3136
common common-lib
3237
default-language: Haskell2010
3338
ghc-options:
@@ -46,6 +51,9 @@ common common-lib
4651
ghc-options: -fno-ignore-asserts
4752
cpp-options: -DENABLE_ASSERTIONS
4853

54+
if flag(expensive-invariants)
55+
cpp-options: -DENABLE_EXPENSIVE_INVARIANTS
56+
4957
common common-test
5058
import: common-lib
5159
ghc-options:
@@ -284,6 +292,7 @@ library
284292
Ouroboros.Consensus.Util.LeakyBucket
285293
Ouroboros.Consensus.Util.MonadSTM.NormalForm
286294
Ouroboros.Consensus.Util.MonadSTM.StrictSVar
295+
Ouroboros.Consensus.Util.NormalForm.Invariant
287296
Ouroboros.Consensus.Util.NormalForm.StrictMVar
288297
Ouroboros.Consensus.Util.NormalForm.StrictTVar
289298
Ouroboros.Consensus.Util.Orphans

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/MiniProtocol/ChainSync/Client.hs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ import Data.Word (Word64)
9696
import GHC.Generics (Generic)
9797
import GHC.Stack (HasCallStack)
9898
import Network.TypedProtocol.Core
99-
import NoThunks.Class (unsafeNoThunks)
10099
import Ouroboros.Consensus.Block
101100
import Ouroboros.Consensus.BlockchainTime (RelativeTime)
102101
import Ouroboros.Consensus.Config
@@ -2190,7 +2189,7 @@ continueWithState ::
21902189
Stateful m blk s st ->
21912190
m (Consensus st blk m)
21922191
continueWithState !s (Stateful f) =
2193-
checkInvariant (show <$> unsafeNoThunks s) $ f s
2192+
checkInvariant (noThunksInvariant s) $ f s
21942193

21952194
{-------------------------------------------------------------------------------
21962195
Return value
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{-# LANGUAGE CPP #-}
2+
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
3+
{-# OPTIONS_GHC -Wno-unused-imports #-}
4+
5+
-- | 'NoThunks' invariant.
6+
--
7+
-- Due to its expensive nature, this invariant is gated behind the
8+
-- @expensive-invariants@ flag from the @ouroboros-consensus@ package.
9+
module Ouroboros.Consensus.Util.NormalForm.Invariant
10+
( -- * Invariant
11+
noThunksInvariant
12+
) where
13+
14+
import NoThunks.Class (NoThunks (..), unsafeNoThunks)
15+
16+
{-------------------------------------------------------------------------------
17+
Invariant
18+
-------------------------------------------------------------------------------}
19+
20+
noThunksInvariant :: NoThunks a => a -> Maybe String
21+
#ifdef ENABLE_EXPENSIVE_INVARIANTS
22+
noThunksInvariant = fmap show . unsafeNoThunks
23+
#else
24+
noThunksInvariant = const Nothing
25+
#endif

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Util/NormalForm/StrictMVar.hs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@
99
-- 'NoThunks' invariant. See 'newMVarWithInvariant' and
1010
-- 'newEmptyMVarWithInvariant'.
1111
--
12-
-- Use the @checkmvarinvariants@ cabal flag from the @strict-checked-vars@
13-
-- package to enable or disable invariant checks at compile time.
12+
-- Due to their expensive nature, checks for the 'NoThunks' invariant are
13+
-- disabled by default and can be enabled at compile-time via the
14+
-- @expensive-invariants@ flag from the @ouroboros-consensus@ package. To
15+
-- disable invariant checks entirely (i.e., both 'NoThunks' and custom ones),
16+
-- use the @checkmvarinvariants@ cabal flag from the @strict-checked-vars@
17+
-- package.
1418
--
1519
-- The exports of this module (should) mirror the exports of the
1620
-- "Control.Concurrent.Class.MonadMVar.Strict.Checked" module from the
@@ -42,7 +46,8 @@ import Control.Concurrent.Class.MonadMVar.Strict.Checked hiding
4246
)
4347
import qualified Control.Concurrent.Class.MonadMVar.Strict.Checked as Checked
4448
import GHC.Stack (HasCallStack)
45-
import NoThunks.Class (NoThunks (..), unsafeNoThunks)
49+
import NoThunks.Class (NoThunks (..))
50+
import Ouroboros.Consensus.Util.NormalForm.Invariant (noThunksInvariant)
4651

4752
{-------------------------------------------------------------------------------
4853
StrictMVar
@@ -80,13 +85,6 @@ newEmptyMVarWithInvariant ::
8085
newEmptyMVarWithInvariant inv =
8186
Checked.newEmptyMVarWithInvariant (\x -> inv x <> noThunksInvariant x)
8287

83-
{-------------------------------------------------------------------------------
84-
Invariant
85-
-------------------------------------------------------------------------------}
86-
87-
noThunksInvariant :: NoThunks a => a -> Maybe String
88-
noThunksInvariant = fmap show . unsafeNoThunks
89-
9088
{-------------------------------------------------------------------------------
9189
NoThunks instance
9290
-------------------------------------------------------------------------------}

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Util/NormalForm/StrictTVar.hs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@
99
-- 'NoThunks' invariant. See 'newTVarWithInvariant' and
1010
-- 'newTVarWithInvariantIO'.
1111
--
12-
-- Use the @checktvarinvariants@ cabal flag from the @strict-checked-vars@
13-
-- package to enable or disable invariant checks at compile time.
12+
-- Due to their expensive nature, checks for the 'NoThunks' invariant are
13+
-- disabled by default and can be enabled at compile-time via the
14+
-- @expensive-invariants@ flag from the @ouroboros-consensus@ package. To
15+
-- disable invariant checks entirely (i.e., both 'NoThunks' and custom ones),
16+
-- use the @checktvarinvariants@ cabal flag from the @strict-checked-vars@
17+
-- package.
1418
--
1519
-- The exports of this module (should) mirror the exports of the
1620
-- "Control.Concurrent.Class.MonadSTM.Strict.TVar.Checked" module from the
@@ -43,9 +47,7 @@ import Control.Concurrent.Class.MonadSTM.Strict.TVar.Checked hiding
4347
import qualified Control.Concurrent.Class.MonadSTM.Strict.TVar.Checked as Checked
4448
import GHC.Stack
4549
import NoThunks.Class (NoThunks (..))
46-
import Ouroboros.Consensus.Util.NormalForm.StrictMVar
47-
( noThunksInvariant
48-
)
50+
import Ouroboros.Consensus.Util.NormalForm.Invariant (noThunksInvariant)
4951

5052
{-------------------------------------------------------------------------------
5153
StrictTVar

0 commit comments

Comments
 (0)