Skip to content

Commit 1ed5fcb

Browse files
committed
fix old actions runs because we did not increment the step
before releasing the serialized queue
1 parent 51c1ceb commit 1ed5fcb

File tree

7 files changed

+156
-72
lines changed

7 files changed

+156
-72
lines changed

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

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,9 @@ import Development.IDE.Types.Options as Options
129129
import qualified Language.LSP.Protocol.Message as LSP
130130
import qualified Language.LSP.Server as LSP
131131

132-
import Control.Exception (Exception (fromException))
133-
import Data.Either (isLeft, isRight,
134-
lefts)
132+
import Data.Either (isRight, lefts)
133+
import Data.Int (Int64)
134+
import Data.IORef.Extra (atomicModifyIORef'_)
135135
import Development.IDE.Core.Tracing
136136
import Development.IDE.GHC.Compat (NameCache,
137137
NameCacheUpdater,
@@ -142,13 +142,15 @@ import Development.IDE.Graph hiding (ShakeValue,
142142
action)
143143
import qualified Development.IDE.Graph as Shake
144144
import Development.IDE.Graph.Database (ShakeDatabase,
145+
shakeGetActionQueueLength,
145146
shakeGetBuildStep,
146147
shakeGetDatabaseKeys,
147148
shakeNewDatabase,
148149
shakeProfileDatabase,
149150
shakeRunDatabaseForKeys,
151+
shakeRunDatabaseForKeysSep,
150152
shakeShutDatabase)
151-
import Development.IDE.Graph.Internal.Action (runActionInDb)
153+
import Development.IDE.Graph.Internal.Action (runActionInDbCb)
152154
import Development.IDE.Graph.Internal.Database (AsyncParentKill (AsyncParentKill))
153155
import Development.IDE.Graph.Internal.Types (Step (..),
154156
getShakeStep)
@@ -184,15 +186,15 @@ import qualified StmContainers.Map as STM
184186
import System.FilePath hiding (makeRelative)
185187
import System.IO.Unsafe (unsafePerformIO)
186188
import System.Time.Extra
187-
import UnliftIO (MonadUnliftIO (withRunInIO))
188-
import qualified UnliftIO.Exception as UE
189+
import UnliftIO (MonadUnliftIO (withRunInIO),
190+
newIORef, readIORef)
189191

190192

191193

192194
data Log
193195
= LogCreateHieDbExportsMapStart
194196
| LogCreateHieDbExportsMapFinish !Int
195-
| LogBuildSessionRestart !String ![DelayedActionInternal] !KeySet !Seconds !(Maybe FilePath)
197+
| LogBuildSessionRestart !String ![DelayedActionInternal] !KeySet !Seconds !(Maybe FilePath) !Int
196198
| LogBuildSessionRestartTakingTooLong !Seconds
197199
| LogDelayedAction !(DelayedAction ()) !Seconds
198200
| LogBuildSessionFinish !Step !(Either SomeException [Either SomeException ()])
@@ -207,10 +209,13 @@ data Log
207209
-- * OfInterest Log messages
208210
| LogSetFilesOfInterest ![(NormalizedFilePath, FileOfInterestStatus)]
209211
| LogShakeText !T.Text
212+
| LogMonitering !T.Text !Int64
210213
deriving Show
211214

212215
instance Pretty Log where
213216
pretty = \case
217+
LogMonitering name value ->
218+
"Monitoring:" <+> pretty name <+> "value:" <+> pretty value
214219
LogDiagsPublishLog key lastDiags diags ->
215220
vcat
216221
[ "Publishing diagnostics for" <+> pretty (show key)
@@ -222,11 +227,12 @@ instance Pretty Log where
222227
"Initializing exports map from hiedb"
223228
LogCreateHieDbExportsMapFinish exportsMapSize ->
224229
"Done initializing exports map from hiedb. Size:" <+> pretty exportsMapSize
225-
LogBuildSessionRestart reason actionQueue keyBackLog abortDuration shakeProfilePath ->
230+
LogBuildSessionRestart reason actionQueue keyBackLog abortDuration shakeProfilePath step ->
226231
vcat
227232
[ "Restarting build session due to" <+> pretty reason
228233
, "Action Queue:" <+> pretty (map actionName actionQueue)
229234
, "Keys:" <+> pretty (map show $ toListKeySet keyBackLog)
235+
, "Current step:" <+> pretty (show step)
230236
, "Aborting previous build session took" <+> pretty (showDuration abortDuration) <+> pretty shakeProfilePath ]
231237
LogBuildSessionRestartTakingTooLong seconds ->
232238
"Build restart is taking too long (" <> pretty seconds <> " seconds)"
@@ -690,7 +696,7 @@ shakeOpen :: Recorder (WithPriority Log)
690696
shakeOpen recorder lspEnv defaultConfig idePlugins debouncer
691697
shakeProfileDir (IdeReportProgress reportProgress)
692698
ideTesting
693-
withHieDb threadQueue opts monitoring rules rootDir = mdo
699+
withHieDb threadQueue opts argMonitoring rules rootDir = mdo
694700
-- see Note [Serializing runs in separate thread]
695701
let indexQueue = tIndexQueue threadQueue
696702
restartQueue = tRestartQueue threadQueue
@@ -717,12 +723,12 @@ shakeOpen recorder lspEnv defaultConfig idePlugins debouncer
717723
let hiedbWriter = HieDbWriter{..}
718724
exportsMap <- newTVarIO mempty
719725
-- lazily initialize the exports map with the contents of the hiedb
720-
-- TODO: exceptions can be swallowed here?
721-
_ <- async $ do
726+
async <- async $ do
722727
logWith recorder Debug LogCreateHieDbExportsMapStart
723728
em <- createExportsMapHieDb withHieDb
724729
atomically $ modifyTVar' exportsMap (<> em)
725730
logWith recorder Debug $ LogCreateHieDbExportsMapFinish (ExportsMap.size em)
731+
link async
726732

727733
progress <-
728734
if reportProgress
@@ -750,26 +756,46 @@ shakeOpen recorder lspEnv defaultConfig idePlugins debouncer
750756

751757
checkParents <- optCheckParents
752758

759+
760+
logMonitoring <- newLogMonitoring recorder
761+
let monitoring = logMonitoring <> argMonitoring
753762
-- monitoring
754763
let readValuesCounter = fromIntegral . countRelevantKeys checkParents <$> getStateKeys shakeExtras
755764
readDirtyKeys = fromIntegral . countRelevantKeys checkParents . toListKeySet <$> readTVarIO(dirtyKeys shakeExtras)
756765
readIndexPending = fromIntegral . HMap.size <$> readTVarIO (indexPending $ hiedbWriter shakeExtras)
757766
readExportsMap = fromIntegral . ExportsMap.exportsMapSize <$> readTVarIO (exportsMap shakeExtras)
758767
readDatabaseCount = fromIntegral . countRelevantKeys checkParents . map fst <$> shakeGetDatabaseKeys shakeDb
759768
readDatabaseStep = fromIntegral <$> shakeGetBuildStep shakeDb
769+
readDatabaseActionQueueCount = fromIntegral <$> shakeGetActionQueueLength shakeDb
760770

761771
registerGauge monitoring "ghcide.values_count" readValuesCounter
762772
registerGauge monitoring "ghcide.dirty_keys_count" readDirtyKeys
763773
registerGauge monitoring "ghcide.indexing_pending_count" readIndexPending
764774
registerGauge monitoring "ghcide.exports_map_count" readExportsMap
765775
registerGauge monitoring "ghcide.database_count" readDatabaseCount
766776
registerCounter monitoring "ghcide.num_builds" readDatabaseStep
777+
registerCounter monitoring "ghcide.database_action_queue_count" readDatabaseActionQueueCount
767778

768779
stopMonitoring <- start monitoring
769780

770781
let ideState = IdeState{..}
771782
return ideState
772-
783+
newLogMonitoring :: MonadIO m => Recorder (WithPriority Log) -> m Monitoring
784+
newLogMonitoring logger = do
785+
actions <- newIORef []
786+
let registerCounter name readA = do
787+
let update = do
788+
val <- readA
789+
logWith logger Info $ LogMonitering name (fromIntegral val)
790+
atomicModifyIORef'_ actions (update :)
791+
registerGauge = registerCounter
792+
let start = do
793+
a <- regularly 10 $ sequence_ =<< readIORef actions
794+
return (cancel a)
795+
return Monitoring{..}
796+
where
797+
regularly :: Seconds -> IO () -> IO (Async ())
798+
regularly delay act = async $ forever (act >> sleep delay)
773799

774800
getStateKeys :: ShakeExtras -> IO [Key]
775801
getStateKeys = (fmap.fmap) fst . atomically . ListT.toList . STM.listT . stateValues
@@ -837,7 +863,8 @@ shakeRestart recorder IdeState{..} vfs reason acts ioActionBetweenShakeSession =
837863
res <- shakeDatabaseProfile shakeDb
838864
backlog <- readTVarIO $ dirtyKeys shakeExtras
839865
-- this log is required by tests
840-
logWith recorder Debug $ LogBuildSessionRestart reason queue backlog stopTime res
866+
step <- shakeGetBuildStep shakeDb
867+
logWith recorder Debug $ LogBuildSessionRestart reason queue backlog stopTime res step
841868
)
842869
-- It is crucial to be masked here, otherwise we can get killed
843870
-- between spawning the new thread and updating shakeSession.
@@ -859,12 +886,13 @@ shakeEnqueue :: ShakeExtras -> DelayedAction a -> IO (IO a)
859886
shakeEnqueue ShakeExtras{actionQueue, shakeRecorder} act = do
860887
(b, dai) <- instantiateDelayedAction act
861888
atomicallyNamed "actionQueue - push" $ pushQueue dai actionQueue
889+
logWith shakeRecorder Debug $ LogShakeText (T.pack $ "Enqueued action: " <> actionName act)
862890
let wait' barrier =
863891
waitBarrier barrier `catches`
864892
[ Handler(\BlockedIndefinitelyOnMVar ->
865893
fail $ "internal bug: forever blocked on MVar for " <>
866894
actionName act)
867-
, Handler (\e@AsyncCancelled -> do
895+
, Handler (\e@(SomeAsyncException _) -> do
868896
logWith shakeRecorder Debug $ LogCancelledAction (T.pack $ actionName act)
869897

870898
atomicallyNamed "actionQueue - abort" $ abortQueue dai actionQueue
@@ -892,6 +920,7 @@ newSession recorder extras@ShakeExtras{..} vfsMod shakeDb acts reason = do
892920
VFSModified vfs -> atomically $ writeTVar vfsVar vfs
893921

894922
IdeOptions{optRunSubset} <- getIdeOptionsIO extras
923+
895924
reenqueued <- atomicallyNamed "actionQueue - peek" $ peekInProgress actionQueue
896925
step <- getShakeStep shakeDb
897926
allPendingKeys <-
@@ -907,37 +936,39 @@ newSession recorder extras@ShakeExtras{..} vfsMod shakeDb acts reason = do
907936
Left e -> logWith recorder Error $ LogShakeText (T.pack $ label ++ " failed: " ++ show e)
908937
Right r -> logWith recorder Debug $ LogShakeText (T.pack $ label ++ " finished: " ++ show r)
909938
pumpActionThread = do
910-
d <- liftIO $ atomicallyNamed "action queue - pop" $ popQueue actionQueue
911-
r <- runActionInDb [run d]
912-
liftIO $ logResult "pumpActionThread" r
939+
logWith recorder Debug $ LogShakeText (T.pack $ "Starting action" ++ "(step: " <> show step)
940+
d <- runActionInDbCb actionName run (popQueue actionQueue) (logResult "pumpActionThread" . return)
941+
step <- getShakeStep shakeDb
942+
logWith recorder Debug $ LogShakeText (T.pack $ "started action" ++ "(step: " <> show step <> "): " <> actionName d)
913943
pumpActionThread
914944

915945
-- TODO figure out how to thread the otSpan into defineEarlyCutoff
916-
run d = do
946+
run d = do
917947
start <- liftIO offsetTime
918948
getAction d
919949
liftIO $ atomicallyNamed "actionQueue - done" $ doneQueue d actionQueue
920950
runTime <- liftIO start
921951
logWith recorder (actionPriority d) $ LogDelayedAction d runTime
922952

923953
-- The inferred type signature doesn't work in ghc >= 9.0.1
924-
workRun :: (forall b. IO b -> IO b) -> IO ()
925-
workRun restore = withSpan "Shake session" $ \otSpan -> do
954+
-- workRun :: (forall b. IO b -> IO b) -> IO ()
955+
workRun start restore = withSpan "Shake session" $ \otSpan -> do
926956
setTag otSpan "reason" (fromString reason)
927957
setTag otSpan "queue" (fromString $ unlines $ map actionName reenqueued)
928958
whenJust allPendingKeys $ \kk -> setTag otSpan "keys" (BS8.pack $ unlines $ map show $ toListKeySet kk)
929-
let keysActs = pumpActionThread : map run (reenqueued ++ acts)
930-
res <- try @SomeException $
931-
restore $ shakeRunDatabaseForKeys (toListKeySet <$> allPendingKeys) shakeDb keysActs
932-
logWith recorder Debug $ LogBuildSessionFinish step $ res
959+
res <- try @SomeException $ restore start
960+
logWith recorder Debug $ LogBuildSessionFinish step res
933961

962+
963+
let keysActs = pumpActionThread : map run (reenqueued ++ acts)
964+
-- first we increase the step, so any actions started from here on
965+
start <- shakeRunDatabaseForKeysSep (toListKeySet <$> allPendingKeys) shakeDb keysActs
934966
-- Do the work in a background thread
935967
parentTid <- myThreadId
936968
workThread <- asyncWithUnmask $ \x -> do
937969
childThreadId <- myThreadId
938970
logWith recorder Info $ LogShakeText ("Starting shake thread: " <> T.pack (show childThreadId) <> " (parent: " <> T.pack (show parentTid) <> ")")
939-
workRun x
940-
971+
workRun start x
941972

942973
-- Cancelling is required to flush the Shake database when either
943974
-- the filesystem or the Ghc configuration have changed
@@ -949,7 +980,7 @@ newSession recorder extras@ShakeExtras{..} vfsMod shakeDb acts reason = do
949980
cancelWith workThread $ AsyncParentKill tid step
950981
shakeShutDatabase shakeDb
951982

952-
983+
-- should wait until the step has increased
953984
pure (ShakeSession{..})
954985

955986
instantiateDelayedAction

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

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@ module Development.IDE.Graph.Database(
44
shakeNewDatabase,
55
shakeRunDatabase,
66
shakeRunDatabaseForKeys,
7+
shakeRunDatabaseForKeysSep,
78
shakeProfileDatabase,
89
shakeGetBuildStep,
910
shakeGetDatabaseKeys,
1011
shakeGetDirtySet,
1112
shakeGetCleanKeys
1213
,shakeGetBuildEdges,
13-
shakeShutDatabase) where
14-
import Control.Concurrent.STM.Stats (readTVarIO)
14+
shakeShutDatabase,
15+
shakeGetActionQueueLength) where
16+
import Control.Concurrent.STM.Stats (atomically,
17+
readTVarIO)
1518
import Control.Exception (SomeException)
19+
import Control.Monad (join)
1620
import Data.Dynamic
1721
import Data.Maybe
1822
import Development.IDE.Graph.Classes ()
@@ -40,7 +44,7 @@ shakeNewDatabase que opts rules = do
4044
pure $ ShakeDatabase (length actions) actions db
4145

4246
shakeRunDatabase :: ShakeDatabase -> [Action a] -> IO [Either SomeException a]
43-
shakeRunDatabase = shakeRunDatabaseForKeys Nothing
47+
shakeRunDatabase s xs = shakeRunDatabaseForKeys Nothing s xs
4448

4549
-- | Returns the set of dirty keys annotated with their age (in # of builds)
4650
shakeGetDirtySet :: ShakeDatabase -> IO [(Key, Int)]
@@ -58,15 +62,26 @@ unvoid :: Functor m => m () -> m a
5862
unvoid = fmap undefined
5963

6064
-- | Assumes that the database is not running a build
61-
shakeRunDatabaseForKeys
65+
-- The nested IO is to
66+
-- seperate incrementing the step from running the build
67+
shakeRunDatabaseForKeysSep
6268
:: Maybe [Key]
6369
-- ^ Set of keys changed since last run. 'Nothing' means everything has changed
6470
-> ShakeDatabase
6571
-> [Action a]
66-
-> IO [Either SomeException a]
67-
shakeRunDatabaseForKeys keysChanged (ShakeDatabase lenAs1 as1 db) as2 = do
72+
-> IO (IO [Either SomeException a])
73+
shakeRunDatabaseForKeysSep keysChanged (ShakeDatabase lenAs1 as1 db) as2 = do
6874
incDatabase db keysChanged
69-
drop lenAs1 <$> runActions db (map unvoid as1 ++ as2)
75+
return $ drop lenAs1 <$> runActions db (map unvoid as1 ++ as2)
76+
77+
shakeRunDatabaseForKeys
78+
:: Maybe [Key]
79+
-- ^ Set of keys changed since last run. 'Nothing' means everything has changed
80+
-> ShakeDatabase
81+
-> [Action a]
82+
-> (IO [Either SomeException a])
83+
shakeRunDatabaseForKeys keysChanged sdb as2 = join $ shakeRunDatabaseForKeysSep keysChanged sdb as2
84+
7085

7186

7287
-- | Given a 'ShakeDatabase', write an HTML profile to the given file about the latest run.
@@ -90,3 +105,7 @@ shakeGetBuildEdges (ShakeDatabase _ _ db) = do
90105
-- annotated with how long ago (in # builds) they were visited
91106
shakeGetDatabaseKeys :: ShakeDatabase -> IO [(Key, Int)]
92107
shakeGetDatabaseKeys (ShakeDatabase _ _ db) = getKeysAndVisitAge db
108+
109+
shakeGetActionQueueLength :: ShakeDatabase -> IO Int
110+
shakeGetActionQueueLength (ShakeDatabase _ _ db) =
111+
atomically $ databaseGetActionQueueLength db

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ module Development.IDE.Graph.Internal.Action
1313
, runActions
1414
, Development.IDE.Graph.Internal.Action.getDirtySet
1515
, getKeysAndVisitedAge
16-
, runActionInDb
16+
, runActionInDbCb
1717
) where
1818

1919
import Control.Concurrent.Async
20+
import Control.Concurrent.STM.Stats (atomicallyNamed)
2021
import Control.DeepSeq (force)
2122
import Control.Exception
2223
import Control.Monad.IO.Class
@@ -31,7 +32,7 @@ import Development.IDE.Graph.Internal.Key
3132
import Development.IDE.Graph.Internal.Rules (RuleResult)
3233
import Development.IDE.Graph.Internal.Types
3334
import System.Exit
34-
import UnliftIO (atomically,
35+
import UnliftIO (STM, atomically,
3536
newEmptyTMVarIO,
3637
putTMVar, readTMVar)
3738

@@ -48,23 +49,32 @@ parallel [] = return []
4849
parallel xs = do
4950
a <- Action ask
5051
deps <- liftIO $ readIORef $ actionDeps a
51-
5252
case deps of
5353
UnknownDeps ->
5454
-- if we are already in the rerun mode, nothing we do is going to impact our state
55-
runActionInDb xs
55+
runActionInDb "parallel" xs
5656
deps -> error $ "parallel not supported when we have precise dependencies: " ++ show deps
5757
-- (newDeps, res) <- liftIO $ unzip <$> runActionInDb usingState xs
5858
-- liftIO $ writeIORef (actionDeps a) $ mconcat $ deps : newDeps
5959
-- return ()
6060

61-
runActionInDb :: [Action a] -> Action [Either SomeException a]
62-
runActionInDb acts = do
61+
-- non-blocking version of runActionInDb
62+
runActionInDbCb :: (a -> String) -> (a -> Action result) -> STM a -> (Either SomeException result -> IO ()) -> Action a
63+
runActionInDbCb getTitle work getAct handler = do
64+
a <- Action ask
65+
liftIO $ atomicallyNamed "action queue - pop" $ do
66+
act <- getAct
67+
runInDataBase (getTitle act) (actionDatabase a) [(ignoreState a $ work act, handler)]
68+
return act
69+
70+
runActionInDb :: String -> [Action a] -> Action [Either SomeException a]
71+
runActionInDb title acts = do
6372
a <- Action ask
6473
xs <- mapM (\x -> do
6574
barrier <- newEmptyTMVarIO
6675
return (x, barrier)) acts
67-
liftIO $ atomically $ runInDataBase (actionDatabase a) (map (\(x, b) -> (ignoreState a x >>= (atomically . putTMVar b . Right), atomically . putTMVar b . Left)) xs)
76+
liftIO $ atomically $ runInDataBase title (actionDatabase a)
77+
(map (\(x, b) -> (ignoreState a x, atomically . putTMVar b)) xs)
6878
results <- liftIO $ mapM (atomically . readTMVar) $ fmap snd xs
6979
return results
7080

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ builderOne db@Database {..} stack id = do
123123
Dirty s -> do
124124
SMap.focus (updateStatus $ Running current s) id databaseValues
125125
traceEvent ("Starting build of key: " ++ show id ++ ", step "++ show current)
126-
$ runOneInDataBase db (refresh db stack id s) $ \e -> atomically $ SMap.focus (updateStatus $ Exception current e s) id databaseValues
126+
$ runOneInDataBase (show id) db (refresh db stack id s) $ \e -> atomically $ SMap.focus (updateStatus $ Exception current e s) id databaseValues
127127
return Nothing
128128
Clean r -> return $ Just r
129129
-- force here might contains async exceptions from previous runs

0 commit comments

Comments
 (0)