@@ -25,6 +25,8 @@ module Development.IDE.Core.Shake(
2525 IdeState , shakeSessionInit , shakeExtras , shakeDb , rootDir ,
2626 ShakeExtras (.. ), getShakeExtras , getShakeExtrasRules ,
2727 KnownTargets (.. ), Target (.. ), toKnownFiles , unionKnownTargets , mkKnownTargets ,
28+ ShakeRestartArgs (.. ),
29+ shakeRestart ,
2830 IdeRule , IdeResult , RestartQueue ,
2931 GetModificationTime (GetModificationTime , GetModificationTime_ , missingFileDiagnostics ),
3032 shakeOpen , shakeShut ,
@@ -76,7 +78,7 @@ module Development.IDE.Core.Shake(
7678 Log (.. ),
7779 VFSModified (.. ), getClientConfigAction ,
7880 ThreadQueue (.. ),
79- runWithSignal
81+ runWithSignal , runRestartTask , runRestartTaskDync , dynShakeRestart
8082 ) where
8183
8284import Control.Concurrent.Async
@@ -107,8 +109,7 @@ import Data.Hashable
107109import qualified Data.HashMap.Strict as HMap
108110import Data.HashSet (HashSet )
109111import qualified Data.HashSet as HSet
110- import Data.List.Extra (foldl' , partition ,
111- takeEnd )
112+ import Data.List.Extra (partition , takeEnd )
112113import qualified Data.Map.Strict as Map
113114import Data.Maybe
114115import qualified Data.SortedList as SL
@@ -152,7 +153,7 @@ import Development.IDE.Graph.Database (ShakeDatabase,
152153 shakeShutDatabase )
153154import Development.IDE.Graph.Internal.Action (runActionInDbCb )
154155import Development.IDE.Graph.Internal.Database (AsyncParentKill (AsyncParentKill ))
155- import Development.IDE.Graph.Internal.Types (Step (.. ),
156+ import Development.IDE.Graph.Internal.Types (DBQue , Step (.. ),
156157 getShakeStep )
157158import Development.IDE.Graph.Rule
158159import Development.IDE.Types.Action
@@ -194,7 +195,7 @@ import UnliftIO (MonadUnliftIO (withRun
194195data Log
195196 = LogCreateHieDbExportsMapStart
196197 | LogCreateHieDbExportsMapFinish ! Int
197- | LogBuildSessionRestart ! String ! [DelayedActionInternal ] ! KeySet ! Seconds ! (Maybe FilePath ) ! Int
198+ | LogBuildSessionRestart ! ShakeRestartArgs ! [DelayedActionInternal ] ! KeySet ! Seconds ! (Maybe FilePath ) ! Int
198199 | LogBuildSessionRestartTakingTooLong ! Seconds
199200 | LogDelayedAction ! (DelayedAction () ) ! Seconds
200201 | LogBuildSessionFinish ! Step ! (Either SomeException [Either SomeException () ])
@@ -227,9 +228,10 @@ instance Pretty Log where
227228 " Initializing exports map from hiedb"
228229 LogCreateHieDbExportsMapFinish exportsMapSize ->
229230 " Done initializing exports map from hiedb. Size:" <+> pretty exportsMapSize
230- LogBuildSessionRestart reason actionQueue keyBackLog abortDuration shakeProfilePath step ->
231+ LogBuildSessionRestart restartArgs actionQueue keyBackLog abortDuration shakeProfilePath step ->
231232 vcat
232- [ " Restarting build session due to" <+> pretty reason
233+ [ " Restarting build session due to" <+> pretty (sraReason restartArgs)
234+ , " Restarts num:" <+> pretty (sraCount $ restartArgs)
233235 , " Action Queue:" <+> pretty (map actionName actionQueue)
234236 , " Keys:" <+> pretty (map show $ toListKeySet keyBackLog)
235237 , " Current step:" <+> pretty (show step)
@@ -287,7 +289,9 @@ data HieDbWriter
287289-- The inner `(HieDb -> IO ()) -> IO ()` wraps `HieDb -> IO ()`
288290-- with (currently) retry functionality
289291type IndexQueue = TaskQueue (((HieDb -> IO () ) -> IO () ) -> IO () )
290- type RestartQueue = TaskQueue (IO () )
292+ -- type RestartQueue = TaskQueue ShakeRestartArgs
293+ type ShakeQueue = DBQue
294+ type RestartQueue = ShakeQueue
291295type LoaderQueue = TaskQueue (IO () )
292296
293297
@@ -716,7 +720,7 @@ shakeOpen recorder lspEnv defaultConfig idePlugins debouncer
716720 semanticTokensCache <- STM. newIO
717721 positionMapping <- STM. newIO
718722 knownTargetsVar <- newTVarIO $ hashed emptyKnownTargets
719- let restartShakeSession = shakeRestart recorder ideState
723+ let restartShakeSession = shakeRestart restartQueue
720724 persistentKeys <- newTVarIO mempty
721725 indexPending <- newTVarIO HMap. empty
722726 indexCompleted <- newTVarIO 0
@@ -761,11 +765,11 @@ shakeOpen recorder lspEnv defaultConfig idePlugins debouncer
761765 checkParents <- optCheckParents
762766
763767
764- logMonitoring <- newLogMonitoring recorder
765- let monitoring = logMonitoring <> argMonitoring
768+ -- logMonitoring <- newLogMonitoring recorder
769+ let monitoring = argMonitoring
766770 -- monitoring
767771 let readValuesCounter = fromIntegral . countRelevantKeys checkParents <$> getStateKeys shakeExtras
768- readDirtyKeys = fromIntegral . countRelevantKeys checkParents . toListKeySet <$> readTVarIO(dirtyKeys shakeExtras)
772+ readDirtyKeys = fromIntegral . countRelevantKeys checkParents . toListKeySet <$> readTVarIO (dirtyKeys shakeExtras)
769773 readIndexPending = fromIntegral . HMap. size <$> readTVarIO (indexPending $ hiedbWriter shakeExtras)
770774 readExportsMap = fromIntegral . ExportsMap. exportsMapSize <$> readTVarIO (exportsMap shakeExtras)
771775 readDatabaseCount = fromIntegral . countRelevantKeys checkParents . map fst <$> shakeGetDatabaseKeys shakeDb
@@ -784,6 +788,7 @@ shakeOpen recorder lspEnv defaultConfig idePlugins debouncer
784788
785789 let ideState = IdeState {.. }
786790 return ideState
791+
787792newLogMonitoring :: MonadIO m => Recorder (WithPriority Log ) -> m Monitoring
788793newLogMonitoring logger = do
789794 actions <- newIORef []
@@ -849,37 +854,102 @@ delayedAction a = do
849854 liftIO $ shakeEnqueue extras a
850855
851856
857+ data ShakeRestartArgs = ShakeRestartArgs
858+ { sraVfs :: ! VFSModified
859+ , sraReason :: ! String
860+ , sraActions :: ! [DelayedAction () ]
861+ , sraBetweenSessions :: IO [Key ]
862+ , sraReStartQueue :: ! RestartQueue
863+ , sraCount :: ! Int
864+ , sraWaitMVars :: ! [MVar () ]
865+ -- ^ Just for debugging, how many restarts have been requested so far
866+ }
867+
868+ instance Show ShakeRestartArgs where
869+ show ShakeRestartArgs {.. } =
870+ " ShakeRestartArgs { sraReason = " ++ show sraReason
871+ ++ " , sraActions = " ++ show (map actionName sraActions)
872+ ++ " , sraCount = " ++ show sraCount
873+ ++ " }"
874+
875+ instance Semigroup ShakeRestartArgs where
876+ a <> b = ShakeRestartArgs
877+ { sraVfs = sraVfs a <> sraVfs b
878+ , sraReason = sraReason a ++ " ; " ++ sraReason b
879+ , sraActions = sraActions a ++ sraActions b
880+ , sraBetweenSessions = (++) <$> sraBetweenSessions a <*> sraBetweenSessions b
881+ , sraReStartQueue = sraReStartQueue a
882+ , sraCount = sraCount a + sraCount b
883+ , sraWaitMVars = sraWaitMVars a ++ sraWaitMVars b
884+ }
885+
852886-- | Restart the current 'ShakeSession' with the given system actions.
853887-- Any actions running in the current session will be aborted,
854888-- but actions added via 'shakeEnqueue' will be requeued.
855- shakeRestart :: Recorder (WithPriority Log ) -> IdeState -> VFSModified -> String -> [DelayedAction () ] -> IO [Key ] -> IO ()
856- shakeRestart recorder IdeState {.. } vfs reason acts ioActionBetweenShakeSession =
857- void $ awaitRunInThreadAtHead (restartQueue shakeExtras) $ do
858- withMVar'
859- shakeSession
860- (\ runner -> do
861- (stopTime,() ) <- duration $ logErrorAfter 10 $ cancelShakeSession runner
862- queue <- atomicallyNamed " actionQueue - peek" $ peekInProgress $ actionQueue shakeExtras
863- keys <- ioActionBetweenShakeSession
864- -- it is every important to update the dirty keys after we enter the critical section
865- -- see Note [Housekeeping rule cache and dirty key outside of hls-graph]
866- atomically $ modifyTVar' (dirtyKeys shakeExtras) $ \ x -> foldl' (flip insertKeySet) x keys
867- res <- shakeDatabaseProfile shakeDb
868- backlog <- readTVarIO $ dirtyKeys shakeExtras
869- -- this log is required by tests
870- step <- shakeGetBuildStep shakeDb
871- logWith recorder Debug $ LogBuildSessionRestart reason queue backlog stopTime res step
872- )
873- -- It is crucial to be masked here, otherwise we can get killed
874- -- between spawning the new thread and updating shakeSession.
875- -- See https://github.com/haskell/ghcide/issues/79
876- (\ () -> do
877- (,() ) <$> newSession recorder shakeExtras vfs shakeDb acts reason)
878- where
879- logErrorAfter :: Seconds -> IO () -> IO ()
880- logErrorAfter seconds action = flip withAsync (const action) $ do
881- sleep seconds
882- logWith recorder Error (LogBuildSessionRestartTakingTooLong seconds)
889+ shakeRestart :: RestartQueue -> VFSModified -> String -> [DelayedAction () ] -> IO [Key ] -> IO ()
890+ shakeRestart rts vfs reason acts ioActionBetweenShakeSession = do
891+ waitMVar <- newEmptyMVar
892+ void $ submitWork rts $ Left $
893+ toDyn $ ShakeRestartArgs vfs reason acts ioActionBetweenShakeSession rts 1 [waitMVar]
894+ -- Wait until the restart is done
895+ takeMVar waitMVar
896+
897+ dynShakeRestart :: Dynamic -> ShakeRestartArgs
898+ dynShakeRestart dy = case fromDynamic dy of
899+ Just shakeRestartArgs -> shakeRestartArgs
900+ Nothing -> error " Internal error, dynShakeRestart, got invalid dynamic type"
901+
902+ -- runRestartTask :: Recorder (WithPriority Log) -> IdeState -> VFSModified -> String -> [DelayedAction ()] -> IO [Key] -> IO ()
903+ -- runRestartTask recorder IdeState{..} vfs reason acts ioActionBetweenShakeSession =
904+ runRestartTaskDync :: Recorder (WithPriority Log ) -> MVar IdeState -> Dynamic -> IO ()
905+ runRestartTaskDync recorder ideStateVar dy = runRestartTask recorder ideStateVar (dynShakeRestart dy)
906+
907+ runRestartTask :: Recorder (WithPriority Log ) -> MVar IdeState -> ShakeRestartArgs -> IO ()
908+ runRestartTask recorder ideStateVar shakeRestartArgs = do
909+ IdeState {shakeDb, shakeSession, shakeExtras, shakeDatabaseProfile} <- readMVar ideStateVar
910+ let prepareRestart sra@ ShakeRestartArgs {.. } = do
911+ keys <- sraBetweenSessions
912+ -- it is every important to update the dirty keys after we enter the critical section
913+ -- see Note [Housekeeping rule cache and dirty key outside of hls-graph]
914+ atomically $ modifyTVar' (dirtyKeys shakeExtras) $ \ x -> foldl' (flip insertKeySet) x keys
915+ -- Check if there is another restart request pending, if so, we run that one too
916+ readAndGo sra sraReStartQueue
917+ readAndGo sra sraReStartQueue = do
918+ nextRestartArg <- atomically $ tryReadTaskQueue sraReStartQueue
919+ case nextRestartArg of
920+ Nothing -> return sra
921+ Just (Left dy) -> do
922+ res <- prepareRestart $ dynShakeRestart dy
923+ return $ sra <> res
924+ Just (Right _) -> readAndGo sra sraReStartQueue
925+ withMVar'
926+ shakeSession
927+ ( \ runner -> do
928+ -- takeShakeLock shakeDb
929+ (stopTime, () ) <- duration $ logErrorAfter 10 $ cancelShakeSession runner
930+ restartArgs <- prepareRestart shakeRestartArgs
931+ queue <- atomicallyNamed " actionQueue - peek" $ peekInProgress $ actionQueue shakeExtras
932+ res <- shakeDatabaseProfile shakeDb
933+ backlog <- readTVarIO $ dirtyKeys shakeExtras
934+ -- this log is required by tests
935+ step <- shakeGetBuildStep shakeDb
936+ logWith recorder Info $ LogBuildSessionRestart restartArgs queue backlog stopTime res step
937+ return restartArgs
938+ )
939+ -- It is crucial to be masked here, otherwise we can get killed
940+ -- between spawning the new thread and updating shakeSession.
941+ -- See https://github.com/haskell/ghcide/issues/79
942+ ( \ (ShakeRestartArgs {.. }) ->
943+ do
944+ (,() ) <$> newSession recorder shakeExtras sraVfs shakeDb sraActions sraReason
945+ `finally` for_ sraWaitMVars (`putMVar` () )
946+ )
947+ where
948+ logErrorAfter :: Seconds -> IO () -> IO ()
949+ logErrorAfter seconds action = flip withAsync (const action) $ do
950+ sleep seconds
951+ logWith recorder Error (LogBuildSessionRestartTakingTooLong seconds)
952+
883953
884954-- | Enqueue an action in the existing 'ShakeSession'.
885955-- Returns a computation to block until the action is run, propagating exceptions.
@@ -893,7 +963,7 @@ shakeEnqueue ShakeExtras{actionQueue, shakeRecorder} act = do
893963 logWith shakeRecorder Debug $ LogShakeText (T. pack $ " Enqueued action: " <> actionName act)
894964 let wait' barrier =
895965 waitBarrier barrier `catches`
896- [ Handler (\ BlockedIndefinitelyOnMVar ->
966+ [ Handler (\ BlockedIndefinitelyOnMVar ->
897967 fail $ " internal bug: forever blocked on MVar for " <>
898968 actionName act)
899969 , Handler (\ e@ (SomeAsyncException _) -> do
@@ -906,6 +976,10 @@ shakeEnqueue ShakeExtras{actionQueue, shakeRecorder} act = do
906976
907977data VFSModified = VFSUnmodified | VFSModified ! VFS
908978
979+ instance Semigroup VFSModified where
980+ x <> VFSUnmodified = x
981+ _ <> x = x
982+
909983-- | Set up a new 'ShakeSession' with a set of initial actions
910984-- Will crash if there is an existing 'ShakeSession' running.
911985newSession
@@ -1049,7 +1123,7 @@ garbageCollectKeys label maxAge checkParents agedKeys = do
10491123 removeDirtyKey dk values st@ (! counter, keys) (k, age)
10501124 | age > maxAge
10511125 , Just (kt,_) <- fromKeyType k
1052- , not (kt `HSet.member` preservedKeys checkParents)
1126+ , not (kt `HSet.member` preservedKeys checkParents)
10531127 = atomicallyNamed " GC" $ do
10541128 gotIt <- STM. focus (Focus. member <* Focus. delete) k values
10551129 when gotIt $
@@ -1424,12 +1498,12 @@ updateFileDiagnostics :: MonadIO m
14241498 -> [FileDiagnostic ] -- ^ current results
14251499 -> m ()
14261500updateFileDiagnostics recorder fp ver k ShakeExtras {diagnostics, hiddenDiagnostics, publishedDiagnostics, debouncer, lspEnv, ideTesting} current0 = do
1427- liftIO $ withTrace (" update diagnostics " <> fromString(fromNormalizedFilePath fp)) $ \ addTag -> do
1501+ liftIO $ withTrace (" update diagnostics " <> fromString (fromNormalizedFilePath fp)) $ \ addTag -> do
14281502 addTag " key" (show k)
14291503 let (currentShown, currentHidden) = partition ((== ShowDiag ) . fdShouldShowDiagnostic) current
14301504 uri = filePathToUri' fp
14311505 addTagUnsafe :: String -> String -> String -> a -> a
1432- addTagUnsafe msg t x v = unsafePerformIO(addTag (msg <> t) x) `seq` v
1506+ addTagUnsafe msg t x v = unsafePerformIO (addTag (msg <> t) x) `seq` v
14331507 update :: (forall a . String -> String -> a -> a ) -> [FileDiagnostic ] -> STMDiagnosticStore -> STM [FileDiagnostic ]
14341508 update addTagUnsafeMethod new store = addTagUnsafeMethod " count" (show $ Prelude. length new) $ setStageDiagnostics addTagUnsafeMethod uri ver (renderKey k) new store
14351509 current = map (fdLspDiagnosticL %~ diagsFromRule) current0
@@ -1556,3 +1630,4 @@ runWithSignal msgStart msgEnd files rule = do
15561630 kickSignal testing lspEnv files msgStart
15571631 void $ uses rule files
15581632 kickSignal testing lspEnv files msgEnd
1633+
0 commit comments