Merge pull request #653 from RubenAstudillo/feature/no-leak-history-hook

Fix memory leaks in `historyHook` and `workspaceHistoryHook`
This commit is contained in:
Tony Zorman
2021-11-24 21:03:17 +01:00
committed by GitHub
4 changed files with 33 additions and 11 deletions

View File

@@ -1,3 +1,4 @@
{-# language DeriveGeneric, DeriveAnyClass #-}
---------------------------------------------------------------------- ----------------------------------------------------------------------
-- | -- |
-- Module : XMonad.Actions.GroupNavigation -- Module : XMonad.Actions.GroupNavigation
@@ -34,12 +35,14 @@ module XMonad.Actions.GroupNavigation ( -- * Usage
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.State import Control.Monad.State
import Control.DeepSeq
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, (<|), (><), (|>))
import qualified Data.Sequence as Seq import qualified Data.Sequence as Seq
import qualified Data.Set as Set import qualified Data.Set as Set
import Graphics.X11.Types import Graphics.X11.Types
import GHC.Generics
import Prelude hiding (concatMap, drop, elem, filter, null, reverse) import Prelude hiding (concatMap, drop, elem, filter, null, reverse)
import XMonad.Core import XMonad.Core
import XMonad.ManageHook import XMonad.ManageHook
@@ -156,7 +159,7 @@ orderedWorkspaceList ss wsids = rotateTo isCurWS wspcs'
-- The state extension that holds the history information -- The state extension that holds the history information
data HistoryDB = HistoryDB (Maybe Window) -- currently focused window data HistoryDB = HistoryDB (Maybe Window) -- currently focused window
(Seq Window) -- previously focused windows (Seq Window) -- previously focused windows
deriving (Read, Show) deriving (Read, Show, Generic, NFData)
instance ExtensionClass HistoryDB where instance ExtensionClass HistoryDB where
@@ -166,15 +169,17 @@ 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 >>= updateHistory
db' `deepseq` 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

@@ -28,9 +28,9 @@ module XMonad.Hooks.WorkspaceHistory (
, workspaceHistoryModify , workspaceHistoryModify
) where ) where
import Control.Applicative import Control.Applicative
import Prelude import Control.DeepSeq
import Prelude
import XMonad import XMonad
import XMonad.StackSet hiding (delete, filter, new) import XMonad.StackSet hiding (delete, filter, new)
import XMonad.Prelude (delete, find, foldl', groupBy, nub, sortBy) import XMonad.Prelude (delete, find, foldl', groupBy, nub, sortBy)
@@ -65,6 +65,13 @@ newtype WorkspaceHistory = WorkspaceHistory
-- reverse-chronological order. -- reverse-chronological order.
} deriving (Read, Show) } deriving (Read, Show)
-- @ScreenId@ is not an instance of NFData, but is a newtype on @Int@. @seq@
-- is enough for forcing it. This requires us to provide an instance.
instance NFData WorkspaceHistory where
rnf (WorkspaceHistory hist) =
let go = liftRnf2 rwhnf rwhnf
in liftRnf go hist
instance ExtensionClass WorkspaceHistory where instance ExtensionClass WorkspaceHistory where
initialValue = WorkspaceHistory [] initialValue = WorkspaceHistory []
extensionType = PersistentExtension extensionType = PersistentExtension
@@ -72,13 +79,15 @@ instance ExtensionClass WorkspaceHistory where
-- | A 'logHook' that keeps track of the order in which workspaces have -- | A 'logHook' that keeps track of the order in which workspaces have
-- been viewed. -- been viewed.
workspaceHistoryHook :: X () workspaceHistoryHook :: X ()
workspaceHistoryHook = gets windowset >>= (XS.modify . updateLastActiveOnEachScreen) workspaceHistoryHook = workspaceHistoryHookExclude []
-- | Like 'workspaceHistoryHook', but with the ability to exclude -- | Like 'workspaceHistoryHook', but with the ability to exclude
-- certain workspaces. -- certain workspaces.
workspaceHistoryHookExclude :: [WorkspaceId] -> X () workspaceHistoryHookExclude :: [WorkspaceId] -> X ()
workspaceHistoryHookExclude ws = workspaceHistoryHookExclude ws = do
gets windowset >>= XS.modify . updateLastActiveOnEachScreenExclude ws s <- gets windowset
let update' = force . updateLastActiveOnEachScreenExclude ws s
XS.modify' update'
workspaceHistoryWithScreen :: X [(ScreenId, WorkspaceId)] workspaceHistoryWithScreen :: X [(ScreenId, WorkspaceId)]
workspaceHistoryWithScreen = XS.gets history workspaceHistoryWithScreen = XS.gets history

View File

@@ -20,6 +20,7 @@ module XMonad.Util.ExtensibleState (
-- $usage -- $usage
put put
, modify , modify
, modify'
, remove , remove
, get , get
, gets , gets
@@ -90,6 +91,11 @@ modifyStateExts f = State.modify $ \st -> st { extensibleState = f (extensibleSt
modify :: (ExtensionClass a, XLike m) => (a -> a) -> m () modify :: (ExtensionClass a, XLike m) => (a -> a) -> m ()
modify f = put . f =<< get modify f = put . f =<< get
-- | Like @modify@ but the result value is applied strictly in respect to
-- the monadic environment.
modify' :: (ExtensionClass a, XLike m) => (a -> a) -> m ()
modify' f = (put $!) . f =<< get
-- | Add a value to the extensible state field. A previously stored value with the same -- | Add a value to the extensible state field. A previously stored value with the same
-- type will be overwritten. (More precisely: A value whose string representation of its type -- type will be overwritten. (More precisely: A value whose string representation of its type
-- is equal to the new one's) -- is equal to the new one's)

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,
deepseq
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
, deepseq
, 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