Fix historyHook memory leak

updateHistory leaks unfiltered windows from previous states as it is never
forced. The consumer of such data structure is not visible to ghc, so the
demand analysis has to fallback on pure laziness.

We fix this inserting evaluation points on the `historyHook` function. We do
this for two reasons, this is the only function calling `updateHistory`.
Plus we cannot do it clearly at the `updateHistory` function as we operate
inside a continuation on withWindowSet. In respect to the `put`, everything
would be a big thunk.
This commit is contained in:
Ruben Astudillo 2021-11-20 01:29:46 -03:00
parent 282afefddf
commit ed5d6f0d78
2 changed files with 12 additions and 4 deletions

View File

@ -34,6 +34,7 @@ module XMonad.Actions.GroupNavigation ( -- * Usage
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.State import Control.Monad.State
import Control.Seq (using, seqFoldable, rseq)
import Data.Map ((!)) import Data.Map ((!))
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Sequence (Seq, ViewL (EmptyL, (:<)), viewl, (<|), (><), (|>)) import Data.Sequence (Seq, ViewL (EmptyL, (:<)), viewl, (<|), (><), (|>))
@ -166,15 +167,20 @@ instance ExtensionClass HistoryDB where
-- | Action that needs to be executed as a logHook to maintain the -- | Action that needs to be executed as a logHook to maintain the
-- focus history of all windows as the WindowSet changes. -- focus history of all windows as the WindowSet changes.
historyHook :: X () historyHook :: X ()
historyHook = XS.get >>= updateHistory >>= XS.put historyHook = do
db <- XS.get
db'@(HistoryDB cur del) <- updateHistory db
let del' = del `using` seqFoldable rseq
cur' = cur `using` seqFoldable rseq
cur' `seq` del' `seq` XS.put db'
-- Updates the history in response to a WindowSet change -- Updates the history in response to a WindowSet change
updateHistory :: HistoryDB -> X HistoryDB updateHistory :: HistoryDB -> X HistoryDB
updateHistory (HistoryDB oldcur oldhist) = withWindowSet $ \ss -> do updateHistory (HistoryDB oldcur oldhist) = withWindowSet $ \ss ->
let newcur = SS.peek ss let newcur = SS.peek ss
wins = Set.fromList $ SS.allWindows ss wins = Set.fromList $ SS.allWindows ss
newhist = Seq.filter (`Set.member` wins) (ins oldcur oldhist) newhist = Seq.filter (`Set.member` wins) (ins oldcur oldhist)
return $ HistoryDB newcur (del newcur newhist) in pure $ HistoryDB newcur (del newcur newhist)
where where
ins x xs = maybe xs (<| xs) x ins x xs = maybe xs (<| xs) x
del x xs = maybe xs (\x' -> Seq.filter (/= x') xs) x del x xs = maybe xs (\x' -> Seq.filter (/= x') xs) x

View File

@ -61,7 +61,8 @@ library
unix, unix,
X11 >= 1.10 && < 1.11, X11 >= 1.10 && < 1.11,
xmonad >= 0.16.99999 && < 0.18, xmonad >= 0.16.99999 && < 0.18,
utf8-string utf8-string,
parallel
default-language: Haskell2010 default-language: Haskell2010
cpp-options: -DXMONAD_CONTRIB_VERSION_MAJOR=0 cpp-options: -DXMONAD_CONTRIB_VERSION_MAJOR=0
@ -448,6 +449,7 @@ test-suite tests
, process , process
, unix , unix
, utf8-string , utf8-string
, parallel
, xmonad >= 0.16.9999 && < 0.18 , xmonad >= 0.16.9999 && < 0.18
cpp-options: -DTESTING cpp-options: -DTESTING
ghc-options: -Wall -Wno-unused-do-bind ghc-options: -Wall -Wno-unused-do-bind