@@ -327,13 +327,91 @@ class ModulesBuilder::ModuleFileCache {
327
327
const ThreadsafeFS &TFS,
328
328
PrerequisiteModules &BuiltModuleFiles);
329
329
330
+ void startBuildingModule (StringRef ModuleName) {
331
+ std::lock_guard<std::mutex> _ (ModulesBuildingMutex);
332
+ BuildingModules.insert (ModuleName);
333
+ }
334
+ void endBuildingModule (StringRef ModuleName) {
335
+ std::lock_guard<std::mutex> _ (ModulesBuildingMutex);
336
+ BuildingModules.erase (ModuleName);
337
+ }
338
+ bool isBuildingModule (StringRef ModuleName) {
339
+ std::lock_guard<std::mutex> _ (ModulesBuildingMutex);
340
+ return BuildingModules.contains (ModuleName);
341
+ }
342
+
330
343
const GlobalCompilationDatabase &CDB;
331
344
332
345
llvm::StringMap<std::shared_ptr<ModuleFile>> ModuleFiles;
333
346
// Mutex to guard accesses to ModuleFiles.
334
347
std::mutex ModuleFilesMutex;
348
+
349
+ // We should only build a unique module at most at the same time.
350
+ // When we want to build a module, use the mutex to lock it and use the
351
+ // condition variable to notify other threads the status of the build results.
352
+ //
353
+ // Store the mutex and the condition_variable in shared_ptr since they may be
354
+ // accessed by many threads.
355
+ llvm::StringMap<std::shared_ptr<std::mutex>> BuildingModuleMutexes;
356
+ llvm::StringMap<std::shared_ptr<std::condition_variable>> BuildingModuleCVs;
357
+ // The building modules set. A successed built module or a failed module or
358
+ // an unbuilt module shouldn't be in this set.
359
+ // This set is helpful to control the behavior of the condition variables.
360
+ llvm::StringSet<> BuildingModules;
361
+ // Lock when we access BuildingModules, BuildingModuleMutexes and
362
+ // BuildingModuleCVs.
363
+ std::mutex ModulesBuildingMutex;
364
+
365
+ // / An RAII object to guard the process to build a specific module.
366
+ struct ModuleBuildingSharedOwner {
367
+ public:
368
+ ModuleBuildingSharedOwner (StringRef ModuleName,
369
+ std::shared_ptr<std::mutex> &Mutex,
370
+ std::shared_ptr<std::condition_variable> &CV,
371
+ ModuleFileCache &Cache)
372
+ : ModuleName(ModuleName), Mutex(Mutex), CV(CV), Cache(Cache) {
373
+ IsFirstTask = (Mutex.use_count () == 2 );
374
+ }
375
+
376
+ ~ModuleBuildingSharedOwner ();
377
+
378
+ bool isUniqueBuildingOwner () { return IsFirstTask; }
379
+
380
+ std::mutex &getMutex () { return *Mutex; }
381
+
382
+ std::condition_variable &getCV () { return *CV; }
383
+
384
+ private:
385
+ StringRef ModuleName;
386
+ std::shared_ptr<std::mutex> Mutex;
387
+ std::shared_ptr<std::condition_variable> CV;
388
+ ModuleFileCache &Cache;
389
+ bool IsFirstTask;
390
+ };
391
+
392
+ ModuleBuildingSharedOwner
393
+ getOrCreateModuleBuildingOwner (StringRef ModuleName);
335
394
};
336
395
396
+ ModulesBuilder::ModuleFileCache::ModuleBuildingSharedOwner::
397
+ ~ModuleBuildingSharedOwner () {
398
+ std::lock_guard<std::mutex> _ (Cache.ModulesBuildingMutex );
399
+
400
+ Mutex.reset ();
401
+ CV.reset ();
402
+
403
+ // Try to release the memory in builder if possible.
404
+ if (auto Iter = Cache.BuildingModuleCVs .find (ModuleName);
405
+ Iter != Cache.BuildingModuleCVs .end () &&
406
+ Iter->getValue ().use_count () == 1 )
407
+ Cache.BuildingModuleCVs .erase (Iter);
408
+
409
+ if (auto Iter = Cache.BuildingModuleMutexes .find (ModuleName);
410
+ Iter != Cache.BuildingModuleMutexes .end () &&
411
+ Iter->getValue ().use_count () == 1 )
412
+ Cache.BuildingModuleMutexes .erase (Iter);
413
+ }
414
+
337
415
std::shared_ptr<ModuleFile>
338
416
ModulesBuilder::ModuleFileCache::isValidModuleFileUnlocked (
339
417
StringRef ModuleName, ProjectModules &MDB, const ThreadsafeFS &TFS,
@@ -374,6 +452,28 @@ std::shared_ptr<ModuleFile> ModulesBuilder::ModuleFileCache::getValidModuleFile(
374
452
return isValidModuleFileUnlocked (ModuleName, MDB, TFS, BuiltModuleFiles);
375
453
}
376
454
455
+ ModulesBuilder::ModuleFileCache::ModuleBuildingSharedOwner
456
+ ModulesBuilder::ModuleFileCache::getOrCreateModuleBuildingOwner (
457
+ StringRef ModuleName) {
458
+ std::lock_guard<std::mutex> _ (ModulesBuildingMutex);
459
+
460
+ auto MutexIter = BuildingModuleMutexes.find (ModuleName);
461
+ if (MutexIter == BuildingModuleMutexes.end ())
462
+ MutexIter = BuildingModuleMutexes
463
+ .try_emplace (ModuleName, std::make_shared<std::mutex>())
464
+ .first ;
465
+
466
+ auto CVIter = BuildingModuleCVs.find (ModuleName);
467
+ if (CVIter == BuildingModuleCVs.end ())
468
+ CVIter = BuildingModuleCVs
469
+ .try_emplace (ModuleName,
470
+ std::make_shared<std::condition_variable>())
471
+ .first ;
472
+
473
+ return ModuleBuildingSharedOwner (ModuleName, MutexIter->getValue (),
474
+ CVIter->getValue (), *this );
475
+ }
476
+
377
477
llvm::Error ModulesBuilder::ModuleFileCache::getOrBuildModuleFile (
378
478
StringRef ModuleName, const ThreadsafeFS &TFS, ProjectModules &MDB,
379
479
ReusablePrerequisiteModules &BuiltModuleFiles) {
@@ -405,7 +505,40 @@ llvm::Error ModulesBuilder::ModuleFileCache::getOrBuildModuleFile(
405
505
return llvm::Error::success ();
406
506
}
407
507
508
+ ModuleBuildingSharedOwner ModuleBuildingOwner =
509
+ getOrCreateModuleBuildingOwner (ModuleName);
510
+
511
+ std::condition_variable &CV = ModuleBuildingOwner.getCV ();
512
+ std::unique_lock<std::mutex> lk (ModuleBuildingOwner.getMutex ());
513
+ if (!ModuleBuildingOwner.isUniqueBuildingOwner ()) {
514
+ log (" Waiting other task for module {0}" , ModuleName);
515
+ CV.wait (lk, [this , ModuleName] { return !isBuildingModule (ModuleName); });
516
+
517
+ // Try to access the built module files from other threads manually.
518
+ // We don't call getValidModuleFile here since it may be too heavy.
519
+ std::lock_guard<std::mutex> _ (ModuleFilesMutex);
520
+ auto Iter = ModuleFiles.find (ModuleName);
521
+ if (Iter != ModuleFiles.end ()) {
522
+ log (" Got module file from other task building {0}" , ModuleName);
523
+ BuiltModuleFiles.addModuleFile (Iter->second );
524
+ return llvm::Error::success ();
525
+ }
526
+
527
+ // If the module file is not in the cache, it indicates that the building
528
+ // from other thread failed, so we give up earlier in this case to avoid
529
+ // wasting time.
530
+ return llvm::createStringError (llvm::formatv (
531
+ " The module file {0} may be failed to build in other thread." ,
532
+ ModuleName));
533
+ }
534
+
408
535
log (" Building module {0}" , ModuleName);
536
+ startBuildingModule (ModuleName);
537
+
538
+ auto _ = llvm::make_scope_exit ([&]() {
539
+ endBuildingModule (ModuleName);
540
+ CV.notify_all ();
541
+ });
409
542
410
543
llvm::SmallString<256 > ModuleFilesPrefix =
411
544
getUniqueModuleFilesPath (ModuleUnitFileName);
0 commit comments