| ... |
... |
@@ -14,6 +14,8 @@ module GHC.Driver.Downsweep |
|
14
|
14
|
, downsweepFromRootNodes
|
|
15
|
15
|
, downsweepInteractiveImports
|
|
16
|
16
|
, DownsweepMode(..)
|
|
|
17
|
+ , DownsweepM, DownsweepEnv(..)
|
|
|
18
|
+ , runDownsweepM
|
|
17
|
19
|
-- * Summary functions
|
|
18
|
20
|
, summariseModule
|
|
19
|
21
|
, summariseFile
|
| ... |
... |
@@ -62,7 +64,7 @@ import GHC.Data.OsPath ( OsPath, unsafeEncodeUtf ) |
|
62
|
64
|
import GHC.Data.StringBuffer
|
|
63
|
65
|
import GHC.Data.Graph.Directed.Reachability
|
|
64
|
66
|
|
|
65
|
|
-import GHC.Utils.Exception ( throwIO, SomeAsyncException )
|
|
|
67
|
+import GHC.Utils.Exception ( throwIO, SomeAsyncException, AsyncException (..) )
|
|
66
|
68
|
import GHC.Utils.Outputable
|
|
67
|
69
|
import GHC.Utils.Panic
|
|
68
|
70
|
import GHC.Utils.Misc
|
| ... |
... |
@@ -112,8 +114,11 @@ import Control.Monad.Trans.Reader |
|
112
|
114
|
import qualified Data.Map.Strict as M
|
|
113
|
115
|
import Control.Monad.Trans.Class
|
|
114
|
116
|
import System.IO.Unsafe (unsafeInterleaveIO)
|
|
115
|
|
-import Data.IORef
|
|
116
|
117
|
import qualified Data.List.NonEmpty as NE
|
|
|
118
|
+import Control.Concurrent
|
|
|
119
|
+import Control.Concurrent.STM.TQueue
|
|
|
120
|
+import Control.Concurrent.STM
|
|
|
121
|
+import Control.Applicative
|
|
117
|
122
|
|
|
118
|
123
|
{-
|
|
119
|
124
|
Note [The ModuleGraph]
|
| ... |
... |
@@ -176,8 +181,9 @@ incrementally constructing a ModuleGraph using the GHC API; See #27054). So |
|
176
|
181
|
`downsweep` takes a `Maybe ModuleGraph` as one of its arguments.
|
|
177
|
182
|
|
|
178
|
183
|
Downsweep iteratively *expands* each so-called 'DownsweepNode' into a list of
|
|
179
|
|
-its dependencies, and recursively traverses all reachable nodes in a
|
|
180
|
|
-depth-first order using 'dfsBuild'. A 'DownsweepNode' is *expanded* by 'dsNodeExpand':
|
|
|
184
|
+its dependencies, and recursively traverses all reachable nodes in a parallel
|
|
|
185
|
+non-det-depth-first order using 'parDfsBuild'. A 'DownsweepNode' is *expanded*
|
|
|
186
|
+by 'dsNodeExpand':
|
|
181
|
187
|
|
|
182
|
188
|
dsNodeExpand :: DownsweepNode -> DownsweepM (NodeRes (ModuleGraphNode, [DownsweepNode]))
|
|
183
|
189
|
|
| ... |
... |
@@ -256,38 +262,48 @@ downsweep :: HscEnv |
|
256
|
262
|
-- which case there can be repeats
|
|
257
|
263
|
downsweep hsc_env diag_wrapper msg old_summaries maybe_base_graph excl_mods allow_dup_roots = do
|
|
258
|
264
|
n_jobs <- mkWorkerLimit (hsc_dflags hsc_env)
|
|
259
|
|
- summ_cache <- newIORef (mkModSummaryCache (zip old_summaries (repeat SummOld)))
|
|
260
|
|
- imps_cache <- newIORef Map.empty
|
|
261
|
|
- (root_errs, root_summaries) <- rootSummariesParallel n_jobs hsc_env diag_wrapper msg
|
|
262
|
|
- (getRootSummary excl_mods summ_cache imps_cache)
|
|
263
|
|
- let closure_errs = checkHomeUnitsClosed unit_env
|
|
264
|
|
- unit_env = hsc_unit_env hsc_env
|
|
265
|
|
-
|
|
266
|
|
- all_errs = closure_errs ++ root_errs
|
|
267
|
|
-
|
|
268
|
|
- case all_errs of
|
|
269
|
|
- [] -> do
|
|
270
|
|
- (downsweep_errs, downsweep_nodes) <-
|
|
271
|
|
- downsweepFromRootNodes hsc_env summ_cache imps_cache maybe_base_graph
|
|
272
|
|
- excl_mods allow_dup_roots DownsweepUseCompile (map ModuleNodeCompile root_summaries) []
|
|
273
|
|
-
|
|
274
|
|
- let (other_errs, unit_nodes) = partitionEithers $
|
|
275
|
|
- HUG.unitEnv_foldWithKey (\nodes uid hue -> nodes ++ unitModuleNodes downsweep_nodes uid hue) []
|
|
276
|
|
- (hsc_HUG hsc_env)
|
|
277
|
|
-
|
|
278
|
|
- let all_nodes = downsweep_nodes ++ unit_nodes
|
|
279
|
|
- let all_errs = downsweep_errs ++ other_errs
|
|
280
|
|
-
|
|
281
|
|
- let logger = hsc_logger hsc_env
|
|
282
|
|
- tmpfs = hsc_tmpfs hsc_env
|
|
283
|
|
- -- if we have been passed -fno-code, we enable code generation
|
|
284
|
|
- -- for dependencies of modules that have -XTemplateHaskell,
|
|
285
|
|
- -- otherwise those modules will fail to compile.
|
|
286
|
|
- -- See Note [-fno-code mode] #8025
|
|
287
|
|
- th_configured_nodes <- enableCodeGenForTH logger tmpfs unit_env all_nodes
|
|
288
|
|
-
|
|
289
|
|
- return (all_errs, th_configured_nodes)
|
|
290
|
|
- _ -> return (all_errs, emptyMG)
|
|
|
265
|
+ summ_cache <- newMVar (mkModSummaryCache (zip old_summaries (repeat SummOld)))
|
|
|
266
|
+ imps_cache <- newMVar Map.empty
|
|
|
267
|
+ withMakeEnv n_jobs hsc_env diag_wrapper msg $ \make_env -> do
|
|
|
268
|
+ (root_errs, root_summaries) <- rootSummariesParallel n_jobs make_env (hsc_targets hsc_env)
|
|
|
269
|
+ (getRootSummary excl_mods summ_cache imps_cache)
|
|
|
270
|
+ let closure_errs = checkHomeUnitsClosed unit_env
|
|
|
271
|
+ unit_env = hsc_unit_env hsc_env
|
|
|
272
|
+
|
|
|
273
|
+ all_errs = closure_errs ++ root_errs
|
|
|
274
|
+
|
|
|
275
|
+ case all_errs of
|
|
|
276
|
+ [] -> do
|
|
|
277
|
+ let env = DownsweepEnv
|
|
|
278
|
+ { ds_hsc_env = hsc_env
|
|
|
279
|
+ , ds_summaries_cache = summ_cache
|
|
|
280
|
+ , ds_imports_cache = imps_cache
|
|
|
281
|
+ , ds_mode = DownsweepUseCompile
|
|
|
282
|
+ , ds_excl_mods = excl_mods
|
|
|
283
|
+ , ds_n_jobs = n_jobs
|
|
|
284
|
+ , ds_make_env = make_env
|
|
|
285
|
+ }
|
|
|
286
|
+ (downsweep_errs, downsweep_nodes) <- runDownsweepM env $
|
|
|
287
|
+ downsweepFromRootNodes maybe_base_graph allow_dup_roots
|
|
|
288
|
+ (map ModuleNodeCompile root_summaries) []
|
|
|
289
|
+
|
|
|
290
|
+ let (other_errs, unit_nodes) = partitionEithers $
|
|
|
291
|
+ HUG.unitEnv_foldWithKey (\nodes uid hue -> nodes ++ unitModuleNodes downsweep_nodes uid hue) []
|
|
|
292
|
+ (hsc_HUG hsc_env)
|
|
|
293
|
+
|
|
|
294
|
+ let all_nodes = downsweep_nodes ++ unit_nodes
|
|
|
295
|
+ let all_errs = downsweep_errs ++ other_errs
|
|
|
296
|
+
|
|
|
297
|
+ let logger = hsc_logger hsc_env
|
|
|
298
|
+ tmpfs = hsc_tmpfs hsc_env
|
|
|
299
|
+ -- if we have been passed -fno-code, we enable code generation
|
|
|
300
|
+ -- for dependencies of modules that have -XTemplateHaskell,
|
|
|
301
|
+ -- otherwise those modules will fail to compile.
|
|
|
302
|
+ -- See Note [-fno-code mode] #8025
|
|
|
303
|
+ th_configured_nodes <- enableCodeGenForTH logger tmpfs unit_env all_nodes
|
|
|
304
|
+
|
|
|
305
|
+ return (all_errs, th_configured_nodes)
|
|
|
306
|
+ _ -> return (all_errs, emptyMG)
|
|
291
|
307
|
where
|
|
292
|
308
|
-- Dependencies arising on a unit (backpack and module linking deps)
|
|
293
|
309
|
unitModuleNodes :: [ModuleGraphNode] -> UnitId -> HomeUnitEnv -> [Either (Messages DriverMessage) ModuleGraphNode]
|
| ... |
... |
@@ -330,15 +346,28 @@ downsweep hsc_env diag_wrapper msg old_summaries maybe_base_graph excl_mods allo |
|
330
|
346
|
downsweepThunk :: HscEnv -> ModSummary -> IO ModuleGraph
|
|
331
|
347
|
downsweepThunk hsc_env mod_summary = unsafeInterleaveIO $ do
|
|
332
|
348
|
debugTraceMsg (hsc_logger hsc_env) 3 $ text "Computing Module Graph thunk..."
|
|
333
|
|
- summs <- newIORef (mkModSummaryCache [(mod_summary,SummOld)])
|
|
334
|
|
- imps <- newIORef mempty
|
|
335
|
|
- ~(errs, mg) <- downsweepFromRootNodes hsc_env summs imps Nothing [] True DownsweepUseFixed [ModuleNodeCompile mod_summary] []
|
|
336
|
|
- let dflags = hsc_dflags hsc_env
|
|
337
|
|
- liftIO $ printOrThrowDiagnostics (hsc_logger hsc_env)
|
|
338
|
|
- (initPrintConfig dflags)
|
|
339
|
|
- (initDiagOpts dflags)
|
|
340
|
|
- (GhcDriverMessage <$> unionManyMessages errs)
|
|
341
|
|
- return (mkModuleGraph mg)
|
|
|
349
|
+ njobs <- mkWorkerLimit (hsc_dflags hsc_env)
|
|
|
350
|
+ summs <- newMVar (mkModSummaryCache [(mod_summary,SummOld)])
|
|
|
351
|
+ imps <- newMVar mempty
|
|
|
352
|
+ withMakeEnv njobs hsc_env mkUnknownDiagnostic Nothing $ \make_env -> do
|
|
|
353
|
+ let env = DownsweepEnv
|
|
|
354
|
+ { ds_hsc_env = hsc_env
|
|
|
355
|
+ , ds_summaries_cache = summs
|
|
|
356
|
+ , ds_imports_cache = imps
|
|
|
357
|
+ , ds_mode = DownsweepUseFixed
|
|
|
358
|
+ , ds_excl_mods = []
|
|
|
359
|
+ , ds_n_jobs = njobs
|
|
|
360
|
+ , ds_make_env = make_env
|
|
|
361
|
+ }
|
|
|
362
|
+ ~(errs, mg) <- runDownsweepM env $
|
|
|
363
|
+ downsweepFromRootNodes Nothing True
|
|
|
364
|
+ [ModuleNodeCompile mod_summary] []
|
|
|
365
|
+ let dflags = hsc_dflags hsc_env
|
|
|
366
|
+ liftIO $ printOrThrowDiagnostics (hsc_logger hsc_env)
|
|
|
367
|
+ (initPrintConfig dflags)
|
|
|
368
|
+ (initDiagOpts dflags)
|
|
|
369
|
+ (GhcDriverMessage <$> unionManyMessages errs)
|
|
|
370
|
+ return (mkModuleGraph mg)
|
|
342
|
371
|
|
|
343
|
372
|
-- | Construct a module graph starting from the interactive context.
|
|
344
|
373
|
-- Produces, a thunk, which when forced will perform the downsweep.
|
| ... |
... |
@@ -362,13 +391,23 @@ downsweepInteractiveImports hsc_env ic = unsafeInterleaveIO $ do |
|
362
|
391
|
-- :load. Any home package modules need to already be in here.
|
|
363
|
392
|
let cached_nodes = Map.fromList [ (mkNodeKey n, NSuccess n) | n <- mg_mss (hsc_mod_graph hsc_env) ]
|
|
364
|
393
|
|
|
365
|
|
- summ_cache <- newIORef mempty
|
|
366
|
|
- imps_cache <- newIORef mempty
|
|
367
|
|
- let env = DownsweepEnv hsc_env DownsweepUseFixed{-or DownsweepUseCompile?-} summ_cache imps_cache []
|
|
368
|
|
- graph <- runDownsweepM env do
|
|
369
|
|
- loopFromInteractive cached_nodes interactive_mn imps
|
|
370
|
|
- let all_nodes = [s | NSuccess s <- M.elems graph ]
|
|
371
|
|
- return $ mkModuleGraph all_nodes
|
|
|
394
|
+ n_jobs <- mkWorkerLimit (hsc_dflags hsc_env)
|
|
|
395
|
+ summ_cache <- newMVar mempty
|
|
|
396
|
+ imps_cache <- newMVar mempty
|
|
|
397
|
+ withMakeEnv n_jobs hsc_env mkUnknownDiagnostic Nothing $ \make_env -> do
|
|
|
398
|
+ let env = DownsweepEnv
|
|
|
399
|
+ { ds_hsc_env = hsc_env
|
|
|
400
|
+ , ds_mode = DownsweepUseFixed{-or DownsweepUseCompile?-}
|
|
|
401
|
+ , ds_summaries_cache = summ_cache
|
|
|
402
|
+ , ds_imports_cache = imps_cache
|
|
|
403
|
+ , ds_excl_mods = []
|
|
|
404
|
+ , ds_n_jobs = n_jobs
|
|
|
405
|
+ , ds_make_env = make_env
|
|
|
406
|
+ }
|
|
|
407
|
+ graph <- runDownsweepM env do
|
|
|
408
|
+ loopFromInteractive cached_nodes interactive_mn imps
|
|
|
409
|
+ let all_nodes = [s | NSuccess s <- M.elems graph ]
|
|
|
410
|
+ return $ mkModuleGraph all_nodes
|
|
372
|
411
|
|
|
373
|
412
|
-- | Create a module graph from a list of installed modules.
|
|
374
|
413
|
-- This is used by the loader when we need to load modules but there
|
| ... |
... |
@@ -396,26 +435,38 @@ downsweepInstalledModules hsc_env mods = do |
|
396
|
435
|
-- already know that we can find the modules we need to load.
|
|
397
|
436
|
_ -> throwGhcException $ ProgramError $ showSDoc (hsc_dflags hsc_env) $ text "downsweepInstalledModules: Could not find installed module" <+> ppr i
|
|
398
|
437
|
|
|
|
438
|
+ njobs <- mkWorkerLimit (hsc_dflags hsc_env)
|
|
399
|
439
|
nodes <- mapM process installed_mods
|
|
400
|
|
- summs <- newIORef mempty
|
|
401
|
|
- imps <- newIORef mempty
|
|
402
|
|
- (errs, mg) <- downsweepFromRootNodes hsc_env summs imps Nothing [] True DownsweepUseFixed nodes external_uids
|
|
|
440
|
+ summs <- newMVar mempty
|
|
|
441
|
+ imps <- newMVar mempty
|
|
|
442
|
+ withMakeEnv njobs hsc_env mkUnknownDiagnostic Nothing $ \make_env -> do
|
|
|
443
|
+ let env = DownsweepEnv
|
|
|
444
|
+ { ds_hsc_env = hsc_env
|
|
|
445
|
+ , ds_summaries_cache = summs
|
|
|
446
|
+ , ds_imports_cache = imps
|
|
|
447
|
+ , ds_mode = DownsweepUseFixed
|
|
|
448
|
+ , ds_excl_mods = []
|
|
|
449
|
+ , ds_n_jobs = njobs
|
|
|
450
|
+ , ds_make_env = make_env
|
|
|
451
|
+ }
|
|
|
452
|
+ (errs, mg) <- runDownsweepM env $
|
|
|
453
|
+ downsweepFromRootNodes Nothing True nodes external_uids
|
|
403
|
454
|
|
|
404
|
|
- -- Similarly here, we should really not get any errors, but print them out if we do.
|
|
405
|
|
- let dflags = hsc_dflags hsc_env
|
|
406
|
|
- liftIO $ printOrThrowDiagnostics (hsc_logger hsc_env)
|
|
407
|
|
- (initPrintConfig dflags)
|
|
408
|
|
- (initDiagOpts dflags)
|
|
409
|
|
- (GhcDriverMessage <$> unionManyMessages errs)
|
|
|
455
|
+ -- Similarly here, we should really not get any errors, but print them out if we do.
|
|
|
456
|
+ let dflags = hsc_dflags hsc_env
|
|
|
457
|
+ liftIO $ printOrThrowDiagnostics (hsc_logger hsc_env)
|
|
|
458
|
+ (initPrintConfig dflags)
|
|
|
459
|
+ (initDiagOpts dflags)
|
|
|
460
|
+ (GhcDriverMessage <$> unionManyMessages errs)
|
|
410
|
461
|
|
|
411
|
|
- return (mkModuleGraph mg)
|
|
|
462
|
+ return (mkModuleGraph mg)
|
|
412
|
463
|
|
|
413
|
464
|
-----------------------------------------------------------------------------
|
|
414
|
465
|
-- * Orchestrator: downsweepFromRootNodes
|
|
415
|
466
|
-----------------------------------------------------------------------------
|
|
416
|
467
|
|
|
417
|
|
-type ModSummaryCache = IORef ModSummaryCacheMap
|
|
418
|
|
-type ImportsCache = IORef ImportsCacheMap
|
|
|
468
|
+type ModSummaryCache = MVar ModSummaryCacheMap
|
|
|
469
|
+type ImportsCache = MVar ImportsCacheMap
|
|
419
|
470
|
|
|
420
|
471
|
-- | A cache from file paths to the already summarised modules. The same file
|
|
421
|
472
|
-- can be used in multiple units so the map is actually also keyed by which
|
| ... |
... |
@@ -450,30 +501,26 @@ data DownsweepMode = DownsweepUseCompile | DownsweepUseFixed |
|
450
|
501
|
-- 'UnitId's.
|
|
451
|
502
|
-- This function will start at the given roots, and traverse downwards to find
|
|
452
|
503
|
-- all the dependencies, all the way to the leaf units.
|
|
453
|
|
-downsweepFromRootNodes :: HscEnv
|
|
454
|
|
- -> ModSummaryCache
|
|
455
|
|
- -> ImportsCache
|
|
456
|
|
- -> Maybe ModuleGraph
|
|
457
|
|
- -> [ModuleName]
|
|
458
|
|
- -> Bool
|
|
459
|
|
- -> DownsweepMode -- ^ Whether to create fixed or compile nodes for dependencies
|
|
460
|
|
- -> [ModuleNodeInfo] -- ^ The starting ModuleNodeInfo
|
|
461
|
|
- -> [UnitId] -- ^ The starting units
|
|
462
|
|
- -> IO ([DriverMessages], [ModuleGraphNode])
|
|
463
|
|
-downsweepFromRootNodes hsc_env summ_cache imps_cache maybe_base_graph excl_mods allow_dup_roots mode root_nodes root_uids = do
|
|
|
504
|
+downsweepFromRootNodes
|
|
|
505
|
+ :: Maybe ModuleGraph
|
|
|
506
|
+ -> Bool
|
|
|
507
|
+ -> [ModuleNodeInfo] -- ^ The starting ModuleNodeInfo
|
|
|
508
|
+ -> [UnitId] -- ^ The starting units
|
|
|
509
|
+ -> DownsweepM ([DriverMessages], [ModuleGraphNode])
|
|
|
510
|
+downsweepFromRootNodes maybe_base_graph allow_dup_roots root_nodes root_uids =
|
|
|
511
|
+ ReaderT $ \env@DownsweepEnv{..} -> do
|
|
464
|
512
|
when (not allow_dup_roots) $
|
|
465
|
513
|
case root_duplicates of
|
|
466
|
514
|
[] -> return ()
|
|
467
|
|
- (dup_root:_) -> multiRootsErr sec dup_root
|
|
468
|
|
- modifyImpsCache imps_cache (`M.union` mkRootMap root_nodes) -- add root nodes to imports cache
|
|
469
|
|
- let env = DownsweepEnv hsc_env mode summ_cache imps_cache excl_mods
|
|
470
|
|
- deps' <- runDownsweepM env $ do
|
|
|
515
|
+ (dup_root:_) -> multiRootsErr (sec ds_hsc_env) dup_root
|
|
|
516
|
+ modifyImpsCache ds_imports_cache (`M.union` mkRootMap root_nodes) -- add root nodes to imports cache
|
|
|
517
|
+ deps' <- runDownsweepM env $ do
|
|
471
|
518
|
let base_nodes = maybe M.empty moduleGraphNodeMap maybe_base_graph
|
|
472
|
519
|
module_deps <- loopModuleNodeInfos base_nodes root_nodes
|
|
473
|
|
- all_deps <- loopUnits module_deps (hscActiveUnitId hsc_env) root_uids
|
|
474
|
|
- deps' <- loopInstantiations all_deps (getHomeUnitInstantiations hsc_env)
|
|
|
520
|
+ all_deps <- loopUnits module_deps (hscActiveUnitId ds_hsc_env) root_uids
|
|
|
521
|
+ deps' <- loopInstantiations all_deps (getHomeUnitInstantiations ds_hsc_env)
|
|
475
|
522
|
return deps'
|
|
476
|
|
- f_cache <- readIORef summ_cache
|
|
|
523
|
+ f_cache <- readMVar ds_summaries_cache
|
|
477
|
524
|
let downsweep_errs = lefts (M.elems f_cache)
|
|
478
|
525
|
downsweep_nodes = [ s | NSuccess s <- M.elems deps' ]
|
|
479
|
526
|
|
| ... |
... |
@@ -501,7 +548,7 @@ downsweepFromRootNodes hsc_env summ_cache imps_cache maybe_base_graph excl_mods |
|
501
|
548
|
moduleGraphNodeMap graph
|
|
502
|
549
|
= M.fromList [(mkNodeKey node, NSuccess node) | node <- mgModSummaries' graph]
|
|
503
|
550
|
|
|
504
|
|
- sec = initSourceErrorContext (hsc_dflags hsc_env)
|
|
|
551
|
+ sec hsc_env = initSourceErrorContext (hsc_dflags hsc_env)
|
|
505
|
552
|
|
|
506
|
553
|
--------------------------------------------------------------------------------
|
|
507
|
554
|
-- ** 'DownsweepM'
|
| ... |
... |
@@ -509,11 +556,14 @@ downsweepFromRootNodes hsc_env summ_cache imps_cache maybe_base_graph excl_mods |
|
509
|
556
|
|
|
510
|
557
|
type DownsweepM a = ReaderT DownsweepEnv IO a
|
|
511
|
558
|
data DownsweepEnv = DownsweepEnv {
|
|
512
|
|
- downsweep_hsc_env :: HscEnv
|
|
513
|
|
- , _downsweep_mode :: DownsweepMode
|
|
514
|
|
- , _downsweep_summaries_cache :: ModSummaryCache
|
|
515
|
|
- , downsweep_imports_cache :: ImportsCache
|
|
516
|
|
- , _downsweep_excl_mods :: [ModuleName]
|
|
|
559
|
+ ds_hsc_env :: HscEnv
|
|
|
560
|
+ , ds_mode :: DownsweepMode
|
|
|
561
|
+ -- ^ Whether to create fixed or compile nodes for dependencies
|
|
|
562
|
+ , ds_summaries_cache :: ModSummaryCache
|
|
|
563
|
+ , ds_imports_cache :: ImportsCache
|
|
|
564
|
+ , ds_excl_mods :: [ModuleName]
|
|
|
565
|
+ , ds_n_jobs :: WorkerLimit
|
|
|
566
|
+ , ds_make_env :: MakeEnv
|
|
517
|
567
|
}
|
|
518
|
568
|
|
|
519
|
569
|
mkModSummaryCache :: [(ModSummary, SummProvenance)] -> ModSummaryCacheMap
|
| ... |
... |
@@ -529,8 +579,8 @@ addModSummaryCache ms pr fe = upd_fe fe |
|
529
|
579
|
|
|
530
|
580
|
modifySummCache :: ModSummaryCache -> (ModSummaryCacheMap -> ModSummaryCacheMap) -> IO ()
|
|
531
|
581
|
modifyImpsCache :: ImportsCache -> (ImportsCacheMap -> ImportsCacheMap) -> IO ()
|
|
532
|
|
-modifySummCache r f = atomicModifyIORef' r (\c -> (f c, ()))
|
|
533
|
|
-modifyImpsCache r f = atomicModifyIORef' r (\c -> (f c, ()))
|
|
|
582
|
+modifySummCache v f = modifyMVar v (\c -> let !r = f c in pure (r, ()))
|
|
|
583
|
+modifyImpsCache v f = modifyMVar v (\c -> let !r = f c in pure (r, ()))
|
|
534
|
584
|
|
|
535
|
585
|
-- | A cache from a module import (in given home unit context, with a package
|
|
536
|
586
|
-- qualifier, and the imported module name (with or without SOURCE)) to the
|
| ... |
... |
@@ -553,7 +603,7 @@ loopModuleNodeInfos :: M.Map NodeKey (NodeRes ModuleGraphNode) -> [ModuleNodeInf |
|
553
|
603
|
loopUnits :: M.Map NodeKey (NodeRes ModuleGraphNode) -> UnitId -> [UnitId] -> DownsweepM (M.Map NodeKey (NodeRes ModuleGraphNode))
|
|
554
|
604
|
loopInstantiations :: M.Map NodeKey (NodeRes ModuleGraphNode) -> [(UnitId, InstantiatedUnit)] -> DownsweepM (M.Map NodeKey (NodeRes ModuleGraphNode))
|
|
555
|
605
|
loopFromInteractive :: M.Map NodeKey (NodeRes ModuleGraphNode) -> Module -> [InteractiveImport] -> DownsweepM (M.Map NodeKey (NodeRes ModuleGraphNode))
|
|
556
|
|
-loopDownsweepNodes base_map nodes = dfsBuild (Just base_map) nodes dsNodeInfoKey dsNodeExpand
|
|
|
606
|
+loopDownsweepNodes base_map nodes = parDfsBuild (Just base_map) nodes dsNodeInfoKey dsNodeExpand
|
|
557
|
607
|
loopModuleNodeInfos base_map = loopDownsweepNodes base_map . map DSMod
|
|
558
|
608
|
loopUnits base_map homud = loopDownsweepNodes base_map . map (DSUnit homud)
|
|
559
|
609
|
loopInstantiations base_map = loopDownsweepNodes base_map . map (uncurry DSInst)
|
| ... |
... |
@@ -617,7 +667,7 @@ dsNodeExpand = \case |
|
617
|
667
|
|
|
618
|
668
|
expandModuleSummary :: ModSummary -> DownsweepM (NodeRes (ModuleGraphNode, [DownsweepNode]))
|
|
619
|
669
|
expandModuleSummary ms = do -- Didn't work out what the imports mean yet, now do that.
|
|
620
|
|
- hsc_env <- asks downsweep_hsc_env
|
|
|
670
|
+ hsc_env <- asks ds_hsc_env
|
|
621
|
671
|
let home_uid = ms_unitid ms
|
|
622
|
672
|
home_unit = ue_unitHomeUnit home_uid (hsc_unit_env hsc_env)
|
|
623
|
673
|
(final_deps, todo) <- unzip <$> mapM (expandModImport home_uid home_unit) (calcDeps ms)
|
| ... |
... |
@@ -652,7 +702,7 @@ expandModuleSummary ms = do -- Didn't work out what the imports mean yet, now do |
|
652
|
702
|
FoundHomeWithError (_uid, _e) -> return
|
|
653
|
703
|
( Nothing, [] )
|
|
654
|
704
|
-- the error @e@ is already stored in the summarisation cache,
|
|
655
|
|
- -- (the IORef in DownsweepM) and will get reported at the end.
|
|
|
705
|
+ -- (the MVar in DownsweepM) and will get reported at the end.
|
|
656
|
706
|
FoundHome s -> return
|
|
657
|
707
|
-- MP: This assumes that we can only instantiate non home units, which is probably fair enough for now.
|
|
658
|
708
|
( Just $ mkModuleEdge lvl (NodeKey_Module (mnKey s))
|
| ... |
... |
@@ -673,7 +723,7 @@ expandModuleSummary ms = do -- Didn't work out what the imports mean yet, now do |
|
673
|
723
|
-- NB: If you ever reach a Fixed node, everything under that also must be fixed.
|
|
674
|
724
|
expandFixedModuleNode :: ModNodeKeyWithUid -> ModLocation -> DownsweepM (NodeRes (ModuleGraphNode, [DownsweepNode]))
|
|
675
|
725
|
expandFixedModuleNode key loc = do
|
|
676
|
|
- hsc_env <- asks downsweep_hsc_env
|
|
|
726
|
+ hsc_env <- asks ds_hsc_env
|
|
677
|
727
|
-- MP: TODO, we should just read the dependency info from the interface rather than either
|
|
678
|
728
|
-- a. Loading the whole thing into the EPS (this might never nececssary and causes lots of things to be permanently loaded into memory)
|
|
679
|
729
|
-- b. Loading the whole interface into a buffer before discarding it. (wasted allocation and deserialisation)
|
| ... |
... |
@@ -732,7 +782,7 @@ expandUnitNode :: UnitId {-^ @node_uid@ -} -> UnitId {-^ Home unit from where @n |
|
732
|
782
|
expandUnitNode node_uid home_context_uid = do
|
|
733
|
783
|
-- Set active unit so that looking loopUnit finds the correct
|
|
734
|
784
|
-- -package flags in the unit state.
|
|
735
|
|
- hsc_env <- asks downsweep_hsc_env
|
|
|
785
|
+ hsc_env <- asks ds_hsc_env
|
|
736
|
786
|
let lcl_hsc_env = hscSetActiveUnitId home_context_uid hsc_env
|
|
737
|
787
|
case unitDepends <$> lookupUnitId (hsc_units lcl_hsc_env) node_uid of
|
|
738
|
788
|
Just us -> pure $ NSuccess ((UnitNode us node_uid), map (\u -> DSUnit{node_uid=u, home_context_uid{-inherit-}}) us)
|
| ... |
... |
@@ -745,8 +795,8 @@ expandInstantiatedUnit iud home_uid = pure $ NSuccess |
|
745
|
795
|
|
|
746
|
796
|
expandInteractiveImports :: Module -> [InteractiveImport] -> DownsweepM (NodeRes (ModuleGraphNode, [DownsweepNode]))
|
|
747
|
797
|
expandInteractiveImports imod imps = do
|
|
748
|
|
- hsc_env <- asks downsweep_hsc_env
|
|
749
|
|
- imps_cache <- asks downsweep_imports_cache
|
|
|
798
|
+ hsc_env <- asks ds_hsc_env
|
|
|
799
|
+ imps_cache <- asks ds_imports_cache
|
|
750
|
800
|
|
|
751
|
801
|
let
|
|
752
|
802
|
-- A simple edge to a module from the same home unit
|
| ... |
... |
@@ -807,13 +857,13 @@ downsweepSummarise :: HomeUnit |
|
807
|
857
|
-> Maybe (StringBuffer, UTCTime)
|
|
808
|
858
|
-> DownsweepM SummariseResult
|
|
809
|
859
|
downsweepSummarise home_unit imp maybe_buf = do
|
|
810
|
|
- DownsweepEnv hsc_env mode summaries_cache_ref imports_cache_ref excl_mods <- ask
|
|
811
|
|
- liftIO $ case mode of
|
|
|
860
|
+ DownsweepEnv{..} <- ask
|
|
|
861
|
+ liftIO $ case ds_mode of
|
|
812
|
862
|
DownsweepUseCompile ->
|
|
813
|
|
- summariseModule hsc_env home_unit summaries_cache_ref imports_cache_ref
|
|
814
|
|
- imp maybe_buf excl_mods
|
|
|
863
|
+ summariseModule ds_hsc_env home_unit ds_summaries_cache ds_imports_cache
|
|
|
864
|
+ imp maybe_buf ds_excl_mods
|
|
815
|
865
|
DownsweepUseFixed ->
|
|
816
|
|
- summariseModuleInterface hsc_env home_unit imports_cache_ref imp excl_mods
|
|
|
866
|
+ summariseModuleInterface ds_hsc_env home_unit ds_imports_cache imp ds_excl_mods
|
|
817
|
867
|
|
|
818
|
868
|
multiRootsErr :: SourceErrorContext -> NE.NonEmpty ModuleNodeInfo -> IO ()
|
|
819
|
869
|
multiRootsErr sec (summ1 NE.:| summs)
|
| ... |
... |
@@ -878,56 +928,15 @@ getRootSummary excl_mods summ_cache imports_cache hsc_env target |
|
878
|
928
|
rootLoc = mkGeneralSrcSpan (fsLit "<command line>")
|
|
879
|
929
|
dflags = homeUnitEnv_dflags (ue_findHomeUnitEnv uid (hsc_unit_env hsc_env))
|
|
880
|
930
|
|
|
881
|
|
--- | Execute 'getRootSummary' for the 'Target's using the parallelism pipeline
|
|
882
|
|
--- system.
|
|
883
|
|
--- Create bundles of 'Target's wrapped in a 'MakeAction' that uses
|
|
884
|
|
--- 'withAbstractSem' to wait for a free slot, limiting the number of
|
|
885
|
|
--- concurrently computed summaries to the value of the @-j@ option or the slots
|
|
886
|
|
--- allocated by the job server, if that is used.
|
|
887
|
|
---
|
|
888
|
|
--- The 'MakeAction' returns 'Maybe', which is not handled as an error, because
|
|
889
|
|
--- 'runLoop' only sets it to 'Nothing' when an exception was thrown, so the
|
|
890
|
|
--- result won't be read anyway here.
|
|
891
|
|
---
|
|
892
|
|
--- To emulate the current behavior, we funnel exceptions past the concurrency
|
|
893
|
|
--- barrier and rethrow the first one afterwards.
|
|
894
|
|
-rootSummariesParallel ::
|
|
895
|
|
- WorkerLimit ->
|
|
896
|
|
- HscEnv ->
|
|
897
|
|
- (GhcMessage -> AnyGhcDiagnostic) ->
|
|
898
|
|
- Maybe Messager ->
|
|
899
|
|
- (HscEnv -> Target -> IO (Either DriverMessages ModSummary)) ->
|
|
900
|
|
- IO ([DriverMessages], [ModSummary])
|
|
901
|
|
-rootSummariesParallel n_jobs hsc_env diag_wrapper msg get_summary = do
|
|
902
|
|
- (actions, get_results) <- unzip <$> mapM action_and_result (zip [1..] bundles)
|
|
903
|
|
- runPipelines n_jobs hsc_env diag_wrapper msg actions
|
|
904
|
|
- (sequence . catMaybes <$> sequence get_results) >>= \case
|
|
905
|
|
- Right results -> pure (partitionEithers (concat results))
|
|
906
|
|
- Left exc -> throwIO exc
|
|
907
|
|
- where
|
|
908
|
|
- bundles = mk_bundles targets
|
|
909
|
|
-
|
|
910
|
|
- mk_bundles = unfoldr \case
|
|
911
|
|
- [] -> Nothing
|
|
912
|
|
- ts -> Just (splitAt bundle_size ts)
|
|
913
|
|
-
|
|
914
|
|
- bundle_size = 20
|
|
915
|
|
-
|
|
916
|
|
- targets = hsc_targets hsc_env
|
|
917
|
|
-
|
|
918
|
|
- action_and_result (log_queue_id, ts) = do
|
|
919
|
|
- res_var <- liftIO newEmptyMVar
|
|
920
|
|
- pure $! (MakeAction (action log_queue_id ts) res_var, readMVar res_var)
|
|
921
|
|
-
|
|
922
|
|
- action log_queue_id target_bundle = do
|
|
923
|
|
- env@MakeEnv {compile_sem} <- ask
|
|
924
|
|
- lift $ lift $
|
|
925
|
|
- withAbstractSem compile_sem $
|
|
926
|
|
- withLoggerHsc log_queue_id env \ lcl_hsc_env ->
|
|
927
|
|
- MC.try (mapM (get_summary lcl_hsc_env) target_bundle) >>= \case
|
|
928
|
|
- Left e | Just (_ :: SomeAsyncException) <- fromException e ->
|
|
929
|
|
- throwIO e
|
|
930
|
|
- a -> pure a
|
|
|
931
|
+-- | Execute 'getRootSummary' for the 'Target's using the parallelism pipeline system.
|
|
|
932
|
+rootSummariesParallel
|
|
|
933
|
+ :: WorkerLimit -> MakeEnv -> [Target]
|
|
|
934
|
+ -> (HscEnv -> Target -> IO (Either DriverMessages ModSummary))
|
|
|
935
|
+ -> IO ([DriverMessages], [ModSummary])
|
|
|
936
|
+rootSummariesParallel n_jobs make_env targets get_summary = do
|
|
|
937
|
+ partitionEithers <$> mapConcDS n_jobs bundle_size make_env get_summary targets
|
|
|
938
|
+ where
|
|
|
939
|
+ bundle_size = 20
|
|
931
|
940
|
|
|
932
|
941
|
--------------------------------------------------------------------------------
|
|
933
|
942
|
-- * Check/validate properties and error out
|
| ... |
... |
@@ -1325,7 +1334,7 @@ summariseFile |
|
1325
|
1334
|
-> IO (Either DriverMessages ModSummary)
|
|
1326
|
1335
|
|
|
1327
|
1336
|
summariseFile hsc_env' home_unit summ_cache_ref src_fn mb_phase maybe_buf
|
|
1328
|
|
- = do file_summ_cache <- readIORef summ_cache_ref
|
|
|
1337
|
+ = do file_summ_cache <- readMVar summ_cache_ref
|
|
1329
|
1338
|
case M.lookup (homeUnitId home_unit, src_fn_os) file_summ_cache of
|
|
1330
|
1339
|
Just (Right (chd_summary, SummFresh)) ->
|
|
1331
|
1340
|
-- Fresh: use it straight away
|
| ... |
... |
@@ -1505,7 +1514,7 @@ summariseModuleDispatch k hsc_env' imps_cache_ref home_unit imp excl_mods |
|
1505
|
1514
|
|
|
1506
|
1515
|
find_it :: IO SummariseResult
|
|
1507
|
1516
|
find_it = do
|
|
1508
|
|
- imps_cache <- readIORef imps_cache_ref
|
|
|
1517
|
+ imps_cache <- readMVar imps_cache_ref
|
|
1509
|
1518
|
case M.lookup cache_key imps_cache of
|
|
1510
|
1519
|
Just result -> return result
|
|
1511
|
1520
|
Nothing -> do
|
| ... |
... |
@@ -1547,7 +1556,7 @@ summariseModuleWithSource home_unit summ_cache_ref is_boot maybe_buf hsc_env loc |
|
1547
|
1556
|
-- Adjust location to point to the hs-boot source file,
|
|
1548
|
1557
|
-- hi file, object file, when is_boot says so
|
|
1549
|
1558
|
let src_fn = expectJust (ml_hs_file location)
|
|
1550
|
|
- summ_cache <- readIORef summ_cache_ref
|
|
|
1559
|
+ summ_cache <- readMVar summ_cache_ref
|
|
1551
|
1560
|
|
|
1552
|
1561
|
-- Reject the cache result if the module name doesn't match the inferred
|
|
1553
|
1562
|
-- module name based on the file name.
|
| ... |
... |
@@ -1722,10 +1731,10 @@ getPreprocessedImports hsc_env src_fn mb_phase maybe_buf = do |
|
1722
|
1731
|
return PreprocessedImports {..}
|
|
1723
|
1732
|
|
|
1724
|
1733
|
--------------------------------------------------------------------------------
|
|
1725
|
|
--- * Generic traversal of iteratively-built graph: dfsBuild
|
|
|
1734
|
+-- * Generic traversal of iteratively-built graph: parDfsBuild
|
|
1726
|
1735
|
--------------------------------------------------------------------------------
|
|
1727
|
1736
|
|
|
1728
|
|
--- | The result of expanding a node in 'dfsBuild'.
|
|
|
1737
|
+-- | The result of expanding a node in 'parDfsBuild'.
|
|
1729
|
1738
|
data NodeRes v
|
|
1730
|
1739
|
-- | Computed the node payload successfully
|
|
1731
|
1740
|
= NSuccess v
|
| ... |
... |
@@ -1738,7 +1747,7 @@ data NodeRes v |
|
1738
|
1747
|
-- abort.
|
|
1739
|
1748
|
| NSkip
|
|
1740
|
1749
|
|
|
1741
|
|
--- | In a depth-first order, and starting from the given roots, traverse a
|
|
|
1750
|
+-- | In a parallel non-det-depth-first order, and starting from the given roots, traverse a
|
|
1742
|
1751
|
-- graph by iteratively expanding a node into a payload and a list of children
|
|
1743
|
1752
|
-- nodes to visit next.
|
|
1744
|
1753
|
--
|
| ... |
... |
@@ -1751,18 +1760,17 @@ data NodeRes v |
|
1751
|
1760
|
-- The result is a mapping from the key of every node transitively reachable
|
|
1752
|
1761
|
-- from the root nodes (inclusively) to the payload returned by expanding that
|
|
1753
|
1762
|
-- node. The result includes the previously visited nodes given in @base_map@,
|
|
1754
|
|
--- s.t. @dfsBuild base_map [] _ _ == base_map@.
|
|
|
1763
|
+-- s.t. @parDfsBuild base_map [] _ _ == base_map@.
|
|
1755
|
1764
|
--
|
|
1756
|
1765
|
-- The @expand@ function returns an 'NResult'. See the 'NResult' documentation
|
|
1757
|
1766
|
-- for more information about each result type.
|
|
1758
|
1767
|
--
|
|
1759
|
|
--- Error handling and exiting early can be achieved by selecting a @Monad m@
|
|
1760
|
|
--- accordingly, such as @Control.Monad.Except.Except@
|
|
1761
|
|
---
|
|
1762
|
1768
|
-- Example usage: @n@ is instanced to @DownsweepNode@, @k@ is @NodeKey@, and @v@ is @ModuleNodeEdge@.
|
|
1763
|
1769
|
--
|
|
1764
|
|
--- See also Note [Downsweep Control Flow and Caching]
|
|
1765
|
|
-dfsBuild :: (Ord k, Monad m)
|
|
|
1770
|
+-- See Note [Parallel Downsweep] for more information about how parallelism is
|
|
|
1771
|
+-- achieved, and See Note [Downsweep Control Flow and Caching] for information
|
|
|
1772
|
+-- about the various caches used.
|
|
|
1773
|
+parDfsBuild :: forall k v n. Ord k
|
|
1766
|
1774
|
=> Maybe (Map.Map k (NodeRes v))
|
|
1767
|
1775
|
-- ^ Base map, existing results. We won't re-expand any of the nodes
|
|
1768
|
1776
|
-- already present in this map.
|
| ... |
... |
@@ -1770,34 +1778,102 @@ dfsBuild :: (Ord k, Monad m) |
|
1770
|
1778
|
-- ^ The root nodes from where to start traversal
|
|
1771
|
1779
|
-> (n -> k)
|
|
1772
|
1780
|
-- ^ Compute the key which uniquely identifies this node
|
|
1773
|
|
- -> (n -> m (NodeRes (v,[n])))
|
|
|
1781
|
+ -> (n -> DownsweepM (NodeRes (v,[n])))
|
|
1774
|
1782
|
-- ^ Expand this node into its payload result and into the list of
|
|
1775
|
1783
|
-- children nodes to visit next.
|
|
1776
|
|
- -> m (Map.Map k (NodeRes v))
|
|
|
1784
|
+ -> DownsweepM (Map.Map k (NodeRes v))
|
|
1777
|
1785
|
-- ^ The result accumulates the payload of expanding the root nodes
|
|
1778
|
1786
|
-- and all nodes transitively reachable from those roots.
|
|
1779
|
|
-dfsBuild base_map roots key expand = go roots (fromMaybe Map.empty base_map)
|
|
|
1787
|
+parDfsBuild base_map roots key expand = ReaderT $ \ds_env -> do
|
|
|
1788
|
+ exc_var <- newTVarIO $ Nothing @MC.SomeException
|
|
|
1789
|
+ visited_var <- newTVarIO $ fromMaybe Map.empty base_map
|
|
|
1790
|
+ pending <- newTVarIO $ Set.empty @k
|
|
|
1791
|
+ worklist <- newTQueueIO @n
|
|
|
1792
|
+ threads <- newTVarIO []
|
|
|
1793
|
+
|
|
|
1794
|
+ coord_tid <- forkIO $
|
|
|
1795
|
+ coordinator ds_env exc_var visited_var worklist pending threads
|
|
|
1796
|
+ `MC.catch` \case
|
|
|
1797
|
+ (e::MC.SomeException)
|
|
|
1798
|
+ -- exit cleanly when killed
|
|
|
1799
|
+ | Just ThreadKilled <- fromException e -> return ()
|
|
|
1800
|
+ -- if the coordinator somehow else crashes,
|
|
|
1801
|
+ -- signal the exc_var for the main thread to throw it
|
|
|
1802
|
+ | otherwise -> atomically (modifyTVar' exc_var (<|> Just e))
|
|
|
1803
|
+
|
|
|
1804
|
+ atomically $ mapM_ (writeTQueue worklist) roots
|
|
|
1805
|
+
|
|
|
1806
|
+ mb_exc <- wait_done exc_var worklist pending
|
|
|
1807
|
+ `MC.finally` do
|
|
|
1808
|
+ killThread coord_tid
|
|
|
1809
|
+ mapM_ killThread =<< readTVarIO threads
|
|
|
1810
|
+
|
|
|
1811
|
+ case mb_exc of
|
|
|
1812
|
+ Just e -> throwIO e
|
|
|
1813
|
+ Nothing -> readTVarIO visited_var
|
|
|
1814
|
+
|
|
1780
|
1815
|
where
|
|
1781
|
|
- go [] visited = pure visited
|
|
1782
|
|
- go (s:ss) visited
|
|
1783
|
|
- | k `Map.member` visited
|
|
1784
|
|
- = go ss visited
|
|
1785
|
|
- | otherwise
|
|
1786
|
|
- = do r <- expand s
|
|
1787
|
|
- case r of
|
|
1788
|
|
- NSkip ->
|
|
1789
|
|
- go ss
|
|
1790
|
|
- (Map.insert k NSkip visited) -- Skip!
|
|
1791
|
|
- NSuccess (v,ns) ->
|
|
1792
|
|
- go (ns ++ ss)
|
|
1793
|
|
- (Map.insert k (NSuccess v) visited)
|
|
1794
|
|
- where
|
|
1795
|
|
- k = key s
|
|
|
1816
|
+ wait_done exc_var worklist pending =
|
|
|
1817
|
+ -- this txn retries until all work is done or an exception is signaled
|
|
|
1818
|
+ atomically $ do
|
|
|
1819
|
+ readTVar exc_var >>= \case
|
|
|
1820
|
+ Just e -> return (Just e)
|
|
|
1821
|
+ Nothing -> do
|
|
|
1822
|
+ empty_worklist <- isEmptyTQueue worklist
|
|
|
1823
|
+ empty_pending <- Set.null <$> readTVar pending
|
|
|
1824
|
+ check (empty_worklist && empty_pending)
|
|
|
1825
|
+ return Nothing
|
|
|
1826
|
+
|
|
|
1827
|
+ coordinator ds_env exc_var visvar worklist pendvar threads = forever $ do
|
|
|
1828
|
+ mb_node_to_expand <- atomically $ do
|
|
|
1829
|
+ node <- readTQueue worklist
|
|
|
1830
|
+ let k = key node
|
|
|
1831
|
+
|
|
|
1832
|
+ visited <- readTVar visvar
|
|
|
1833
|
+ pending <- readTVar pendvar
|
|
|
1834
|
+
|
|
|
1835
|
+ if (k `Set.member` pending || k `Map.member` visited)
|
|
|
1836
|
+ then return Nothing
|
|
|
1837
|
+ else do
|
|
|
1838
|
+ -- must add to pending in the same transaction as worklist dequeue,
|
|
|
1839
|
+ -- otherwise the main thread may find both the worklist and pending
|
|
|
1840
|
+ -- lists empty and exit prematurely.
|
|
|
1841
|
+ modifyTVar' pendvar (Set.insert k)
|
|
|
1842
|
+ return (Just (k, node))
|
|
|
1843
|
+
|
|
|
1844
|
+ case mb_node_to_expand of
|
|
|
1845
|
+ Nothing -> return ()
|
|
|
1846
|
+ Just (k, node) -> do
|
|
|
1847
|
+ tid <- MC.mask_ $ forkIOWithUnmask $ \unmask ->
|
|
|
1848
|
+ unmask (withLocalTmpFSMake (ds_make_env ds_env) $ \make_env ->
|
|
|
1849
|
+ worker ds_env{ds_make_env = make_env} visvar worklist pendvar k node)
|
|
|
1850
|
+ `MC.catch` \case
|
|
|
1851
|
+ e | Just (_ :: SomeAsyncException) <- fromException e
|
|
|
1852
|
+ -> throwIO e -- async exceptions like KillThread get thrown
|
|
|
1853
|
+ | otherwise -- exceptions in workers are written for main thread
|
|
|
1854
|
+ -> atomically (modifyTVar' exc_var (<|> Just e))
|
|
|
1855
|
+
|
|
|
1856
|
+ atomically $ modifyTVar' threads (tid:)
|
|
|
1857
|
+
|
|
|
1858
|
+ worker ds_env@DownsweepEnv{..} visvar worklist pendvar k node =
|
|
|
1859
|
+ withAbstractSem (compile_sem ds_make_env) $ do
|
|
|
1860
|
+ r <- runDownsweepM ds_env $
|
|
|
1861
|
+ expand node -- do the main work!
|
|
|
1862
|
+
|
|
|
1863
|
+ atomically $ do
|
|
|
1864
|
+ case r of
|
|
|
1865
|
+ NSkip ->
|
|
|
1866
|
+ modifyTVar' visvar (Map.insert k NSkip)
|
|
|
1867
|
+ NSuccess (v,ns) -> do
|
|
|
1868
|
+ modifyTVar' visvar (Map.insert k (NSuccess v))
|
|
|
1869
|
+ mapM_ (writeTQueue worklist) ns
|
|
|
1870
|
+
|
|
|
1871
|
+ modifyTVar' pendvar (Set.delete k)
|
|
1796
|
1872
|
|
|
1797
|
1873
|
{-
|
|
1798
|
1874
|
Note [Downsweep Control Flow and Caching]
|
|
1799
|
1875
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
1800
|
|
-The control flow of downsweep is extracted into a single function `dfsBuild`,
|
|
|
1876
|
+The control flow of downsweep is extracted into a single function `parDfsBuild`,
|
|
1801
|
1877
|
which takes care of iteratively expanding and traversing all nodes of the
|
|
1802
|
1878
|
in-construction module graph necessary to build a full `ModuleGraph` at the
|
|
1803
|
1879
|
end.
|
| ... |
... |
@@ -1806,7 +1882,7 @@ There are three levels of caching going on, all of which are necessary to make |
|
1806
|
1882
|
sure we don't do repeated work (notably, we NEVER summarise the same module
|
|
1807
|
1883
|
twice).
|
|
1808
|
1884
|
|
|
1809
|
|
-1. `dfsBuild` accumulates the final module graph and never revisits the
|
|
|
1885
|
+1. `parDfsBuild` accumulates the final module graph and never revisits the
|
|
1810
|
1886
|
same node of the module graph. Cache is keyed by the final
|
|
1811
|
1887
|
`ModuleGraph`s `NodeKey`s.
|
|
1812
|
1888
|
|
| ... |
... |
@@ -1874,6 +1950,83 @@ twice). |
|
1874
|
1950
|
|
|
1875
|
1951
|
See tests T27461a and T27461b.
|
|
1876
|
1952
|
|
|
1877
|
|
-See also Note [Downsweep: building and maintaining the module graph] and
|
|
1878
|
|
-Note [The ModuleGraph].
|
|
|
1953
|
+See also Note [Downsweep: building and maintaining the module graph] and Note [The ModuleGraph].
|
|
|
1954
|
+
|
|
|
1955
|
+
|
|
|
1956
|
+Note [Parallel Downsweep]
|
|
|
1957
|
+~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
1958
|
+Downsweep traverses the modules iteratively to discover the module graph
|
|
|
1959
|
+structure (see Note [Downsweep: building and maintaining the module graph])
|
|
|
1960
|
+
|
|
|
1961
|
+Each module has to be expanded/processed to discover dependencies amongst other
|
|
|
1962
|
+things, and that processing can often be costly (e.g. see `expandModuleSummary`).
|
|
|
1963
|
+
|
|
|
1964
|
+We leverage multiple threads in this traversal to expand more than one module
|
|
|
1965
|
+at once, respecting -j<N> to mean we never expand more than N modules at once.
|
|
|
1966
|
+The parallel downsweep is all handled by `parDfsBuild` as follows:
|
|
|
1967
|
+
|
|
|
1968
|
+- We launch a thread for every module we discover that needs to be
|
|
|
1969
|
+ expanded in the `coordinator` thread, popping it from the worklist
|
|
|
1970
|
+- Every launched `worker` thread blocks waiting for a semaphore token
|
|
|
1971
|
+ (`withAbstractSem`) to respect -j<N>
|
|
|
1972
|
+- The main thread waits until both the worklist and pending list is
|
|
|
1973
|
+ cleared, atomically.
|
|
|
1974
|
+
|
|
|
1975
|
+STM is used crucially to guarantee e.g. we don't have race conditions
|
|
|
1976
|
+between taking from the worklist and writing to the pending list while
|
|
|
1977
|
+checking whether they are clear.
|
|
|
1978
|
+
|
|
|
1979
|
+Exceptions are bubbled up to the main thread. The "main" thread, which is
|
|
|
1980
|
+typically waiting for the worklist+pending lists to be clear, instead gets
|
|
|
1981
|
+unblocked by this exception (signaled in `exc_var`) and re-throws it.
|
|
1879
|
1982
|
-}
|
|
|
1983
|
+
|
|
|
1984
|
+--------------------------------------------------------------------------------
|
|
|
1985
|
+-- * Concurrent utilities
|
|
|
1986
|
+--------------------------------------------------------------------------------
|
|
|
1987
|
+
|
|
|
1988
|
+-- | Map an action over a list using the parallelism pipeline system.
|
|
|
1989
|
+-- Create bundles of the list elems wrapped in a 'MakeAction' that uses
|
|
|
1990
|
+-- 'withAbstractSem' to wait for a free slot, limiting the number of
|
|
|
1991
|
+-- concurrently computed summaries to the value of the @-j@ option or the slots
|
|
|
1992
|
+-- allocated by the job server, if that is used.
|
|
|
1993
|
+--
|
|
|
1994
|
+-- The 'MakeAction' returns 'Maybe', which is not handled as an error, because
|
|
|
1995
|
+-- 'runLoop' only sets it to 'Nothing' when an exception was thrown, so the
|
|
|
1996
|
+-- result won't be read anyway here.
|
|
|
1997
|
+--
|
|
|
1998
|
+-- To emulate the current behavior, we funnel exceptions past the concurrency
|
|
|
1999
|
+-- barrier and rethrow the first one afterwards.
|
|
|
2000
|
+mapConcDS ::
|
|
|
2001
|
+ WorkerLimit ->
|
|
|
2002
|
+ Int {-^ Batch size -} ->
|
|
|
2003
|
+ MakeEnv ->
|
|
|
2004
|
+ (HscEnv -> a -> IO b) ->
|
|
|
2005
|
+ [a] ->
|
|
|
2006
|
+ IO ([b])
|
|
|
2007
|
+mapConcDS n_jobs bundle_size make_env run_action xs = do
|
|
|
2008
|
+ (actions, get_results) <- unzip <$> mapM action_and_result (zip [1..] bundles)
|
|
|
2009
|
+ runAllPipelines n_jobs make_env actions
|
|
|
2010
|
+ (sequence . catMaybes <$> sequence get_results) >>= \case
|
|
|
2011
|
+ Right results -> pure (concat results)
|
|
|
2012
|
+ Left exc -> throwIO exc
|
|
|
2013
|
+ where
|
|
|
2014
|
+ bundles = mk_bundles xs
|
|
|
2015
|
+
|
|
|
2016
|
+ mk_bundles = unfoldr \case
|
|
|
2017
|
+ [] -> Nothing
|
|
|
2018
|
+ ts -> Just (splitAt bundle_size ts)
|
|
|
2019
|
+
|
|
|
2020
|
+ action_and_result (log_queue_id, ts) = do
|
|
|
2021
|
+ res_var <- liftIO newEmptyMVar
|
|
|
2022
|
+ pure $! (MakeAction (action log_queue_id ts) res_var, readMVar res_var)
|
|
|
2023
|
+
|
|
|
2024
|
+ action log_queue_id target_bundle = do
|
|
|
2025
|
+ env@MakeEnv {compile_sem} <- ask
|
|
|
2026
|
+ lift $ lift $
|
|
|
2027
|
+ withAbstractSem compile_sem $
|
|
|
2028
|
+ withLoggerHsc log_queue_id env \ lcl_hsc_env ->
|
|
|
2029
|
+ MC.try (mapM (run_action lcl_hsc_env) target_bundle) >>= \case
|
|
|
2030
|
+ Left e | Just (_ :: SomeAsyncException) <- fromException e ->
|
|
|
2031
|
+ throwIO e
|
|
|
2032
|
+ a -> pure a |