Force values in setValues and getValues (#2494)

Otherwise, we can end up retaining references to the old map which
prevent it from being garbage collected.

On a simple testcase that repeatedly opens and closes a module, this
seems to make memory usage constant whereas it was increasing
each time before.
This commit is contained in:
Moritz Kiefer 2019-08-12 17:40:12 +02:00 committed by Gary Verhaegen
parent 79c04cd202
commit 7e0f263720

View File

@ -248,16 +248,31 @@ setValues :: IdeRule k v
-> NormalizedFilePath
-> Value v
-> IO ()
setValues state key file val = modifyVar_ state $
pure . HMap.insert (file, Key key) (fmap toDyn val)
setValues state key file val = modifyVar_ state $ \vals -> do
-- Force to make sure the old HashMap is not retained
evaluate $ HMap.insert (file, Key key) (fmap toDyn val) vals
-- | We return Nothing if the rule has not run and Just Failed if it has failed to produce a value.
getValues :: forall k v. IdeRule k v => Var Values -> k -> NormalizedFilePath -> IO (Maybe (Value v))
getValues state key file = do
vs <- readVar state
return $ do
v <- HMap.lookup (file, Key key) vs
pure $ fmap (fromJust . fromDynamic @v) v
case HMap.lookup (file, Key key) vs of
Nothing -> pure Nothing
Just v -> do
let r = fmap (fromJust . fromDynamic @v) v
-- Force to make sure we do not retain a reference to the HashMap
-- and we blow up immediately if the fromJust should fail
-- (which would be an internal error).
evaluate (r `seqValue` Just r)
-- | Seq the result stored in the Shake value. This only
-- evaluates the value to WHNF not NF. We take care of the latter
-- elsewhere and doing it twice is expensive.
seqValue :: Value v -> b -> b
seqValue v b = case v of
Succeeded ver v -> rnf ver `seq` v `seq` b
Stale ver v -> rnf ver `seq` v `seq` b
Failed -> b
-- | Open a 'IdeState', should be shut using 'shakeShut'.
shakeOpen :: (LSP.FromServerMessage -> IO ()) -- ^ diagnostic handler