-
-
Notifications
You must be signed in to change notification settings - Fork 392
Fix hls-graph: phantom dependencies invoke in branching deps (resolve #3423) #4087
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 29 commits
b862c6d
f4458a7
03dac25
069f3ed
4614d82
10fa6ee
b4bf796
3953584
89a263e
d09cc33
b4a527c
c8b286a
fd4ab4d
0e73a5c
e08f070
cb4a527
90ebb96
37560e9
8459019
9d33b5a
1ede741
5126c75
1fd122e
fd812cf
e8e88c7
bbe1be6
0bd4a20
c580ff5
888e249
aeeb1be
451e7ce
eee5dc8
cf37253
69d1dad
95a42f0
4a1a52e
af2bdfb
87cfc28
9b72bf0
c3732b1
10d0494
024757c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,9 +3,9 @@ | |
{-# OPTIONS_GHC -Wno-redundant-constraints #-} | ||
|
||
{-# LANGUAGE DerivingStrategies #-} | ||
{-# LANGUAGE LambdaCase #-} | ||
{-# LANGUAGE RecordWildCards #-} | ||
{-# LANGUAGE TypeFamilies #-} | ||
{-# LANGUAGE ViewPatterns #-} | ||
|
||
module Development.IDE.Graph.Internal.Database (newDatabase, incDatabase, build, getDirtySet, getKeysAndVisitAge) where | ||
|
||
|
@@ -25,7 +25,7 @@ import Control.Monad.Trans.Reader | |
import qualified Control.Monad.Trans.State.Strict as State | ||
import Data.Dynamic | ||
import Data.Either | ||
import Data.Foldable (for_, traverse_) | ||
import Data.Foldable (fold, for_, traverse_) | ||
import Data.IORef.Extra | ||
import Data.List.NonEmpty (unzip) | ||
import Data.Maybe | ||
|
@@ -133,26 +133,40 @@ builder db@Database{..} stack keys = withRunInIO $ \(RunInIO run) -> do | |
waitAll | ||
pure results | ||
|
||
isDirty :: Foldable t => Result -> t (a, Result) -> Bool | ||
isDirty me = any (\(_,dep) -> resultBuilt me < resultChanged dep) | ||
|
||
-- | Refresh dependencies for a key and compute the key: | ||
-- The deps refresh is kicking up linearly. If any of the deps are dirty in the process, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please explain what does kicking mean in this context? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we used to try to kick all the deps concurrently and collect all the results. The linearly means one following another. Following the deps order in the action when it last ran There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Kicking means firing up the refresh of the dependency, if we have clean cache, it might be just a look up. |
||
-- we jump to the actual computation of the key and shortcut the refreshing the rest of the deps. | ||
-- * If no dirty dependencies and we have evaluated the key previously, then we refresh it in the current thread. | ||
-- This assumes that the implementation will be a lookup | ||
-- * Otherwise, we spawn a new thread to refresh the dirty deps (if any) and the key itself | ||
refreshDeps :: KeySet -> Database -> Stack -> Key -> Result -> [KeySet] -> AIO (IO Result) | ||
refreshDeps visited db stack key result = \case | ||
-- no more deps to refresh | ||
[] -> pure $ compute db stack key RunDependenciesSame (Just result) | ||
(dep:deps) -> do | ||
let newVisited = dep <> visited | ||
res <- builder db stack (toListKeySet (dep `differenceKeySet` visited)) | ||
case res of | ||
Left res -> if isDirty result res | ||
-- restart the computation if any of the deps are dirty | ||
then asyncWithCleanUp $ liftIO $ compute db stack key RunDependenciesChanged (Just result) | ||
-- else kick the rest of the deps | ||
else refreshDeps newVisited db stack key result deps | ||
Right iores -> asyncWithCleanUp $ liftIO $ do | ||
res <- iores | ||
if isDirty result res | ||
then compute db stack key RunDependenciesChanged (Just result) | ||
else join $ runAIO $ refreshDeps newVisited db stack key result deps | ||
|
||
-- | Refresh a key: | ||
-- * If no dirty dependencies and we have evaluated the key previously, then we refresh it in the current thread. | ||
-- This assumes that the implementation will be a lookup | ||
-- * Otherwise, we spawn a new thread to refresh the dirty deps (if any) and the key itself | ||
refresh :: Database -> Stack -> Key -> Maybe Result -> AIO (IO Result) | ||
-- refresh _ st k _ | traceShow ("refresh", st, k) False = undefined | ||
refresh db stack key result = case (addStack key stack, result) of | ||
(Left e, _) -> throw e | ||
(Right stack, Just me@Result{resultDeps = ResultDeps (toListKeySet -> deps)}) -> do | ||
res <- builder db stack deps | ||
let isDirty = any (\(_,dep) -> resultBuilt me < resultChanged dep) | ||
case res of | ||
Left res -> | ||
if isDirty res | ||
then asyncWithCleanUp $ liftIO $ compute db stack key RunDependenciesChanged result | ||
else pure $ compute db stack key RunDependenciesSame result | ||
Right iores -> asyncWithCleanUp $ liftIO $ do | ||
res <- iores | ||
let mode = if isDirty res then RunDependenciesChanged else RunDependenciesSame | ||
compute db stack key mode result | ||
(Right stack, Just me@Result{resultDeps = ResultDeps deps}) -> refreshDeps mempty db stack key me (reverse deps) | ||
(Right stack, _) -> | ||
asyncWithCleanUp $ liftIO $ compute db stack key RunDependenciesChanged result | ||
|
||
|
@@ -172,8 +186,8 @@ compute db@Database{..} stack key mode result = do | |
actualDeps = if runChanged /= ChangedNothing then deps else previousDeps | ||
previousDeps= maybe UnknownDeps resultDeps result | ||
let res = Result runValue built' changed built actualDeps execution runStore | ||
case getResultDepsDefault mempty actualDeps of | ||
deps | not(nullKeySet deps) | ||
case fold $ getResultDepsDefault mempty actualDeps of | ||
deps | not (nullKeySet deps) | ||
&& runChanged /= ChangedNothing | ||
-> do | ||
-- IMPORTANT: record the reverse deps **before** marking the key Clean. | ||
|
@@ -182,7 +196,7 @@ compute db@Database{..} stack key mode result = do | |
-- on the next build. | ||
void $ | ||
updateReverseDeps key db | ||
(getResultDepsDefault mempty previousDeps) | ||
(fold $ getResultDepsDefault mempty previousDeps) | ||
deps | ||
_ -> pure () | ||
atomicallyNamed "compute" $ SMap.focus (updateStatus $ Clean res) key databaseValues | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -144,17 +144,17 @@ data Result = Result { | |
resultData :: !BS.ByteString | ||
} | ||
|
||
data ResultDeps = UnknownDeps | AlwaysRerunDeps !KeySet | ResultDeps !KeySet | ||
data ResultDeps = UnknownDeps | AlwaysRerunDeps ![KeySet] | ResultDeps ![KeySet] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. make it clear that these are stored in reverse order There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I added a comment about this invariant. |
||
deriving (Eq, Show) | ||
|
||
getResultDepsDefault :: KeySet -> ResultDeps -> KeySet | ||
getResultDepsDefault :: KeySet -> ResultDeps -> [KeySet] | ||
getResultDepsDefault _ (ResultDeps ids) = ids | ||
getResultDepsDefault _ (AlwaysRerunDeps ids) = ids | ||
getResultDepsDefault def UnknownDeps = def | ||
getResultDepsDefault def UnknownDeps = [def] | ||
|
||
mapResultDeps :: (KeySet -> KeySet) -> ResultDeps -> ResultDeps | ||
mapResultDeps f (ResultDeps ids) = ResultDeps $ f ids | ||
mapResultDeps f (AlwaysRerunDeps ids) = AlwaysRerunDeps $ f ids | ||
mapResultDeps f (ResultDeps ids) = ResultDeps $ fmap f ids | ||
mapResultDeps f (AlwaysRerunDeps ids) = AlwaysRerunDeps $ fmap f ids | ||
mapResultDeps _ UnknownDeps = UnknownDeps | ||
|
||
instance Semigroup ResultDeps where | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about this commented out code?
Do we need to keep it? Does something break when you uncomment it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two are small files, the other two are large files. Just conveniently choosing between them.