Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit eb5356d

Browse files
new hls-graph runtime
1 parent 08350aa commit eb5356d

File tree

17 files changed

+328
-259
lines changed

17 files changed

+328
-259
lines changed

‎ghcide/ghcide.cabal‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ library
142142
Development.IDE.Core.Shake
143143
Development.IDE.Core.Tracing
144144
Development.IDE.Core.UseStale
145-
Development.IDE.Core.WorkerThread
146145
Development.IDE.GHC.Compat
147146
Development.IDE.GHC.Compat.Core
148147
Development.IDE.GHC.Compat.CmdLine

‎ghcide/session-loader/Development/IDE/Session.hs‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,12 @@ import qualified Data.HashSet as Set
105105
import qualified Data.Set as OS
106106
import Database.SQLite.Simple
107107
import Development.IDE.Core.Tracing (withTrace)
108-
import Development.IDE.Core.WorkerThread
109108
import qualified Development.IDE.GHC.Compat.Util as Compat
110109
import Development.IDE.Session.Diagnostics (renderCradleError)
111110
import Development.IDE.Types.Shake (WithHieDb,
112111
WithHieDbShield (..),
113112
toNoFileKey)
113+
import Development.IDE.WorkerThread
114114
import GHC.Data.Graph.Directed
115115
import HieDb.Create
116116
import HieDb.Types
@@ -153,6 +153,14 @@ data Log
153153
| LogSessionWorkerThread LogWorkerThread
154154
deriving instance Show Log
155155

156+
instance Pretty LogWorkerThread where
157+
pretty = \case
158+
LogThreadEnding t -> "Worker thread ending:" <+> pretty t
159+
LogThreadEnded t -> "Worker thread ended:" <+> pretty t
160+
LogSingleWorkStarting t -> "Worker starting a unit of work: " <+> pretty t
161+
LogSingleWorkEnded t -> "Worker ended a unit of work: " <+> pretty t
162+
LogMainThreadId t tid -> "Main thread for" <+> pretty t <+> "is" <+> pretty (show tid)
163+
156164
instance Pretty Log where
157165
pretty = \case
158166
LogSessionWorkerThread msg -> pretty msg
@@ -384,7 +392,7 @@ runWithDb recorder fp = ContT $ \k -> do
384392
_ <- withWriteDbRetryable deleteMissingRealFiles
385393
_ <- withWriteDbRetryable garbageCollectTypeNames
386394

387-
runContT (withWorkerQueue (cmapWithPrio LogSessionWorkerThread recorder) "hiedb thread" (writer withWriteDbRetryable))
395+
runContT (withWorkerQueue (logWith (cmapWithPrio LogSessionWorkerThread recorder) Debug) "hiedb thread" (writer withWriteDbRetryable))
388396
$ \chan -> withHieDb fp (\readDb -> k (WithHieDbShield $ makeWithHieDbRetryable recorder rng readDb, chan))
389397
where
390398
writer withHieDbRetryable l = do

‎ghcide/src/Development/IDE/Core/Compile.hs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ import Development.IDE.Core.Preprocessor
7575
import Development.IDE.Core.ProgressReporting (progressUpdate)
7676
import Development.IDE.Core.RuleTypes
7777
import Development.IDE.Core.Shake
78-
import Development.IDE.Core.WorkerThread (writeTaskQueue)
78+
import Development.IDE.WorkerThread (writeTaskQueue)
7979
import Development.IDE.Core.Tracing (withTrace)
8080
import qualified Development.IDE.GHC.Compat as Compat
8181
import qualified Development.IDE.GHC.Compat as GHC

‎ghcide/src/Development/IDE/Core/FileStore.hs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ import Development.IDE.Core.IdeConfiguration (isWorkspaceFile)
4545
import Development.IDE.Core.RuleTypes
4646
import Development.IDE.Core.Shake hiding (Log)
4747
import qualified Development.IDE.Core.Shake as Shake
48-
import Development.IDE.Core.WorkerThread
4948
import Development.IDE.GHC.Orphans ()
5049
import Development.IDE.Graph
5150
import Development.IDE.Import.DependencyInformation
5251
import Development.IDE.Types.Diagnostics
5352
import Development.IDE.Types.Location
5453
import Development.IDE.Types.Options
5554
import Development.IDE.Types.Shake (toKey)
55+
import Development.IDE.WorkerThread
5656
import HieDb.Create (deleteMissingRealFiles)
5757
import Ide.Logger (Pretty (pretty),
5858
Priority (Info),

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

Lines changed: 121 additions & 87 deletions
Large diffs are not rendered by default.

‎ghcide/src/Development/IDE/LSP/LanguageServer.hs‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,15 @@ import Development.IDE.Core.IdeConfiguration
4646
import Development.IDE.Core.Service (shutdown)
4747
import Development.IDE.Core.Shake hiding (Log)
4848
import Development.IDE.Core.Tracing
49-
import Development.IDE.Core.WorkerThread
5049
import qualified Development.IDE.Session as Session
5150
import Development.IDE.Types.Shake (WithHieDb,
5251
WithHieDbShield (..))
52+
import Development.IDE.WorkerThread
5353
import Ide.Logger
5454
import Language.LSP.Server (LanguageContextEnv,
5555
LspServerLog,
5656
type (<~>))
57+
import System.Time.Extra (Seconds, sleep)
5758
import System.Timeout (timeout)
5859
data Log
5960
= LogRegisteringIdeConfig !IdeConfiguration
@@ -67,10 +68,13 @@ data Log
6768
| LogShutDownTimeout Int
6869
| LogServerExitWith (Either () Int)
6970
| LogReactorShutdownConfirmed !T.Text
71+
| LogInitializeIdeStateTookTooLong Seconds
7072
deriving Show
7173

7274
instance Pretty Log where
7375
pretty = \case
76+
LogInitializeIdeStateTookTooLong seconds ->
77+
"Building the initial session took more than" <+> pretty seconds <+> "seconds"
7478
LogReactorShutdownRequested b ->
7579
"Requested reactor shutdown; stop signal posted: " <+> pretty b
7680
LogReactorShutdownConfirmed msg ->
@@ -350,8 +354,8 @@ handleInit initParams env (TRequestMessage _ _ m params) = otTracedHandler "Init
350354
runWithWorkerThreads :: Recorder (WithPriority Session.Log) -> FilePath -> (WithHieDb -> ThreadQueue -> IO ()) -> IO ()
351355
runWithWorkerThreads recorder dbLoc f = evalContT $ do
352356
(WithHieDbShield hiedb, threadQueue) <- runWithDb recorder dbLoc
353-
sessionRestartTQueue <- withWorkerQueueSimple (cmapWithPrio Session.LogSessionWorkerThread recorder) "RestartTQueue"
354-
sessionLoaderTQueue <- withWorkerQueueSimple (cmapWithPrio Session.LogSessionWorkerThread recorder) "SessionLoaderTQueue"
357+
sessionRestartTQueue <- withWorkerQueueSimple (logWith (cmapWithPrio Session.LogSessionWorkerThread recorder) Debug) "RestartTQueue"
358+
sessionLoaderTQueue <- withWorkerQueueSimple (logWith (cmapWithPrio Session.LogSessionWorkerThread recorder) Debug) "SessionLoaderTQueue"
355359
liftIO $ f hiedb (ThreadQueue threadQueue sessionRestartTQueue sessionLoaderTQueue)
356360

357361
-- | Runs the action until it ends or until the given MVar is put.

‎hls-graph/hls-graph.cabal‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,14 @@ library
6565
Development.IDE.Graph.KeyMap
6666
Development.IDE.Graph.KeySet
6767
Development.IDE.Graph.Rule
68+
Development.IDE.WorkerThread
6869
Paths_hls_graph
6970

7071
autogen-modules: Paths_hls_graph
7172
hs-source-dirs: src
7273
build-depends:
74+
, mtl ^>=2.3.1
75+
, safe-exceptions ^>=0.1.7.4
7376
, aeson
7477
, async >=2.0
7578
, base >=4.12 && <5

‎hls-graph/src/Development/IDE/Graph.hs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module Development.IDE.Graph(
1818
-- * Actions for inspecting the keys in the database
1919
getDirtySet,
2020
getKeysAndVisitedAge,
21+
2122
module Development.IDE.Graph.KeyMap,
2223
module Development.IDE.Graph.KeySet,
2324
) where

‎hls-graph/src/Development/IDE/Graph/Database.hs‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ module Development.IDE.Graph.Database(
99
shakeGetDatabaseKeys,
1010
shakeGetDirtySet,
1111
shakeGetCleanKeys
12-
,shakeGetBuildEdges) where
12+
,shakeGetBuildEdges,
13+
shakeShutDatabase) where
1314
import Control.Concurrent.STM.Stats (readTVarIO)
1415
import Data.Dynamic
1516
import Data.Maybe
@@ -21,16 +22,20 @@ import Development.IDE.Graph.Internal.Options
2122
import Development.IDE.Graph.Internal.Profile (writeProfile)
2223
import Development.IDE.Graph.Internal.Rules
2324
import Development.IDE.Graph.Internal.Types
25+
import Development.IDE.WorkerThread (TaskQueue)
2426

2527

2628
-- Placeholder to be the 'extra' if the user doesn't set it
2729
data NonExportedType = NonExportedType
2830

29-
shakeNewDatabase :: ShakeOptions -> Rules () -> IO ShakeDatabase
30-
shakeNewDatabase opts rules = do
31+
shakeShutDatabase :: ShakeDatabase -> IO ()
32+
shakeShutDatabase (ShakeDatabase _ _ db) = shutDatabase db
33+
34+
shakeNewDatabase :: TaskQueue (IO ()) -> ShakeOptions -> Rules () -> IO ShakeDatabase
35+
shakeNewDatabase que opts rules = do
3136
let extra = fromMaybe (toDyn NonExportedType) $ shakeExtra opts
3237
(theRules, actions) <- runRules extra rules
33-
db <- newDatabase extra theRules
38+
db <- newDatabase que extra theRules
3439
pure $ ShakeDatabase (length actions) actions db
3540

3641
shakeRunDatabase :: ShakeDatabase -> [Action a] -> IO [a]

‎hls-graph/src/Development/IDE/Graph/Internal/Database.hs‎

Lines changed: 38 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@ module Development.IDE.Graph.Internal.Database (compute, newDatabase, incDatabas
1212

1313
import Prelude hiding (unzip)
1414

15-
import Control.Concurrent.Async
16-
import Control.Concurrent.Extra
17-
import Control.Concurrent.STM.Stats (STM, TVar, atomically,
15+
import Control.Concurrent.STM.Stats (STM, atomically,
1816
atomicallyNamed,
1917
modifyTVar', newTVarIO,
2018
readTVar, readTVarIO,
@@ -31,28 +29,29 @@ import Data.IORef.Extra
3129
import Data.Maybe
3230
import Data.Traversable (for)
3331
import Data.Tuple.Extra
34-
import Debug.Trace (traceM)
32+
import Debug.Trace (traceEvent)
3533
import Development.IDE.Graph.Classes
3634
import Development.IDE.Graph.Internal.Key
3735
import Development.IDE.Graph.Internal.Rules
3836
import Development.IDE.Graph.Internal.Types
3937
import qualified Focus
4038
import qualified ListT
4139
import qualified StmContainers.Map as SMap
42-
import System.Time.Extra (duration, sleep)
43-
import UnliftIO (MonadUnliftIO (withRunInIO))
44-
import qualified UnliftIO.Exception as UE
40+
import System.Time.Extra (duration)
4541

4642
#if MIN_VERSION_base(4,19,0)
4743
import Data.Functor (unzip)
4844
#else
4945
import Data.List.NonEmpty (unzip)
5046
#endif
47+
import Development.IDE.WorkerThread (TaskQueue,
48+
awaitRunInThreadStmInNewThread)
5149

5250

53-
newDatabase :: Dynamic -> TheRules -> IO Database
54-
newDatabase databaseExtra databaseRules = do
51+
newDatabase :: TaskQueue (IO()) ->Dynamic -> TheRules -> IO Database
52+
newDatabase databaseQueue databaseExtra databaseRules = do
5553
databaseStep <- newTVarIO $ Step 0
54+
databaseThreads <- newTVarIO []
5655
databaseValues <- atomically SMap.new
5756
pure Database{..}
5857

@@ -100,8 +99,9 @@ build db stack keys = do
10099
else throw $ AsyncParentKill i $ Step (-1)
101100
where
102101
go = do
103-
step <- readTVarIO $ databaseStep db
104-
!built <- runAIO step $ builder db stack (fmap newKey keys)
102+
-- step <- readTVarIO $ databaseStep db
103+
-- built <- mapConcurrently (builderOne db stack) (fmap newKey keys)
104+
built <- builder db stack (fmap newKey keys)
105105
let (ids, vs) = unzip built
106106
pure (ids, fmap (asV . resultValue) vs)
107107
where
@@ -112,38 +112,39 @@ build db stack keys = do
112112
-- | Build a list of keys and return their results.
113113
-- If none of the keys are dirty, we can return the results immediately.
114114
-- Otherwise, a blocking computation is returned *which must be evaluated asynchronously* to avoid deadlock.
115-
builder :: (Traversable f) => Database -> Stack -> f Key -> AIO (f (Key, Result))
115+
builder :: (Traversable f) => Database -> Stack -> f Key -> IO (f (Key, Result))
116116
-- builder _ st kk | traceShow ("builder", st,kk) False = undefined
117-
builder db stack keys = do
118-
keyWaits <- for keys $ \k -> builderOne db stack k
119-
!res <- for keyWaits $ \(k, waitR) -> do
120-
!v<- liftIO waitR
121-
return (k, v)
122-
return res
117+
builder db stack keys = for keys $ \k -> builderOne db stack k
123118

124-
builderOne :: Database -> Stack -> Key -> AIO (Key,IO Result)
125-
builderOne db@Database {..} stack id = UE.uninterruptibleMask $\restore ->do
126-
current <- liftIO $ readTVarIO databaseStep
127-
(k, registerWaitResult) <- restore $ liftIO $ atomicallyNamed "builder" $ do
119+
builderOne :: Database -> Stack -> Key -> IO (Key, Result)
120+
builderOne db@Database {..} stack id = do
121+
traceEvent ("builderOne: "++showid) return()
122+
res <- liftIO $ atomicallyNamed "builder" $ do
128123
-- Spawn the id if needed
129124
status <- SMap.lookup id databaseValues
125+
current@(Step cs) <- readTVar databaseStep
126+
let getStep = do
127+
Step current <- readTVar databaseStep
128+
return current
129+
130130
val <- case viewDirty current $ maybe (Dirty Nothing) keyStatus status of
131131
Dirty s -> do
132-
let act =
133-
asyncWithCleanUp
134-
((restore $ refresh db stack id s)
135-
`UE.onException` UE.uninterruptibleMask_ (liftIO (atomicallyNamed "builder - onException" (SMap.focus updateDirty id databaseValues)))
136-
)
137132
SMap.focus (updateStatus $ Running current s) id databaseValues
138-
return act
139-
Clean r -> pure . pure . pure $ r
133+
traceEvent ("Starting build of key: " ++ show id ++ ", step "++ show current)
134+
$ awaitRunInThreadStmInNewThread getStep cs databaseQueue databaseThreads (refresh db stack id s)
135+
$ \e -> atomically $ SMap.focus (updateStatus $ Exception current e s) id databaseValues
136+
return Nothing
137+
Clean r -> return $ Just r
140138
-- force here might contains async exceptions from previous runs
141139
Running _step _s
142140
| memberStack id stack -> throw $ StackException stack
143141
| otherwise -> retry
144-
pure (id, val)
145-
waitR <- registerWaitResult
146-
return (k, waitR)
142+
Exception _ e _s -> throw e
143+
pure val
144+
case res of
145+
Just r -> return (id, r)
146+
Nothing -> builderOne db stack id
147+
147148
-- | isDirty
148149
-- only dirty when it's build time is older than the changed time of one of its dependencies
149150
isDirty :: Foldable t => Result -> t (a, Result) -> Bool
@@ -156,30 +157,27 @@ isDirty me = any (\(_,dep) -> resultBuilt me < resultChanged dep)
156157
-- * If no dirty dependencies and we have evaluated the key previously, then we refresh it in the current thread.
157158
-- This assumes that the implementation will be a lookup
158159
-- * Otherwise, we spawn a new thread to refresh the dirty deps (if any) and the key itself
159-
refreshDeps :: KeySet -> Database -> Stack -> Key -> Result -> [KeySet] -> AIO Result
160+
refreshDeps :: KeySet -> Database -> Stack -> Key -> Result -> [KeySet] -> IO Result
160161
refreshDeps visited db stack key result = \case
161162
-- no more deps to refresh
162-
[] -> compute' db stack key RunDependenciesSame (Just result)
163+
[] -> compute db stack key RunDependenciesSame (Just result)
163164
(dep:deps) -> do
164165
let newVisited = dep <> visited
165166
res <- builder db stack (toListKeySet (dep `differenceKeySet` visited))
166167
if isDirty result res
167168
-- restart the computation if any of the deps are dirty
168-
then compute' db stack key RunDependenciesChanged (Just result)
169+
then compute db stack key RunDependenciesChanged (Just result)
169170
-- else kick the rest of the deps
170171
else refreshDeps newVisited db stack key result deps
171172

172173

173174
-- refresh :: Database -> Stack -> Key -> Maybe Result -> IO Result
174175
-- refresh _ st k _ | traceShow ("refresh", st, k) False = undefined
175-
refresh :: Database -> Stack -> Key -> Maybe Result -> AIO Result
176+
refresh :: Database -> Stack -> Key -> Maybe Result -> IO Result
176177
refresh db stack key result = case (addStack key stack, result) of
177178
(Left e, _) -> throw e
178179
(Right stack, Just me@Result{resultDeps = ResultDeps deps}) -> refreshDeps mempty db stack key me (reverse deps)
179-
(Right stack, _) -> compute' db stack key RunDependenciesChanged result
180-
181-
compute' :: Database -> Stack -> Key -> RunMode -> Maybe Result -> AIO Result
182-
compute' db stack key mode result = liftIO $ compute db stack key mode result
180+
(Right stack, _) -> compute db stack key RunDependenciesChanged result
183181
-- | Compute a key.
184182
compute :: Database -> Stack -> Key -> RunMode -> Maybe Result -> IO Result
185183
-- compute _ st k _ _ | traceShow ("compute", st, k) False = undefined
@@ -284,68 +282,5 @@ transitiveDirtySet database = flip State.execStateT mempty . traverse_ loop
284282
next <- lift $ atomically $ getReverseDependencies database x
285283
traverse_ loop (maybe mempty toListKeySet next)
286284

287-
--------------------------------------------------------------------------------
288-
-- Asynchronous computations with cancellation
289-
290-
-- | A simple monad to implement cancellation on top of 'Async',
291-
-- generalizing 'withAsync' to monadic scopes.
292-
newtype AIO a = AIO { unAIO :: ReaderT (TVar [Async ()]) IO a }
293-
deriving newtype (Applicative, Functor, Monad, MonadIO)
294-
295-
data AsyncParentKill = AsyncParentKill ThreadId Step
296-
deriving (Show, Eq)
297-
298-
instance Exception AsyncParentKill where
299-
toException = asyncExceptionToException
300-
fromException = asyncExceptionFromException
301-
302-
-- | Run the monadic computation, cancelling all the spawned asyncs if an exception arises
303-
runAIO :: Step -> AIO a -> IO a
304-
runAIO s (AIO act) = do
305-
asyncsRef <- newTVarIO []
306-
-- Log the exact exception (including async exceptions) before cleanup,
307-
-- then rethrow to preserve previous semantics.
308-
runReaderT act asyncsRef `onException` do
309-
asyncs <- atomically $ do
310-
r <- readTVar asyncsRef
311-
modifyTVar' asyncsRef $ const []
312-
return r
313-
tid <- myThreadId
314-
cleanupAsync asyncs tid s
315-
316-
-- | Like 'async' but with built-in cancellation.
317-
-- Returns an IO action to wait on the result.
318-
asyncWithCleanUp :: AIO a -> AIO (IO a)
319-
asyncWithCleanUp act = do
320-
st <- AIO ask
321-
io <- unliftAIO act
322-
-- mask to make sure we keep track of the spawned async
323-
liftIO $ uninterruptibleMask $ \restore -> do
324-
a <- async $ restore io
325-
atomically $ modifyTVar' st (void a :)
326-
return $ wait a
327-
328-
unliftAIO :: AIO a -> AIO (IO a)
329-
unliftAIO act = do
330-
st <- AIO ask
331-
return $ runReaderT (unAIO act) st
332285

333-
instance MonadUnliftIO AIO where
334-
withRunInIO k = do
335-
st <- AIO ask
336-
liftIO $ k (\aio -> runReaderT (unAIO aio) st)
337286

338-
cleanupAsync :: [Async a] -> ThreadId -> Step -> IO ()
339-
-- mask to make sure we interrupt all the asyncs
340-
cleanupAsync asyncs tid step = uninterruptibleMask $ \unmask -> do
341-
-- interrupt all the asyncs without waiting
342-
-- mapM_ (\a -> throwTo (asyncThreadId a) AsyncCancelled) asyncs
343-
mapM_ (\a -> throwTo (asyncThreadId a) $ AsyncParentKill tid step) asyncs
344-
-- Wait until all the asyncs are done
345-
-- But if it takes more than 10 seconds, log to stderr
346-
unless (null asyncs) $ do
347-
let warnIfTakingTooLong = unmask $ forever $ do
348-
sleep 10
349-
traceM "cleanupAsync: waiting for asyncs to finish"
350-
withAsync warnIfTakingTooLong $ \_ ->
351-
mapM_ waitCatch asyncs

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /