@@ -78,9 +78,11 @@ module Development.IDE.Core.Shake(
78
78
) where
79
79
80
80
import Control.Concurrent.Async
81
- import Control.Concurrent.Extra (signalBarrier )
81
+ import Control.Concurrent.Extra (signalBarrier ,
82
+ waitBarrier )
82
83
import Control.Concurrent.STM
83
- import Control.Concurrent.STM (writeTQueue )
84
+ import Control.Concurrent.STM (readTQueue ,
85
+ writeTQueue )
84
86
import Control.Concurrent.STM.Stats (atomicallyNamed )
85
87
import Control.Concurrent.Strict
86
88
import Control.DeepSeq
@@ -107,6 +109,7 @@ import Data.Hashable
107
109
import qualified Data.HashMap.Strict as HMap
108
110
import Data.HashSet (HashSet )
109
111
import qualified Data.HashSet as HSet
112
+ import Data.List (concat )
110
113
import Data.List.Extra (foldl' , intercalate ,
111
114
partition , takeEnd )
112
115
import qualified Data.List.NonEmpty as NE
@@ -200,7 +203,7 @@ data Log
200
203
| LogCancelledAction ! T. Text
201
204
| LogSessionInitialised
202
205
| LogLookupPersistentKey ! T. Text
203
- | LogRestartDebounceCount ! Int
206
+ | LogRestartDebounceCount ! Int ! String
204
207
| LogShakeGarbageCollection ! T. Text ! Int ! Seconds
205
208
-- * OfInterest Log messages
206
209
| LogSetFilesOfInterest ! [(NormalizedFilePath , FileOfInterestStatus )]
@@ -247,8 +250,8 @@ instance Pretty Log where
247
250
LogSetFilesOfInterest ofInterest ->
248
251
" Set files of interst to" <> Pretty. line
249
252
<> 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
252
255
253
256
-- | We need to serialize writes to the database, so we send any function that
254
257
-- needs to write to the database over the channel, where it will be picked up by
@@ -767,16 +770,16 @@ data RestartArguments = RestartArguments
767
770
{ restartVFS :: VFSModified
768
771
, restartReasons :: [String ]
769
772
, restartActions :: [DelayedAction () ]
770
- , restartActionBetweenShakeSession :: IO [Key ]
773
+ , restartActionBetweenShakeSession :: [ IO [Key ] ]
771
774
-- barrier to wait for the session stopped
772
775
, restartBarriers :: [Barrier () ]
773
776
, restartRecorder :: Recorder (WithPriority Log )
774
777
, restartIdeState :: IdeState
775
778
}
776
779
777
780
instance 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
780
783
781
784
-- do x until time up and do y
782
785
-- doUntil time out
@@ -789,38 +792,23 @@ doUntil x = do
789
792
runWithShake :: (ShakeOpQueue -> IO () ) -> IO ()
790
793
runWithShake f = do
791
794
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) $
795
797
const $ f stopQueue
796
798
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
807
799
runShakeLoop :: ShakeOpQueue -> IO ()
808
800
runShakeLoop q = do
801
+ argHead <- atomically $ readTQueue q
809
802
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
824
812
runShakeLoop q
825
813
826
814
-- prepare the restart
@@ -829,10 +817,7 @@ stopShakeSession RestartArguments{restartIdeState=IdeState{..}, ..} = do
829
817
withMVarMasked shakeSession
830
818
(\ runner -> do
831
819
(stopTime,() ) <- duration $ logErrorAfter 10 $ cancelShakeSession runner
832
- keys <- restartActionBetweenShakeSession
833
- atomically $ modifyTVar' (dirtyKeys shakeExtras) $ \ x -> foldl' (flip insertKeySet) x keys
834
820
-- signal the caller that we are done stopping and ready to restart
835
- mapM_ (flip signalBarrier () ) restartBarriers
836
821
return stopTime
837
822
)
838
823
where
@@ -846,6 +831,10 @@ doShakeRestart :: RestartArguments -> Seconds -> IO ()
846
831
doShakeRestart RestartArguments {restartIdeState= IdeState {.. }, .. } stopTime = do
847
832
withMVar' shakeSession
848
833
(\ 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
849
838
res <- shakeDatabaseProfile shakeDb
850
839
backlog <- readTVarIO $ dirtyKeys shakeExtras
851
840
queue <- atomicallyNamed " actionQueue - peek" $ peekInProgress $ actionQueue shakeExtras
@@ -857,6 +846,11 @@ doShakeRestart RestartArguments{restartIdeState=IdeState{..}, ..} stopTime = do
857
846
-- See https://github.com/haskell/ghcide/issues/79
858
847
(\ () -> do
859
848
(,() ) <$> 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)
860
854
861
855
862
856
-- | Restart the current 'ShakeSession' with the given system actions.
@@ -869,12 +863,13 @@ shakeRestart recorder IdeState{..} vfs reason acts ioActionBetweenShakeSession =
869
863
{ restartVFS = vfs
870
864
, restartReasons = [reason]
871
865
, restartActions = acts
872
- , restartActionBetweenShakeSession = ioActionBetweenShakeSession
866
+ , restartActionBetweenShakeSession = [ ioActionBetweenShakeSession]
873
867
, restartBarriers = [barrier]
874
868
, restartRecorder = recorder
875
869
, restartIdeState = IdeState {.. }
876
870
}
877
871
atomically $ writeTQueue (shakeOpQueue $ shakeExtras) restartArgs
872
+ waitBarrier barrier
878
873
879
874
-- | Enqueue an action in the existing 'ShakeSession'.
880
875
-- Returns a computation to block until the action is run, propagating exceptions.
0 commit comments