Skip to content

Commit ebfb375

Browse files
committed
more sync
1 parent f76bff4 commit ebfb375

File tree

2 files changed

+39
-41
lines changed

2 files changed

+39
-41
lines changed

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

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,11 @@ module Development.IDE.Core.Shake(
7878
) where
7979

8080
import Control.Concurrent.Async
81-
import Control.Concurrent.Extra (signalBarrier)
81+
import Control.Concurrent.Extra (signalBarrier,
82+
waitBarrier)
8283
import Control.Concurrent.STM
83-
import Control.Concurrent.STM (writeTQueue)
84+
import Control.Concurrent.STM (readTQueue,
85+
writeTQueue)
8486
import Control.Concurrent.STM.Stats (atomicallyNamed)
8587
import Control.Concurrent.Strict
8688
import Control.DeepSeq
@@ -107,6 +109,7 @@ import Data.Hashable
107109
import qualified Data.HashMap.Strict as HMap
108110
import Data.HashSet (HashSet)
109111
import qualified Data.HashSet as HSet
112+
import Data.List (concat)
110113
import Data.List.Extra (foldl', intercalate,
111114
partition, takeEnd)
112115
import 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

777780
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
780783

781784
-- do x until time up and do y
782785
-- doUntil time out
@@ -789,38 +792,23 @@ doUntil x = do
789792
runWithShake :: (ShakeOpQueue-> IO ()) -> IO ()
790793
runWithShake 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 ()
846831
doShakeRestart 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.

ghcide/test/exe/DiagnosticTests.hs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import Development.IDE.Test (diagnostic,
1616
expectDiagnostics,
1717
expectDiagnosticsWithTags,
1818
expectNoMoreDiagnostics,
19-
flushMessages, waitForAction)
19+
flushMessages, waitForAction,
20+
waitForTypecheck)
2021
import Development.IDE.Types.Location
2122
import qualified Language.LSP.Protocol.Lens as L
2223
import Language.LSP.Protocol.Message
@@ -108,7 +109,8 @@ tests = testGroup "diagnostics"
108109
, "foo :: Int -> String"
109110
, "foo a = _ a"
110111
]
111-
_ <- createDoc "Testing.hs" "haskell" content
112+
s <- createDoc "Testing.hs" "haskell" content
113+
-- waitForTypecheck s
112114
expectDiagnostics
113115
[ ( "Testing.hs"
114116
, [(DiagnosticSeverity_Error, (2, 8), "Found hole: _ :: Int -> String")]
@@ -132,6 +134,7 @@ tests = testGroup "diagnostics"
132134
deferralTest title binding msg = testSessionWait title $ do
133135
_ <- createDoc "A.hs" "haskell" $ sourceA binding
134136
_ <- createDoc "B.hs" "haskell" sourceB
137+
liftIO $ sleep 1
135138
expectDiagnostics $ expectedDs msg
136139
in
137140
[ deferralTest "type error" "True" "Couldn't match expected type"

0 commit comments

Comments
 (0)