@@ -78,9 +78,11 @@ module Development.IDE.Core.Shake(
7878 ) where
7979
8080import Control.Concurrent.Async
81- import Control.Concurrent.Extra (signalBarrier )
81+ import Control.Concurrent.Extra (signalBarrier ,
82+ waitBarrier )
8283import Control.Concurrent.STM
83- import Control.Concurrent.STM (writeTQueue )
84+ import Control.Concurrent.STM (readTQueue ,
85+ writeTQueue )
8486import Control.Concurrent.STM.Stats (atomicallyNamed )
8587import Control.Concurrent.Strict
8688import Control.DeepSeq
@@ -107,6 +109,7 @@ import Data.Hashable
107109import qualified Data.HashMap.Strict as HMap
108110import Data.HashSet (HashSet )
109111import qualified Data.HashSet as HSet
112+ import Data.List (concat )
110113import Data.List.Extra (foldl' , intercalate ,
111114 partition , takeEnd )
112115import qualified Data.List.NonEmpty as NE
@@ -200,7 +203,7 @@ data Log
200203 | LogCancelledAction ! T. Text
201204 | LogSessionInitialised
202205 | LogLookupPersistentKey ! T. Text
203- | LogRestartDebounceCount ! Int
206+ | LogRestartDebounceCount ! Int ! String
204207 | LogShakeGarbageCollection ! T. Text ! Int ! Seconds
205208 -- * OfInterest Log messages
206209 | LogSetFilesOfInterest ! [(NormalizedFilePath , FileOfInterestStatus )]
@@ -247,8 +250,8 @@ instance Pretty Log where
247250 LogSetFilesOfInterest ofInterest ->
248251 " Set files of interst to" <> Pretty. line
249252 <> indent 4 (pretty $ fmap (first fromNormalizedFilePath) ofInterest)
250- LogRestartDebounceCount count ->
251- " Restart debounce count:" <+> pretty count
253+ LogRestartDebounceCount count reason ->
254+ " Restart debounce count:" <+> pretty count <+> " : " <+> pretty reason
252255
253256-- | We need to serialize writes to the database, so we send any function that
254257-- needs to write to the database over the channel, where it will be picked up by
@@ -767,16 +770,16 @@ data RestartArguments = RestartArguments
767770 { restartVFS :: VFSModified
768771 , restartReasons :: [String ]
769772 , restartActions :: [DelayedAction () ]
770- , restartActionBetweenShakeSession :: IO [Key ]
773+ , restartActionBetweenShakeSession :: [ IO [Key ] ]
771774 -- barrier to wait for the session stopped
772775 , restartBarriers :: [Barrier () ]
773776 , restartRecorder :: Recorder (WithPriority Log )
774777 , restartIdeState :: IdeState
775778 }
776779
777780instance Semigroup RestartArguments where
778- RestartArguments a1 a2 a3 a4 a5 a6 a7 <> RestartArguments b1 b2 b3 b4 b5 b6 _b7 =
779- RestartArguments (a1 <> b1) (a2 <> b2) (a3 <> b3) (a4 <> b4) (a5 <> b5) (a6 <> b6) a7
781+ RestartArguments a1 a2 a3 a4 a5 a6 _a7 <> RestartArguments b1 b2 b3 b4 b5 b6 b7 =
782+ RestartArguments (a1 <> b1) (a2 <> b2) (a3 <> b3) (a4 <> b4) (a5 <> b5) b6 b7
780783
781784-- do x until time up and do y
782785-- doUntil time out
@@ -789,38 +792,23 @@ doUntil x = do
789792runWithShake :: (ShakeOpQueue -> IO () ) -> IO ()
790793runWithShake f = do
791794 stopQueue <- newTQueueIO
792- doQueue <- newTQueueIO
793- withAsync (stopShakeLoop stopQueue doQueue) $
794- const $ withAsync (runShakeLoop doQueue) $
795+ -- withAsync (stopShakeLoop stopQueue doQueue) $ const $
796+ withAsync (runShakeLoop stopQueue) $
795797 const $ f stopQueue
796798 where
797- -- keep running the stopShakeOp and stop the shake session
798- -- and send the restart arguments to the runShakeLoop
799- stopShakeLoop :: ShakeOpQueue -> ShakeOpQueue -> IO ()
800- stopShakeLoop stopq doq = do
801- arg <- atomically $ readTQueue stopq
802- -- todo print this out
803- _stopTime <- stopShakeSession arg
804- traceM $ " Stopped shake session"
805- atomically $ writeTQueue doq arg
806- stopShakeLoop stopq doq
807799 runShakeLoop :: ShakeOpQueue -> IO ()
808800 runShakeLoop q = do
801+ argHead <- atomically $ readTQueue q
809802 sleep 0.1
810- x <- atomically (tryPeekTQueue q)
811- when (isJust x) $ do
812- sleep 0.1
813- args <- atomically $ flushTQueue q
814- traceM $ " Restarting shake with " ++ show (length args) ++ " arguments"
815- case NE. nonEmpty args of
816- Nothing -> return ()
817- Just x -> do
818- let count = length x
819- let arg = sconcat x
820- let recorder = restartRecorder arg
821- logWith recorder Info $ LogRestartDebounceCount count
822- -- traceM $ "Restarting shake with " ++ show count ++ " arguments"
823- doShakeRestart arg 1
803+ args <- atomically $ flushTQueue q
804+ case NE. nonEmpty (argHead: args) of
805+ Nothing -> return ()
806+ Just xs -> do
807+ let count = length xs
808+ let arg = sconcat xs
809+ let recorder = restartRecorder arg
810+ logWith recorder Info $ LogRestartDebounceCount count (intercalate " , " (restartReasons arg))
811+ doShakeRestart arg 0
824812 runShakeLoop q
825813
826814-- prepare the restart
@@ -829,10 +817,7 @@ stopShakeSession RestartArguments{restartIdeState=IdeState{..}, ..} = do
829817 withMVarMasked shakeSession
830818 (\ runner -> do
831819 (stopTime,() ) <- duration $ logErrorAfter 10 $ cancelShakeSession runner
832- keys <- restartActionBetweenShakeSession
833- atomically $ modifyTVar' (dirtyKeys shakeExtras) $ \ x -> foldl' (flip insertKeySet) x keys
834820 -- signal the caller that we are done stopping and ready to restart
835- mapM_ (flip signalBarrier () ) restartBarriers
836821 return stopTime
837822 )
838823 where
@@ -846,6 +831,10 @@ doShakeRestart :: RestartArguments -> Seconds -> IO ()
846831doShakeRestart RestartArguments {restartIdeState= IdeState {.. }, .. } stopTime = do
847832 withMVar' shakeSession
848833 (\ runner -> do
834+ (stopTime,() ) <- duration $ logErrorAfter 10 $ cancelShakeSession runner
835+ keys <- concat <$> sequence restartActionBetweenShakeSession
836+ mapM_ (flip signalBarrier () ) restartBarriers
837+ atomically $ modifyTVar' (dirtyKeys shakeExtras) $ \ x -> foldl' (flip insertKeySet) x keys
849838 res <- shakeDatabaseProfile shakeDb
850839 backlog <- readTVarIO $ dirtyKeys shakeExtras
851840 queue <- atomicallyNamed " actionQueue - peek" $ peekInProgress $ actionQueue shakeExtras
@@ -857,6 +846,11 @@ doShakeRestart RestartArguments{restartIdeState=IdeState{..}, ..} stopTime = do
857846 -- See https://github.com/haskell/ghcide/issues/79
858847 (\ () -> do
859848 (,() ) <$> newSession restartRecorder shakeExtras restartVFS shakeDb restartActions (intercalate " , " restartReasons))
849+ where
850+ logErrorAfter :: Seconds -> IO () -> IO ()
851+ logErrorAfter seconds action = flip withAsync (const action) $ do
852+ sleep seconds
853+ logWith restartRecorder Error (LogBuildSessionRestartTakingTooLong seconds)
860854
861855
862856-- | Restart the current 'ShakeSession' with the given system actions.
@@ -869,12 +863,13 @@ shakeRestart recorder IdeState{..} vfs reason acts ioActionBetweenShakeSession =
869863 { restartVFS = vfs
870864 , restartReasons = [reason]
871865 , restartActions = acts
872- , restartActionBetweenShakeSession = ioActionBetweenShakeSession
866+ , restartActionBetweenShakeSession = [ ioActionBetweenShakeSession]
873867 , restartBarriers = [barrier]
874868 , restartRecorder = recorder
875869 , restartIdeState = IdeState {.. }
876870 }
877871 atomically $ writeTQueue (shakeOpQueue $ shakeExtras) restartArgs
872+ waitBarrier barrier
878873
879874-- | Enqueue an action in the existing 'ShakeSession'.
880875-- Returns a computation to block until the action is run, propagating exceptions.
0 commit comments