5
5
{-# LANGUAGE DerivingStrategies #-}
6
6
{-# LANGUAGE DuplicateRecordFields #-}
7
7
{-# LANGUAGE ExistentialQuantification #-}
8
+ {-# LANGUAGE PackageImports #-}
8
9
{-# LANGUAGE PolyKinds #-}
9
10
{-# LANGUAGE RankNTypes #-}
10
11
{-# LANGUAGE RecursiveDo #-}
@@ -155,16 +156,17 @@ import Data.Default
155
156
import Data.Foldable (toList )
156
157
import Data.HashSet (HashSet )
157
158
import qualified Data.HashSet as HSet
158
- import Data.IORef.Extra (atomicModifyIORef'_ ,
159
- atomicModifyIORef_ )
160
159
import Data.String (fromString )
161
160
import Data.Text (pack )
162
161
import Debug.Trace.Flags (userTracingEnabled )
163
162
import qualified Development.IDE.Types.Exports as ExportsMap
163
+ import qualified Focus
164
164
import HieDb.Types
165
165
import Ide.Plugin.Config
166
166
import qualified Ide.PluginUtils as HLS
167
167
import Ide.Types (PluginId )
168
+ import qualified "list-t" ListT
169
+ import qualified StmContainers.Map as STM
168
170
169
171
-- | We need to serialize writes to the database, so we send any function that
170
172
-- needs to write to the database over the channel, where it will be picked up by
@@ -188,7 +190,7 @@ data ShakeExtras = ShakeExtras
188
190
,debouncer :: Debouncer NormalizedUri
189
191
,logger :: Logger
190
192
,globals :: Var (HMap. HashMap TypeRep Dynamic )
191
- ,state :: Var Values
193
+ ,state :: Values
192
194
,diagnostics :: Var DiagnosticStore
193
195
,hiddenDiagnostics :: Var DiagnosticStore
194
196
,publishedDiagnostics :: Var (HMap. HashMap NormalizedUri [Diagnostic ])
@@ -222,7 +224,7 @@ data ShakeExtras = ShakeExtras
222
224
, vfs :: VFSHandle
223
225
, defaultConfig :: Config
224
226
-- ^ Default HLS config, only relevant if the client does not provide any Config
225
- , dirtyKeys :: IORef (HashSet Key )
227
+ , dirtyKeys :: TVar (HashSet Key )
226
228
-- ^ Set of dirty rule keys since the last Shake run
227
229
}
228
230
@@ -326,7 +328,6 @@ getIdeOptionsIO ide = do
326
328
-- for the version of that value.
327
329
lastValueIO :: IdeRule k v => ShakeExtras -> k -> NormalizedFilePath -> IO (Maybe (v , PositionMapping ))
328
330
lastValueIO s@ ShakeExtras {positionMapping,persistentKeys,state} k file = do
329
- hm <- readVar state
330
331
allMappings <- readVar positionMapping
331
332
332
333
let readPersistent
@@ -341,10 +342,10 @@ lastValueIO s@ShakeExtras{positionMapping,persistentKeys,state} k file = do
341
342
MaybeT $ pure $ (,del,ver) <$> fromDynamic dv
342
343
case mv of
343
344
Nothing -> do
344
- void $ modifyVar' state $ HMap. alter (alterValue $ Failed True ) (toKey k file)
345
+ void $ atomically $ STM. focus ( Focus. alter (alterValue $ Failed True )) (toKey k file) state
345
346
return Nothing
346
347
Just (v,del,ver) -> do
347
- void $ modifyVar' state $ HMap. alter (alterValue $ Stale (Just del) ver (toDyn v)) (toKey k file)
348
+ void $ atomically $ STM. focus ( Focus. alter (alterValue $ Stale (Just del) ver (toDyn v))) (toKey k file) state
348
349
return $ Just (v,addDelta del $ mappingForVersion allMappings file ver)
349
350
350
351
-- We got a new stale value from the persistent rule, insert it in the map without affecting diagnostics
@@ -355,7 +356,7 @@ lastValueIO s@ShakeExtras{positionMapping,persistentKeys,state} k file = do
355
356
-- Something already succeeded before, leave it alone
356
357
_ -> old
357
358
358
- case HMap .lookup (toKey k file) hm of
359
+ atomically ( STM .lookup (toKey k file) state) >>= \ case
359
360
Nothing -> readPersistent
360
361
Just (ValueWithDiagnostics v _) -> case v of
361
362
Succeeded ver (fromDynamic -> Just v) -> pure (Just (v, mappingForVersion allMappings file ver))
@@ -420,14 +421,14 @@ shakeDatabaseProfileIO mbProfileDir = do
420
421
return (dir </> file)
421
422
422
423
setValues :: IdeRule k v
423
- => Var Values
424
+ => Values
424
425
-> k
425
426
-> NormalizedFilePath
426
427
-> Value v
427
428
-> Vector FileDiagnostic
428
- -> IO ()
429
+ -> STM ()
429
430
setValues state key file val diags =
430
- void $ modifyVar' state $ HMap . insert (toKey key file) ( ValueWithDiagnostics (fmap toDyn val) diags)
431
+ STM . insert (ValueWithDiagnostics (fmap toDyn val) diags) (toKey key file) state
431
432
432
433
433
434
-- | Delete the value stored for a given ide build key
@@ -437,9 +438,9 @@ deleteValue
437
438
-> k
438
439
-> NormalizedFilePath
439
440
-> IO ()
440
- deleteValue ShakeExtras {dirtyKeys, state} key file = do
441
- void $ modifyVar' state $ HMap . delete (toKey key file)
442
- atomicModifyIORef_ dirtyKeys $ HSet. insert (toKey key file)
441
+ deleteValue ShakeExtras {dirtyKeys, state} key file = atomically $ do
442
+ STM . delete (toKey key file) state
443
+ modifyTVar' dirtyKeys $ HSet. insert (toKey key file)
443
444
444
445
recordDirtyKeys
445
446
:: Shake. ShakeValue k
@@ -448,28 +449,28 @@ recordDirtyKeys
448
449
-> [NormalizedFilePath ]
449
450
-> IO ()
450
451
recordDirtyKeys ShakeExtras {dirtyKeys} key file = withEventTrace " recordDirtyKeys" $ \ addEvent -> do
451
- atomicModifyIORef_ dirtyKeys $ \ x -> foldl' (flip HSet. insert) x (toKey key <$> file)
452
+ atomically $ modifyTVar' dirtyKeys $ \ x -> foldl' (flip HSet. insert) x (toKey key <$> file)
452
453
addEvent (fromString $ " dirty " <> show key) (fromString $ unlines $ map fromNormalizedFilePath file)
453
454
454
455
455
456
-- | We return Nothing if the rule has not run and Just Failed if it has failed to produce a value.
456
457
getValues ::
457
458
forall k v .
458
459
IdeRule k v =>
459
- Var Values ->
460
+ Values ->
460
461
k ->
461
462
NormalizedFilePath ->
462
- IO (Maybe (Value v , Vector FileDiagnostic ))
463
+ STM (Maybe (Value v , Vector FileDiagnostic ))
463
464
getValues state key file = do
464
- vs <- readVar state
465
- case HMap. lookup (toKey key file) vs of
465
+ STM. lookup (toKey key file) state >>= \ case
466
466
Nothing -> pure Nothing
467
467
Just (ValueWithDiagnostics v diagsV) -> do
468
- let r = fmap (fromJust . fromDynamic @ v ) v
468
+ let ! r = seqValue $ fmap (fromJust . fromDynamic @ v ) v
469
+ ! res = (r,diagsV)
469
470
-- Force to make sure we do not retain a reference to the HashMap
470
471
-- and we blow up immediately if the fromJust should fail
471
472
-- (which would be an internal error).
472
- evaluate (r `seqValue` Just (r, diagsV))
473
+ return $ Just res
473
474
474
475
-- | Get all the files in the project
475
476
knownTargets :: Action (Hashed KnownTargets )
@@ -480,11 +481,11 @@ knownTargets = do
480
481
-- | Seq the result stored in the Shake value. This only
481
482
-- evaluates the value to WHNF not NF. We take care of the latter
482
483
-- elsewhere and doing it twice is expensive.
483
- seqValue :: Value v -> b -> b
484
- seqValue v b = case v of
485
- Succeeded ver v -> rnf ver `seq` v `seq` b
486
- Stale d ver v -> rnf d `seq` rnf ver `seq` v `seq` b
487
- Failed _ -> b
484
+ seqValue :: Value v -> Value v
485
+ seqValue val = case val of
486
+ Succeeded ver v -> rnf ver `seq` v `seq` val
487
+ Stale d ver v -> rnf d `seq` rnf ver `seq` v `seq` val
488
+ Failed _ -> val
488
489
489
490
-- | Open a 'IdeState', should be shut using 'shakeShut'.
490
491
shakeOpen :: Maybe (LSP. LanguageContextEnv Config )
@@ -507,7 +508,7 @@ shakeOpen lspEnv defaultConfig logger debouncer
507
508
ideNc <- newIORef (initNameCache us knownKeyNames)
508
509
shakeExtras <- do
509
510
globals <- newVar HMap. empty
510
- state <- newVar HMap. empty
511
+ state <- STM. newIO
511
512
diagnostics <- newVar mempty
512
513
hiddenDiagnostics <- newVar mempty
513
514
publishedDiagnostics <- newVar mempty
@@ -536,7 +537,7 @@ shakeOpen lspEnv defaultConfig logger debouncer
536
537
537
538
let clientCapabilities = maybe def LSP. resClientCapabilities lspEnv
538
539
539
- dirtyKeys <- newIORef mempty
540
+ dirtyKeys <- newTVarIO mempty
540
541
pure ShakeExtras {.. }
541
542
(shakeDbM, shakeClose) <-
542
543
shakeOpenDatabase
@@ -566,8 +567,8 @@ startTelemetry db extras@ShakeExtras{..}
566
567
IdeOptions {optCheckParents} <- getIdeOptionsIO extras
567
568
checkParents <- optCheckParents
568
569
regularly 1 $ do
569
- readVar state >>= observe countKeys . countRelevantKeys checkParents . HMap. keys
570
- readIORef dirtyKeys >>= observe countDirty . countRelevantKeys checkParents . HSet. toList
570
+ observe countKeys . countRelevantKeys checkParents . map fst =<< (atomically . ListT. toList . STM. listT) state
571
+ readTVarIO dirtyKeys >>= observe countDirty . countRelevantKeys checkParents . HSet. toList
571
572
shakeGetBuildStep db >>= observe countBuilds
572
573
573
574
| otherwise = async (pure () )
@@ -624,7 +625,7 @@ shakeRestart IdeState{..} reason acts =
624
625
(\ runner -> do
625
626
(stopTime,() ) <- duration (cancelShakeSession runner)
626
627
res <- shakeDatabaseProfile shakeDb
627
- backlog <- readIORef $ dirtyKeys shakeExtras
628
+ backlog <- readTVarIO ( dirtyKeys shakeExtras)
628
629
queue <- atomically $ peekInProgress $ actionQueue shakeExtras
629
630
let profile = case res of
630
631
Just fp -> " , profile saved at " <> fp
@@ -685,7 +686,7 @@ newSession extras@ShakeExtras{..} shakeDb acts reason = do
685
686
reenqueued <- atomically $ peekInProgress actionQueue
686
687
allPendingKeys <-
687
688
if optRunSubset
688
- then Just <$> readIORef dirtyKeys
689
+ then Just <$> readTVarIO dirtyKeys
689
690
else return Nothing
690
691
let
691
692
-- A daemon-like action used to inject additional work
@@ -785,29 +786,30 @@ garbageCollectDirtyKeysOlderThan maxAge checkParents = otTracedGarbageCollection
785
786
garbageCollectKeys :: String -> Int -> CheckParents -> [(Key , Int )] -> Action [Key ]
786
787
garbageCollectKeys label maxAge checkParents agedKeys = do
787
788
start <- liftIO offsetTime
788
- extras <- getShakeExtras
789
- (n:: Int , garbage ) <- liftIO $ modifyVar (state extras) $ \ vmap ->
790
- evaluate $ foldl' removeDirtyKey (vmap, (0 ,[] )) agedKeys
791
- liftIO $ atomicModifyIORef_ (dirtyKeys extras) $ \ x ->
792
- foldl' (flip HSet. insert) x garbage
789
+ ShakeExtras {state, dirtyKeys, lspEnv, logger, ideTesting} <- getShakeExtras
790
+ (n:: Int , garbage ) <- liftIO $
791
+ foldM (removeDirtyKey dirtyKeys state) (0 ,[] ) agedKeys
793
792
t <- liftIO start
794
793
when (n> 0 ) $ liftIO $ do
795
- logDebug ( logger extras) $ T. pack $
794
+ logDebug logger $ T. pack $
796
795
label <> " of " <> show n <> " keys (took " <> showDuration t <> " )"
797
- when (coerce $ ideTesting extras ) $ liftIO $ mRunLspT ( lspEnv extras) $
796
+ when (coerce ideTesting) $ liftIO $ mRunLspT lspEnv $
798
797
LSP. sendNotification (SCustomMethod " ghcide/GC" )
799
798
(toJSON $ mapMaybe (fmap showKey . fromKeyType) garbage)
800
799
return garbage
801
800
802
801
where
803
802
showKey = show . Q
804
- removeDirtyKey st@ (vmap,( ! counter, keys) ) (k, age)
803
+ removeDirtyKey dk values st@ (! counter, keys) (k, age)
805
804
| age > maxAge
806
805
, Just (kt,_) <- fromKeyType k
807
806
, not (kt `HSet.member` preservedKeys checkParents)
808
- , (True , vmap') <- HMap. alterF (\ prev -> (isJust prev, Nothing )) k vmap
809
- = (vmap', (counter+ 1 , k: keys))
810
- | otherwise = st
807
+ = atomically $ do
808
+ gotIt <- STM. focus (Focus. member <* Focus. delete) k values
809
+ when gotIt $
810
+ modifyTVar' dk (HSet. insert k)
811
+ return $ if gotIt then (counter+ 1 , k: keys) else st
812
+ | otherwise = pure st
811
813
812
814
countRelevantKeys :: CheckParents -> [Key ] -> Int
813
815
countRelevantKeys checkParents =
@@ -906,7 +908,7 @@ useWithStaleFast' key file = do
906
908
wait <- delayedAction $ mkDelayedAction (" C:" ++ show key ++ " :" ++ fromNormalizedFilePath file) Debug $ use key file
907
909
908
910
s@ ShakeExtras {state} <- askShake
909
- r <- liftIO $ getValues state key file
911
+ r <- liftIO $ atomically $ getValues state key file
910
912
liftIO $ case r of
911
913
-- block for the result if we haven't computed before
912
914
Nothing -> do
@@ -1015,7 +1017,7 @@ defineEarlyCutoff' doDiagnostics cmp key file old mode action = do
1015
1017
(if optSkipProgress options key then id else inProgress progress file) $ do
1016
1018
val <- case old of
1017
1019
Just old | mode == RunDependenciesSame -> do
1018
- v <- liftIO $ getValues state key file
1020
+ v <- liftIO $ atomically $ getValues state key file
1019
1021
case v of
1020
1022
-- No changes in the dependencies and we have
1021
1023
-- an existing successful result.
@@ -1034,10 +1036,10 @@ defineEarlyCutoff' doDiagnostics cmp key file old mode action = do
1034
1036
(do v <- action; liftIO $ evaluate $ force v) $
1035
1037
\ (e :: SomeException ) -> do
1036
1038
pure (Nothing , ([ideErrorText file $ T. pack $ show e | not $ isBadDependency e],Nothing ))
1037
- modTime <- liftIO $ (currentValue . fst =<< ) <$> getValues state GetModificationTime file
1039
+ modTime <- liftIO $ (currentValue . fst =<< ) <$> atomically ( getValues state GetModificationTime file)
1038
1040
(bs, res) <- case res of
1039
1041
Nothing -> do
1040
- staleV <- liftIO $ getValues state key file
1042
+ staleV <- liftIO $ atomically $ getValues state key file
1041
1043
pure $ case staleV of
1042
1044
Nothing -> (toShakeValue ShakeResult bs, Failed False )
1043
1045
Just v -> case v of
@@ -1048,7 +1050,7 @@ defineEarlyCutoff' doDiagnostics cmp key file old mode action = do
1048
1050
(Failed b, _) ->
1049
1051
(toShakeValue ShakeResult bs, Failed b)
1050
1052
Just v -> pure (maybe ShakeNoCutoff ShakeResult bs, Succeeded (vfsVersion =<< modTime) v)
1051
- liftIO $ setValues state key file res (Vector. fromList diags)
1053
+ liftIO $ atomically $ setValues state key file res (Vector. fromList diags)
1052
1054
doDiagnostics diags
1053
1055
let eq = case (bs, fmap decodeShakeValue old) of
1054
1056
(ShakeResult a, Just (ShakeResult b)) -> cmp a b
@@ -1060,7 +1062,7 @@ defineEarlyCutoff' doDiagnostics cmp key file old mode action = do
1060
1062
(if eq then ChangedRecomputeSame else ChangedRecomputeDiff )
1061
1063
(encodeShakeValue bs) $
1062
1064
A res
1063
- liftIO $ atomicModifyIORef'_ dirtyKeys (HSet. delete $ toKey key file)
1065
+ liftIO $ atomically $ modifyTVar' dirtyKeys (HSet. delete $ toKey key file)
1064
1066
return res
1065
1067
1066
1068
traceA :: A v -> String
@@ -1148,7 +1150,7 @@ updateFileDiagnostics :: MonadIO m
1148
1150
-> [(ShowDiagnostic ,Diagnostic )] -- ^ current results
1149
1151
-> m ()
1150
1152
updateFileDiagnostics fp k ShakeExtras {logger, diagnostics, hiddenDiagnostics, publishedDiagnostics, state, debouncer, lspEnv} current = liftIO $ do
1151
- modTime <- (currentValue . fst =<< ) <$> getValues state GetModificationTime fp
1153
+ modTime <- (currentValue . fst =<< ) <$> atomically ( getValues state GetModificationTime fp)
1152
1154
let (currentShown, currentHidden) = partition ((== ShowDiag ) . fst ) current
1153
1155
uri = filePathToUri' fp
1154
1156
ver = vfsVersion =<< modTime
0 commit comments