@@ -819,11 +819,11 @@ delayedAction a = do
819819 liftIO $ shakeEnqueue extras a
820820
821821data PendingRestart = PendingRestart
822- { pendingRestartVFS :: VFSModified
823- , pendingRestartActionBetweenSessions :: IO [Key ]
824- , pendingRestartReasons :: [T. Text ]
825- , pendingRestartActions :: [DelayedActionInternal ]
826- , pendingRestartDoneSignals :: [TMVar () ]
822+ { pendingRestartVFS :: ! VFSModified
823+ , pendingRestartActionBetweenSessions :: ! [ IO [Key ] ]
824+ , pendingRestartReasons :: ! [T. Text ]
825+ , pendingRestartActions :: ! [DelayedActionInternal ]
826+ , pendingRestartDoneSignals :: ! [TMVar () ]
827827 }
828828
829829newestVFSModified :: VFSModified -> VFSModified -> VFSModified
@@ -834,34 +834,51 @@ mergePendingRestart :: PendingRestart -> Maybe PendingRestart -> PendingRestart
834834mergePendingRestart new Nothing = new
835835mergePendingRestart new (Just old) = PendingRestart
836836 { pendingRestartVFS = newestVFSModified (pendingRestartVFS new) (pendingRestartVFS old)
837- , pendingRestartReasons = pendingRestartReasons new <> pendingRestartReasons old
838- -- TODO: Contains a quadratic list append on the number of accumulated shake restarts.
839- , pendingRestartActions = pendingRestartActions old <> pendingRestartActions new
840- , pendingRestartActionBetweenSessions = pendingRestartActionBetweenSessions old <> pendingRestartActionBetweenSessions new
841- , pendingRestartDoneSignals = pendingRestartDoneSignals new <> pendingRestartDoneSignals old }
837+ , pendingRestartReasons = pendingRestartReasons new ++ pendingRestartReasons old
838+ , pendingRestartActions = pendingRestartActions new ++ pendingRestartActions old
839+ , pendingRestartActionBetweenSessions = pendingRestartActionBetweenSessions new ++ pendingRestartActionBetweenSessions old
840+ , pendingRestartDoneSignals = pendingRestartDoneSignals new ++ pendingRestartDoneSignals old
841+ }
842842
843843data RestartSlot = RestartSlot
844- { queuedRestart :: IORef (Maybe (PendingRestart ))
845- , restartSignal :: MVar ()
844+ { queuedRestart :: IORef (Maybe PendingRestart )
845+ , restartSignal :: MVar ()
846+ , lastRestartBarrier :: TVar (TMVar () )
847+ -- ^ A barrier that is filled when the most recent shake restart completes.
848+ --
849+ -- Each call to 'shakeRestart' replaces this with a fresh empty TMVar. The
850+ -- restart worker fills it when the restart finishes. Dependents on the
851+ -- restart can then wait on this.
846852 }
847853
848854newRestartSlot :: IO RestartSlot
849- newRestartSlot = RestartSlot <$> newIORef Nothing <*> newEmptyMVar
855+ newRestartSlot = do
856+ initialBarrier <- newTMVarIO () -- starts filled (no pending restart)
857+ RestartSlot <$> newIORef Nothing <*> newEmptyMVar <*> newTVarIO initialBarrier
850858
851859-- | Restart the current 'ShakeSession' with the given system actions.
852- -- Any actions running in the current session will be aborted,
853- -- but actions added via 'shakeEnqueue' will be requeued.
860+ --
861+ -- Any actions running in the current session will be aborted, but actions added
862+ -- via 'shakeEnqueue' will be requeued.
854863shakeRestart :: IdeState -> VFSModified -> T. Text -> [DelayedAction () ] -> IO [Key ] -> IO ()
855864shakeRestart IdeState {.. } vfs reason acts ioActionBetweenShakeSession = do
856865 restartDone <- newEmptyTMVarIO
857- atomicModifyIORef'_ (queuedRestart (restartSlot shakeExtras)) $ Just . mergePendingRestart PendingRestart
866+ let slot = restartSlot shakeExtras
867+ -- Publish this restart's barrier, that dependents LSP requests can wait on.
868+ atomically $ writeTVar (lastRestartBarrier slot) restartDone
869+ atomicModifyIORef'_ (queuedRestart slot) $ Just . mergePendingRestart PendingRestart
858870 { pendingRestartVFS = vfs
859- , pendingRestartActionBetweenSessions = ioActionBetweenShakeSession
871+ , pendingRestartActionBetweenSessions = [ ioActionBetweenShakeSession]
860872 , pendingRestartReasons = [reason]
861873 , pendingRestartActions = acts
862874 , pendingRestartDoneSignals = [restartDone]
863875 }
864- void $ tryPutMVar (restartSignal (restartSlot shakeExtras)) ()
876+ void $ tryPutMVar (restartSignal slot) ()
877+ -- Block until the restart (including ioActionBetweenShakeSession) completes.
878+ -- This preserves the invariant from the original synchronous shakeRestart:
879+ -- callers (e.g. the session loader) must not proceed until their
880+ -- between-session actions have run, otherwise downstream rules can observe
881+ -- stale results (see Note at Session.hs restartSession call site).
865882 atomically $ readTMVar restartDone
866883
867884-- | Run a worker that asynchronously processes shake restart requests. Will
@@ -880,10 +897,10 @@ processPendingRestart recorder IdeState{..} = do
880897 takeMVar (restartSignal (restartSlot shakeExtras))
881898 pendingRestart <- atomicModifyIORef' (queuedRestart (restartSlot shakeExtras)) (Nothing ,)
882899 void $ forM pendingRestart $ \ PendingRestart {.. } -> do
883- flip finally (atomically $ traverse (flip tryPutTMVar () ) pendingRestartDoneSignals) $ do
900+ flip finally (atomically $ traverse (flip tryPutTMVar () ) ( reverse pendingRestartDoneSignals) ) $ do
884901 let sessionAction runner = do
885902 (stopTime,() ) <- duration $ logErrorAfter 10 $ cancelShakeSession runner
886- keys <- pendingRestartActionBetweenSessions
903+ keys <- fmap concat ( sequence ( reverse pendingRestartActionBetweenSessions))
887904 -- it is every important to update the dirty keys after we enter the critical section
888905 -- see Note [Housekeeping rule cache and dirty key outside of hls-graph]
889906 atomically $ modifyTVar' (dirtyKeys shakeExtras) $ \ x -> foldl' (flip insertKeySet) x keys
@@ -892,13 +909,15 @@ processPendingRestart recorder IdeState{..} = do
892909 queue <- atomicallyNamed " actionQueue - peek" $ peekInProgress $ actionQueue shakeExtras
893910
894911 -- this log is required by tests
895- logWith recorder Debug $ LogBuildSessionRestart pendingRestartReasons queue backlog stopTime res
912+ logWith recorder Debug $ LogBuildSessionRestart ( reverse pendingRestartReasons) queue backlog stopTime res
896913
897914 withMVar' shakeSession sessionAction $ \ () ->
898915 -- It is crucial to be masked here, otherwise we can get killed
899916 -- between spawning the new thread and updating shakeSession.
900917 -- See https://github.com/haskell/ghcide/issues/79
901- (,() ) <$> newSession recorder shakeExtras pendingRestartVFS shakeDb pendingRestartActions pendingRestartReasons
918+ (,() ) <$> newSession recorder shakeExtras pendingRestartVFS shakeDb
919+ (reverse pendingRestartActions)
920+ (reverse pendingRestartReasons)
902921 pure ()
903922 where
904923 logErrorAfter :: Seconds -> IO () -> IO ()
0 commit comments