Rodrigo Mesquita pushed to branch wip/romes/27514 at Glasgow Haskell Compiler / GHC

Commits:

8 changed files:

Changes:

  • changelog.d/parallel-downsweep
    1
    +section: compiler
    
    2
    +synopsis: Parallelize the downsweep/module-discovery pass
    
    3
    +issues: #27514
    
    4
    +mrs: !16394
    
    5
    +description: {
    
    6
    +    Parallelize the downsweep pass s.t. processing and discovering the module
    
    7
    +    graph can be done in parallel (parallelizing work like pre-processing CPP
    
    8
    +    in modules) according to the -j<N> flag used. Using Cabal as an example
    
    9
    +    with -j8, parallel downsweep was 2x faster (from 2s to 1s in downsweep time).
    
    10
    +}
    
    11
    +

  • compiler/GHC/Driver/Downsweep.hs
    ... ... @@ -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

  • compiler/GHC/Driver/MakeAction.hs
    ... ... @@ -185,7 +185,8 @@ runLoop fork_thread env (MakeAction act res_var :acts) = do
    185 185
     
    
    186 186
       -- withLocalTmpFs has to occur outside of fork to remain deterministic
    
    187 187
       new_thread <- withLocalTmpFSMake env $ \lcl_env ->
    
    188
    -    fork_thread $ \unmask -> (do
    
    188
    +    MC.mask_ $
    
    189
    +      fork_thread $ \unmask -> (do
    
    189 190
                 mres <- (unmask $ run_pipeline lcl_env act)
    
    190 191
                           `MC.onException` (putMVar res_var Nothing) -- Defensive: If there's an unhandled exception then still signal the failure.
    
    191 192
                 putMVar res_var mres)
    

  • testsuite/tests/ghc-api/fixed-nodes/FixedNodes.hs
    ... ... @@ -24,7 +24,7 @@ import Control.Monad.Catch (handle, throwM)
    24 24
     import Control.Exception.Context
    
    25 25
     import GHC.Driver.MakeFile
    
    26 26
     import GHC.Utils.Outputable
    
    27
    -import Data.IORef (newIORef)
    
    27
    +import Control.Concurrent.MVar
    
    28 28
     -- | Convert a ModuleNodeCompile to a ModuleNodeFixed
    
    29 29
     convertToFixed :: ModuleNodeInfo -> ModuleNodeInfo
    
    30 30
     convertToFixed (ModuleNodeCompile ms) =
    
    ... ... @@ -152,6 +152,6 @@ main = do
    152 152
             getModSummaryFromTarget :: FilePath -> Ghc ModSummary
    
    153 153
             getModSummaryFromTarget file = do
    
    154 154
               hsc_env <- getSession
    
    155
    -          summ_cache <- liftIO $ newIORef mempty
    
    155
    +          summ_cache <- liftIO $ newMVar mempty
    
    156 156
               Right ms <- liftIO $ summariseFile hsc_env (DefiniteHomeUnit mainUnitId Nothing) summ_cache file Nothing Nothing
    
    157 157
               return ms

  • testsuite/tests/ghc-api/fixed-nodes/InterfaceModuleGraph.hs
    ... ... @@ -6,6 +6,7 @@ import GHC.Driver.Session
    6 6
     import GHC.Driver.Monad
    
    7 7
     import GHC.Driver.Env
    
    8 8
     import GHC.Driver.Make (summariseFile)
    
    9
    +import GHC.Driver.MakeAction
    
    9 10
     import GHC.Driver.Downsweep
    
    10 11
     import GHC.Unit.Module.Graph
    
    11 12
     import GHC.Unit.Module.ModSummary
    
    ... ... @@ -16,12 +17,13 @@ import GHC.Types.SourceFile
    16 17
     import System.Environment
    
    17 18
     import Control.Monad (void, when)
    
    18 19
     import Data.Maybe (fromJust)
    
    19
    -import Data.IORef (newIORef)
    
    20
    +import Control.Concurrent.MVar
    
    20 21
     import Control.Exception (ExceptionWithContext(..), SomeException)
    
    21 22
     import Control.Monad.Catch (handle, throwM)
    
    22 23
     import Control.Exception.Context
    
    23 24
     import GHC.Utils.Outputable
    
    24 25
     import Data.List
    
    26
    +import GHC.Types.Error
    
    25 27
     import GHC.Unit.Env
    
    26 28
     import GHC.Unit.State
    
    27 29
     import GHC.Tc.Utils.Monad
    
    ... ... @@ -60,7 +62,7 @@ main = do
    60 62
           hsc_env <- getSession
    
    61 63
           setSession $ hsc_env { hsc_dflags = (hsc_dflags hsc_env) { ghcMode = OneShot } }
    
    62 64
           hsc_env <- getSession
    
    63
    -
    
    65
    +      n_jobs <- liftIO $ mkWorkerLimit (hsc_dflags hsc_env)
    
    64 66
     
    
    65 67
           -- Create ModNodeKeys with unit IDs
    
    66 68
           let keyA = msKey msA
    
    ... ... @@ -68,10 +70,21 @@ main = do
    68 70
               keyC = msKey msC
    
    69 71
     
    
    70 72
           let mkGraph s = do
    
    71
    -            summ_cache <- newIORef mempty
    
    72
    -            imps_cache <- newIORef mempty
    
    73
    -            ([], nodes) <- downsweepFromRootNodes hsc_env summ_cache imps_cache Nothing [] True DownsweepUseFixed s []
    
    74
    -            return $ mkModuleGraph nodes
    
    73
    +            summ_cache <- newMVar mempty
    
    74
    +            imps_cache <- newMVar mempty
    
    75
    +            withMakeEnv n_jobs hsc_env mkUnknownDiagnostic Nothing $ \make_env -> do
    
    76
    +              let env = DownsweepEnv
    
    77
    +                    { ds_hsc_env         = hsc_env
    
    78
    +                    , ds_summaries_cache = summ_cache
    
    79
    +                    , ds_imports_cache   = imps_cache
    
    80
    +                    , ds_mode            = DownsweepUseFixed
    
    81
    +                    , ds_excl_mods       = []
    
    82
    +                    , ds_n_jobs          = n_jobs
    
    83
    +                    , ds_make_env        = make_env
    
    84
    +                    }
    
    85
    +              ([], nodes) <- runDownsweepM env $
    
    86
    +                downsweepFromRootNodes Nothing True s []
    
    87
    +              return $ mkModuleGraph nodes
    
    75 88
     
    
    76 89
           graph <- liftIO $ mkGraph [ModuleNodeCompile msC]
    
    77 90
     
    
    ... ... @@ -101,6 +114,6 @@ main = do
    101 114
             getModSummaryFromTarget :: FilePath -> Ghc ModSummary
    
    102 115
             getModSummaryFromTarget file = do
    
    103 116
               hsc_env <- getSession
    
    104
    -          summ_cache <- liftIO $ newIORef mempty
    
    117
    +          summ_cache <- liftIO $ newMVar mempty
    
    105 118
               Right ms <- liftIO $ summariseFile hsc_env (DefiniteHomeUnit mainUnitId Nothing) summ_cache file Nothing Nothing
    
    106 119
               return ms

  • testsuite/tests/ghc-api/fixed-nodes/ModuleGraphInvariants.hs
    ... ... @@ -23,7 +23,7 @@ import Control.Monad.Catch (handle, throwM)
    23 23
     import Control.Exception.Context
    
    24 24
     import GHC.Utils.Outputable
    
    25 25
     import Data.List
    
    26
    -import Data.IORef (newIORef)
    
    26
    +import Control.Concurrent.MVar
    
    27 27
     
    
    28 28
     -- | Convert a ModuleNodeCompile to a ModuleNodeFixed
    
    29 29
     convertToFixed :: ModuleNodeInfo -> ModuleNodeInfo
    
    ... ... @@ -133,6 +133,6 @@ main = do
    133 133
             getModSummaryFromTarget :: FilePath -> Ghc ModSummary
    
    134 134
             getModSummaryFromTarget file = do
    
    135 135
               hsc_env <- getSession
    
    136
    -          summ_cache <- liftIO $ newIORef mempty
    
    136
    +          summ_cache <- liftIO $ newMVar mempty
    
    137 137
               Right ms <- liftIO $ summariseFile hsc_env (DefiniteHomeUnit mainUnitId Nothing) summ_cache file Nothing Nothing
    
    138 138
               return ms

  • testsuite/tests/splice-imports/SI35.hs
    ... ... @@ -28,7 +28,7 @@ import GHC.Unit.Module.Stage
    28 28
     import GHC.Data.Graph.Directed.Reachability
    
    29 29
     import GHC.Utils.Trace
    
    30 30
     import GHC.Unit.Module.Graph
    
    31
    -import Data.IORef (newIORef)
    
    31
    +import Control.Concurrent.MVar
    
    32 32
     
    
    33 33
     main :: IO ()
    
    34 34
     main = do
    
    ... ... @@ -76,6 +76,6 @@ main = do
    76 76
             getModSummaryFromTarget :: FilePath -> Ghc ModSummary
    
    77 77
             getModSummaryFromTarget file = do
    
    78 78
               hsc_env <- getSession
    
    79
    -          summ_cache <- liftIO $ newIORef mempty
    
    79
    +          summ_cache <- liftIO $ newMVar mempty
    
    80 80
               Right ms <- liftIO $ summariseFile hsc_env (DefiniteHomeUnit mainUnitId Nothing) summ_cache file Nothing Nothing
    
    81
    -          return ms
    \ No newline at end of file
    81
    +          return ms

  • utils/check-ppr/Main.hs
    ... ... @@ -18,7 +18,7 @@ import System.Environment( getArgs )
    18 18
     import System.Exit
    
    19 19
     import System.FilePath
    
    20 20
     import System.IO
    
    21
    -import Data.IORef
    
    21
    +import Control.Concurrent.MVar
    
    22 22
     
    
    23 23
     usage :: String
    
    24 24
     usage = unlines
    
    ... ... @@ -86,7 +86,7 @@ parseOneFile libdir fileName = do
    86 86
              let dflags2 = dflags `gopt_set` Opt_KeepRawTokenStream
    
    87 87
              _ <- setSessionDynFlags dflags2
    
    88 88
              hsc_env <- getSession
    
    89
    -         cache <- liftIO $ newIORef mempty
    
    89
    +         cache <- liftIO $ newMVar mempty
    
    90 90
              mms <- liftIO $ summariseFile hsc_env (hsc_home_unit hsc_env) cache fileName Nothing Nothing
    
    91 91
              case mms of
    
    92 92
                Left _err -> error "parseOneFile"