Skip to content

Commit 4ae3c84

Browse files
committed
remove finished async from database
1 parent bd69f9f commit 4ae3c84

File tree

4 files changed

+83
-49
lines changed

4 files changed

+83
-49
lines changed

ghcide/src/Development/IDE/Core/Shake.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ import Development.IDE.Graph.Database (ShakeDatabase,
154154
shakeProfileDatabase,
155155
shakeRunDatabaseForKeysSep,
156156
shakeShutDatabase,
157-
shakedatabaseRuntimeRevDep)
157+
shakedatabaseRuntimeDep)
158158
import Development.IDE.Graph.Internal.Action (runActionInDbCb)
159159
import Development.IDE.Graph.Internal.Database (AsyncParentKill (AsyncParentKill))
160160
import Development.IDE.Graph.Internal.Key (memberKeySet)
@@ -951,10 +951,10 @@ runRestartTask recorder ideStateVar shakeRestartArgs = do
951951
shakeSession
952952
( \runner -> do
953953
newDirtyKeys <- sraBetweenSessions shakeRestartArgs
954-
reverseMap <- shakedatabaseRuntimeRevDep shakeDb
954+
-- reverseMap <- shakedatabaseRuntimeDep shakeDb
955955
(preservekvs, allRunning2) <- shakeComputeToPreserve shakeDb $ fromListKeySet newDirtyKeys
956956
let uneffected = fst <$> preservekvs
957-
logWith recorder Debug $ LogPreserveKeys uneffected newDirtyKeys allRunning2 reverseMap
957+
logWith recorder Debug $ LogPreserveKeys uneffected newDirtyKeys allRunning2 mempty
958958
(stopTime, ()) <- duration $ logErrorAfter 10 $ cancelShakeSession runner $ S.fromList $ map snd preservekvs
959959
-- it is every important to update the dirty keys after we enter the critical section
960960
-- see Note [Housekeeping rule cache and dirty key outside of hls-graph]

hls-graph/src/Development/IDE/Graph/Database.hs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ module Development.IDE.Graph.Database(
1414
shakeShutDatabase,
1515
shakeGetActionQueueLength,
1616
shakeComputeToPreserve,
17-
shakedatabaseRuntimeRevDep) where
17+
shakedatabaseRuntimeDep) where
1818
import Control.Concurrent.Async (Async)
1919
import Control.Concurrent.STM.Stats (atomically,
2020
readTVarIO)
@@ -23,7 +23,6 @@ import Control.Monad (join)
2323
import Data.Dynamic
2424
import Data.Maybe
2525
import Data.Set (Set)
26-
import qualified Data.Set as Set
2726
import Development.IDE.Graph.Classes ()
2827
import Development.IDE.Graph.Internal.Action
2928
import Development.IDE.Graph.Internal.Database
@@ -33,7 +32,6 @@ import Development.IDE.Graph.Internal.Profile (writeProfile)
3332
import Development.IDE.Graph.Internal.Rules
3433
import Development.IDE.Graph.Internal.Types
3534
import qualified ListT
36-
import qualified StmContainers.Map
3735
import qualified StmContainers.Map as SMap
3836

3937

@@ -47,6 +45,7 @@ shakeNewDatabase :: (String -> IO ()) -> DBQue -> ShakeOptions -> Rules () -> IO
4745
shakeNewDatabase l que opts rules = do
4846
let extra = fromMaybe (toDyn NonExportedType) $ shakeExtra opts
4947
(theRules, actions) <- runRules extra rules
48+
-- give unique names to each action
5049
db <- newDatabase l que extra theRules
5150
pure $ ShakeDatabase (length actions) actions db
5251

@@ -81,9 +80,9 @@ shakeRunDatabaseForKeysSep keysChanged (ShakeDatabase lenAs1 as1 db) as2 = do
8180
incDatabase db keysChanged
8281
return $ drop lenAs1 <$> runActions (newKey "root") db (map unvoid as1 ++ as2)
8382

84-
shakedatabaseRuntimeRevDep :: ShakeDatabase -> IO [(Key, KeySet)]
85-
shakedatabaseRuntimeRevDep (ShakeDatabase _ _ db) =
86-
atomically $ ListT.toList $ SMap.listT (databaseRuntimeRevDep db)
83+
shakedatabaseRuntimeDep :: ShakeDatabase -> IO [(Key, KeySet)]
84+
shakedatabaseRuntimeDep (ShakeDatabase _ _ db) =
85+
atomically $ (ListT.toList . SMap.listT) =<< computeReverseRuntimeMap db
8786

8887

8988
-- shakeComputeToPreserve :: ShakeDatabase -> KeySet -> IO (Set (Async ()))

hls-graph/src/Development/IDE/Graph/Internal/Database.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ newDatabase dataBaseLogger databaseQueue databaseExtra databaseRules = do
5757
databaseThreads <- newTVarIO []
5858
databaseValuesLock <- newTVarIO False
5959
databaseValues <- atomically SMap.new
60-
databaseRuntimeRevDep <- atomically SMap.new
60+
databaseRuntimeDep <- atomically SMap.new
6161
pure Database{..}
6262

6363
-- | Increment the step and mark dirty.
@@ -134,7 +134,7 @@ builderOne parentKey db@Database {..} stack id = do
134134
liftIO $ atomicallyNamed "builder" $ do
135135
-- Spawn the id if needed
136136
dbNotLocked db
137-
insertdatabaseRuntimeRevDep id parentKey db
137+
insertdatabaseRuntimeDep id parentKey db
138138
-- if a build is running, wait
139139
-- it will either be killed or continue
140140
-- depending on wether it is marked as dirty

hls-graph/src/Development/IDE/Graph/Internal/Types.hs

Lines changed: 73 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import Data.Foldable (fold)
2020
import qualified Data.HashMap.Strict as Map
2121
import Data.IORef
2222
import Data.List (intercalate)
23-
import Data.Maybe (fromMaybe, isNothing)
23+
import Data.Maybe (fromMaybe, isJust,
24+
isNothing)
2425
import Data.Set (Set)
2526
import qualified Data.Set as S
2627
import Data.Typeable
@@ -160,31 +161,39 @@ onKeyReverseDeps f it@KeyDetails{..} =
160161

161162
type DBQue = TaskQueue (Either Dynamic (IO ()))
162163
data Database = Database {
163-
databaseExtra :: Dynamic,
164+
databaseExtra :: Dynamic,
164165

165-
databaseThreads :: TVar [(DeliverStatus, Async ())],
166+
databaseThreads :: TVar [(DeliverStatus, Async ())],
166167

167-
databaseRuntimeRevDep :: SMap.Map Key KeySet,
168+
databaseRuntimeDep :: SMap.Map Key KeySet,
168169
-- For each key, the set of keys that depend on it directly.
169170

170171
-- it is used to compute the transitive reverse deps, so
171172
-- if not in any of the transitive reverse deps of a dirty node, it is clean
172173
-- we can skip clean the threads.
173174
-- this is update right before we query the database for the key result.
174-
dataBaseLogger :: String -> IO (),
175+
dataBaseLogger :: String -> IO (),
175176

176-
databaseQueue :: DBQue,
177+
databaseQueue :: DBQue,
177178

178-
databaseRules :: TheRules,
179-
databaseStep :: !(TVar Step),
179+
databaseRules :: TheRules,
180+
databaseStep :: !(TVar Step),
180181

181-
databaseValuesLock :: !(TVar Bool),
182+
databaseValuesLock :: !(TVar Bool),
182183
-- when we restart a build, we set this to False to block any other
183184
-- threads from reading databaseValues
184-
databaseValues :: !(Map Key KeyDetails)
185+
databaseValues :: !(Map Key KeyDetails)
185186

186187
}
187188
---------------------------------------------------------------------
189+
computeReverseRuntimeMap :: Database -> STM (Map Key KeySet)
190+
computeReverseRuntimeMap db = do
191+
-- Create a fresh STM Map and copy the current runtime reverse deps into it.
192+
-- This yields a stable snapshot that won't be mutated by concurrent updates.
193+
m <- SMap.new
194+
pairs <- ListT.toList $ SMap.listT (databaseRuntimeDep db)
195+
forM_ pairs $ \(k, ks) -> SMap.insert ks k m
196+
pure m
188197
-- compute to preserve asyncs
189198
-- only the running stage 2 keys are actually running
190199
-- so we only need to preserve them if they are not affected by the dirty set
@@ -193,34 +202,36 @@ data Database = Database {
193202
-- all non-dirty running need to have an updated step,
194203
-- so it won't be view as dirty when we restart the build
195204
-- computeToPreserve :: Database -> KeySet -> STM ([(Key, Async ())], KeySet, [Key])
196-
computeToPreserve db dirtySet = do
197-
-- All keys that depend (directly or transitively) on any dirty key
198-
affected <- computeTransitiveReverseDeps db dirtySet
199-
allRunings <- getRunningKeys db
200-
let allRuningkeys = map fst allRunings
201-
let running2UnAffected = [ (k ,async) | (k, v) <- allRunings, not (k `memberKeySet` affected), Running _ _ _ (RunningStage2 async) <- [keyStatus v] ]
202-
forM_ allRuningkeys $ \k -> do
203-
-- if not dirty, bump its step
204-
unless (memberKeySet k affected) $ do
205-
SMap.focus
206-
( Focus.alter $ \case
207-
Just kd@KeyDetails {keyStatus = Running {runningStep, runningPrev, runningWait, runningStage}} ->
208-
Just (kd {keyStatus = Running (runningStep + 1) runningPrev runningWait runningStage})
209-
_ -> Nothing
210-
)
211-
k
212-
(databaseValues db)
213-
-- Keep only those whose key is NOT affected by the dirty set
214-
pure ([kv | kv@(k, _async) <- running2UnAffected, not (memberKeySet k affected)], allRuningkeys)
205+
-- computeToPreserve db dirtySet = do
206+
-- -- All keys that depend (directly or transitively) on any dirty key
207+
-- affected <- computeTransitiveReverseDeps db dirtySet
208+
-- allRunings <- getRunningKeys db
209+
-- let allRuningkeys = map fst allRunings
210+
-- let running2UnAffected = [ (k ,async) | (k, v) <- allRunings, not (k `memberKeySet` affected), Running _ _ _ (RunningStage2 async) <- [keyStatus v] ]
211+
-- forM_ allRuningkeys $ \k -> do
212+
-- -- if not dirty, bump its step
213+
-- unless (memberKeySet k affected) $ do
214+
-- SMap.focus
215+
-- ( Focus.alter $ \case
216+
-- Just kd@KeyDetails {keyStatus = Running {runningStep, runningPrev, runningWait, runningStage}} ->
217+
-- Just (kd {keyStatus = Running (runningStep + 1) runningPrev runningWait runningStage})
218+
-- _ -> Nothing
219+
-- )
220+
-- k
221+
-- (databaseValues db)
222+
-- -- Keep only those whose key is NOT affected by the dirty set
223+
-- pure ([kv | kv@(k, _async) <- running2UnAffected, not (memberKeySet k affected)], allRuningkeys)
215224

216225
-- computeToPreserve1 :: Database -> KeySet -> STM ([(Key, Async ())], KeySet, [Key])
226+
computeToPreserve1 :: Database -> KeySet -> STM ([(Key, Async ())], [Key])
217227
computeToPreserve1 db dirtySet = do
218228
-- All keys that depend (directly or transitively) on any dirty key
219229
affected <- computeTransitiveReverseDeps db dirtySet
220230
let rootKey = newKey "root"
221231
threads <- readTVar $ databaseThreads db
222232
-- not root and not effected
223233
let uneffected = [(k, async) | (DeliverStatus _ k, async) <- threads, not (memberKeySet k affected), k /= rootKey]
234+
224235
let allRuningkeys = map (deliverName. fst) threads
225236
forM_ allRuningkeys $ \k -> do
226237
-- if not dirty, bump its step
@@ -236,17 +247,17 @@ computeToPreserve1 db dirtySet = do
236247
-- Keep only those whose key is NOT affected by the dirty set
237248
pure (uneffected, allRuningkeys)
238249

250+
239251
getRunningKeys :: Database -> STM [(Key, KeyDetails)]
240252
getRunningKeys db = do
241253
ListT.toList $ SMap.listT (databaseValues db)
242254

243255
-- compute the transitive reverse dependencies of a set of keys
244-
-- using databaseRuntimeRevDep in the Database
256+
-- using databaseRuntimeDep in the Database
245257
computeTransitiveReverseDeps :: Database -> KeySet -> STM KeySet
246258
computeTransitiveReverseDeps db seeds = do
247-
let rev = databaseRuntimeRevDep db
248-
249-
-- BFS worklist starting from all seed keys.
259+
rev <- computeReverseRuntimeMap db
260+
let -- BFS worklist starting from all seed keys.
250261
-- visited contains everything we've already enqueued (including seeds).
251262
go :: KeySet -> [Key] -> STM KeySet
252263
go visited [] = pure visited
@@ -264,9 +275,13 @@ computeTransitiveReverseDeps db seeds = do
264275
go seeds (toListKeySet seeds)
265276

266277

267-
insertdatabaseRuntimeRevDep :: Key -> Key -> Database -> STM ()
268-
insertdatabaseRuntimeRevDep k pk db = do
269-
SMap.focus (Focus.alter (Just . maybe (singletonKeySet pk) (insertKeySet pk))) k (databaseRuntimeRevDep db)
278+
insertdatabaseRuntimeDep :: Key -> Key -> Database -> STM ()
279+
insertdatabaseRuntimeDep k pk db = do
280+
SMap.focus (Focus.alter (Just . maybe (singletonKeySet k) (insertKeySet k))) pk (databaseRuntimeDep db)
281+
282+
deleteDatabaseRuntimeDep :: Key -> Database -> STM ()
283+
deleteDatabaseRuntimeDep k db = do
284+
SMap.delete k (databaseRuntimeDep db)
270285

271286
---------------------------------------------------------------------
272287

@@ -333,7 +348,8 @@ instance Exception AsyncParentKill where
333348

334349
shutDatabase ::Set (Async ()) -> Database -> IO ()
335350
shutDatabase preserve Database{..} = uninterruptibleMask $ \unmask -> do
336-
-- wait for all threads to finish
351+
-- prune
352+
pruneFinished Database{..}
337353
asyncs <- readTVarIO databaseThreads
338354
step <- readTVarIO databaseStep
339355
tid <- myThreadId
@@ -349,7 +365,8 @@ shutDatabase preserve Database{..} = uninterruptibleMask $ \unmask -> do
349365
-- But if it takes more than 10 seconds, log to stderr
350366
unless (null asyncs) $ do
351367
let warnIfTakingTooLong = unmask $ forever $ do
352-
sleep 5
368+
sleep 10
369+
-- prune finished asyncs to keep the TVar small and report only active ones
353370
as <- readTVarIO databaseThreads
354371
-- poll each async: Nothing => still running
355372
statuses <- forM as $ \(d,a) -> do
@@ -364,6 +381,24 @@ shutDatabase preserve Database{..} = uninterruptibleMask $ \unmask -> do
364381
-- waitForDatabaseRunningKeys :: Database -> IO ()
365382
-- waitForDatabaseRunningKeys = getDatabaseValues >=> mapM_ (waitRunning . snd)
366383

384+
-- | Remove finished asyncs from 'databaseThreads' (non-blocking).
385+
-- Uses 'poll' to check completion without waiting.
386+
pruneFinished :: Database -> IO ()
387+
pruneFinished db@Database{..} = do
388+
threads <- readTVarIO databaseThreads
389+
statuses <- forM threads $ \(d,a) -> do
390+
p <- poll a
391+
return (d,a,p)
392+
let still = [ (d,a) | (d,a,p) <- statuses, isNothing p ]
393+
-- deleteDatabaseRuntimeDep of finished async keys
394+
forM_ statuses $ \(d,_,p) -> when (isJust p) $ do
395+
let k = deliverName d
396+
atomically $ deleteDatabaseRuntimeDep k db
397+
398+
atomically $ modifyTVar' databaseThreads (const still)
399+
400+
401+
367402
getDatabaseValues :: Database -> IO [(Key, Status)]
368403
getDatabaseValues = atomically
369404
. (fmap.fmap) (second keyStatus)

0 commit comments

Comments
 (0)