1
+ {-# LANGUAGE ScopedTypeVariables #-}
2
+ {-# LANGUAGE MagicHash #-}
3
+ {-# LANGUAGE UnboxedTuples #-}
1
4
module Main where
2
5
3
6
import Control.Applicative ((<$>) )
@@ -8,8 +11,11 @@ import qualified Data.HashMap.Strict as HM
8
11
import qualified Data.HashMap.Lazy as HML
9
12
import Data.List (delete )
10
13
import Data.Maybe
14
+ import GHC.Exts (touch #)
15
+ import GHC.IO (IO (.. ))
11
16
import System.Mem (performGC )
12
17
import System.Mem.Weak (mkWeakPtr , deRefWeak )
18
+ import System.Random (randomIO )
13
19
import Test.HUnit (Assertion , assert )
14
20
import Test.Framework (Test , defaultMain )
15
21
import Test.Framework.Providers.HUnit (testCase )
@@ -85,46 +91,37 @@ newtype KC = KC Int
85
91
instance Hashable KC where
86
92
hashWithSalt salt _ = salt
87
93
88
- issue254Lazy :: Assertion
89
- issue254Lazy = issue254LazyLambda 2
94
+ touch :: a -> IO ()
95
+ touch a = IO ( \ s -> ( # touch # a s, () # ))
90
96
91
97
-- We want to make sure that old values in the HashMap are evicted when new values are inserted,
92
98
-- even if they aren't evaluated. To do that, we use the WeakPtr trick described at
93
99
-- http://simonmar.github.io/posts/2018-06-20-Finding-fixing-space-leaks.html.
94
100
-- We insert a value named oldV into the HashMap, then insert over it, checking oldV is no longer reachable.
95
101
--
96
- -- To make the test robust, it's important that oldV isn't hoisted up to the top or shared. To do that,
97
- -- we use NOINLINE, make oldV dependent on an unseen argument, and insert _ <- return () to ensure oldV
98
- -- is under a lambda.
99
- {-# NOINLINE issue254LazyLambda #-}
100
- issue254LazyLambda :: Int -> Assertion
101
- issue254LazyLambda i = do
102
- _ <- return () -- put oldV under a lambda
102
+ -- To make the test robust, it's important that oldV isn't hoisted up to the top or shared.
103
+ -- To do that, we generate it randomly.
104
+ issue254Lazy :: Assertion
105
+ issue254Lazy = do
106
+ i :: Int <- randomIO
103
107
let oldV = error $ " Should not be evaluated: " ++ show i
104
- weakV <- mkWeakPtr oldV Nothing -- test whether oldV is alive
105
- let mp = HML. insert (KC 1 ) (error " Should not be evaluated" ) $ HML. fromList [(KC 0 , " 1" ), (KC 1 , oldV)]
106
- _ <- evaluate mp -- force the insert to happen
108
+ weakV <- mkWeakPtr oldV Nothing -- add the ability to test whether oldV is alive
109
+ mp <- evaluate $ HML. insert (KC 1 ) (error " Should not be evaluated" ) $ HML. fromList [(KC 0 , " 1" ), (KC 1 , oldV)]
107
110
performGC
108
111
res <- deRefWeak weakV -- gives Just if oldV is still alive
109
- _ <- evaluate mp -- makes sure that we didn't GC away the whole HashMap, just oldV
112
+ touch mp -- makes sure that we didn't GC away the whole HashMap, just oldV
110
113
assert $ isNothing res
111
114
112
115
-- Like issue254Lazy, but using strict HashMap
113
116
issue254Strict :: Assertion
114
- issue254Strict = issue254StrictLambda 2
115
-
116
- -- Important that oldV is not hoisted out by optimisation, so use NOINLINE
117
- {-# NOINLINE issue254StrictLambda #-}
118
- issue254StrictLambda :: Int -> Assertion
119
- issue254StrictLambda i = do
120
- _ <- return ()
117
+ issue254Strict = do
118
+ i :: Int <- randomIO
121
119
let oldV = show i
122
120
weakV <- mkWeakPtr oldV Nothing
123
- let mp = HM. insert (KC 1 ) " 3" $ HM. fromList [(KC 0 , " 1" ), (KC 1 , oldV)]
124
- _ <- evaluate mp
121
+ mp <- evaluate $ HM. insert (KC 1 ) " 3" $ HM. fromList [(KC 0 , " 1" ), (KC 1 , oldV)]
125
122
performGC
126
123
res <- deRefWeak weakV
127
- _ <- evaluate mp
124
+ touch mp
128
125
assert $ isNothing res
129
126
130
127
------------------------------------------------------------------------
0 commit comments