Skip to content

Commit 3d110f8

Browse files
committed
[Dependency Scanning] Isolate shared dependency scanner state
Using mutual exclusion, ensuring that multiple threads executing dependency scans do not encounter data races on shared mutable state. There are two layers with shared state where we need to be careful: - `DependencyScanningTool`, as the main entity that scanning clients interact with. This tool instantiates compiler instances for individual scans, computing a scanning invocation hash. It needs to remember those instances for future use, and when creating instances it needs to reset LLVM argument processor's global state, meaning all uses of argument processing must be in a critical section. - `SwiftDependencyScanningService`, as the main cache where dependency scanning results are stored. Each individual scan instantiates a `ModuleDependenciesCache`, which uses the scanning service as the underlying storage. The services' storage is segmented to storing dependencies discovered in a scan with a given context hash, which means two different scanning invocations running at the same time will be accessing different locations in its storage, thus not requiring synchronization. But the service still has some shared state that must be protected, such as the collection of discovered source modules, and the map used to query context-hash-specific underlying cache storage.
1 parent 60ffd84 commit 3d110f8

File tree

5 files changed

+103
-68
lines changed

5 files changed

+103
-68
lines changed

include/swift/AST/ModuleDependencies.h

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "llvm/ADT/ArrayRef.h"
2626
#include "llvm/ADT/Optional.h"
2727
#include "llvm/ADT/StringSet.h"
28+
#include "llvm/Support/Mutex.h"
2829
#include <string>
2930
#include <vector>
3031
#include <unordered_map>
@@ -580,18 +581,20 @@ class SwiftDependencyScanningService {
580581
llvm::StringMap<std::unique_ptr<ContextSpecificGlobalCacheState>>
581582
ContextSpecificCacheMap;
582583

583-
/// The current context hash configuration
584-
Optional<std::string> CurrentContextHash;
585-
586584
/// The context hashes used by scanners using this cache, in the order in
587585
/// which they were used
588586
std::vector<std::string> AllContextHashes;
589587

588+
/// Shared state mutual-exclusivity lock
589+
llvm::sys::SmartMutex<true> ScanningServiceGlobalLock;
590+
590591
/// Retrieve the dependencies map that corresponds to the given dependency
591592
/// kind.
592-
ModuleNameToDependencyMap &getDependenciesMap(ModuleDependencyKind kind);
593+
ModuleNameToDependencyMap &getDependenciesMap(ModuleDependencyKind kind,
594+
StringRef scanContextHash);
593595
const ModuleNameToDependencyMap &
594-
getDependenciesMap(ModuleDependencyKind kind) const;
596+
getDependenciesMap(ModuleDependencyKind kind,
597+
StringRef scanContextHash) const;
595598

596599
public:
597600
SwiftDependencyScanningService();
@@ -613,8 +616,8 @@ class SwiftDependencyScanningService {
613616
return *SharedFilesystemCache;
614617
}
615618

616-
llvm::StringSet<>& getAlreadySeenClangModules() {
617-
return getCurrentCache()->alreadySeenClangModules;
619+
llvm::StringSet<>& getAlreadySeenClangModules(StringRef scanningContextHash) {
620+
return getCacheForScanningContextHash(scanningContextHash)->alreadySeenClangModules;
618621
}
619622

620623
/// Wrap the filesystem on the specified `CompilerInstance` with a
@@ -630,7 +633,7 @@ class SwiftDependencyScanningService {
630633

631634
/// Configure the current state of the cache to respond to queries
632635
/// for the specified scanning context hash.
633-
void configureForContextHash(std::string scanningContextHash);
636+
void configureForContextHash(StringRef scanningContextHash);
634637

635638
/// Return context hashes of all scanner invocations that have used
636639
/// this cache instance.
@@ -640,15 +643,12 @@ class SwiftDependencyScanningService {
640643

641644
/// Whether we have cached dependency information for the given module.
642645
bool hasDependency(StringRef moduleName,
643-
Optional<ModuleDependencyKind> kind) const;
644-
645-
/// Return a pointer to the context-specific cache state of the current
646-
/// scanning action.
647-
ContextSpecificGlobalCacheState *getCurrentCache() const;
646+
Optional<ModuleDependencyKind> kind,
647+
StringRef scanContextHash) const;
648648

649649
/// Return a pointer to the cache state of the specified context hash.
650650
ContextSpecificGlobalCacheState *
651-
getCacheForScanningContextHash(StringRef scanningContextHash) const;
651+
getCacheForScanningContextHash(StringRef scanContextHash) const;
652652

653653
/// Look for source-based module dependency details
654654
Optional<const ModuleDependencyInfo*>
@@ -659,15 +659,22 @@ class SwiftDependencyScanningService {
659659
/// \returns the cached result, or \c None if there is no cached entry.
660660
Optional<const ModuleDependencyInfo*>
661661
findDependency(StringRef moduleName,
662-
Optional<ModuleDependencyKind> kind) const;
662+
Optional<ModuleDependencyKind> kind,
663+
StringRef scanContextHash) const;
663664

664665
/// Record dependencies for the given module.
665666
const ModuleDependencyInfo *recordDependency(StringRef moduleName,
666-
ModuleDependencyInfo dependencies);
667+
ModuleDependencyInfo dependencies,
668+
StringRef scanContextHash);
669+
670+
/// Record source-module dependencies for the given module.
671+
const ModuleDependencyInfo *recordSourceDependency(StringRef moduleName,
672+
ModuleDependencyInfo dependencies);
667673

668674
/// Update stored dependencies for the given module.
669675
const ModuleDependencyInfo *updateDependency(ModuleDependencyID moduleID,
670-
ModuleDependencyInfo dependencies);
676+
ModuleDependencyInfo dependencies,
677+
StringRef scanContextHash);
671678

672679
/// Reference the list of all module dependencies that are not source-based
673680
/// modules (i.e. interface dependencies, binary dependencies, clang
@@ -727,7 +734,7 @@ class ModuleDependenciesCache {
727734
return clangScanningTool;
728735
}
729736
llvm::StringSet<>& getAlreadySeenClangModules() {
730-
return globalScanningService.getAlreadySeenClangModules();
737+
return globalScanningService.getAlreadySeenClangModules(scannerContextHash);
731738
}
732739

733740
/// Look for module dependencies for a module with the given name

include/swift/DependencyScan/DependencyScanningTool.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ class DependencyScanningTool {
115115
/// command-line options specified in the batch scan input entry.
116116
std::unique_ptr<CompilerArgInstanceCacheMap> VersionedPCMInstanceCacheCache;
117117

118+
/// Shared state mutual-exclusivity lock
119+
llvm::sys::SmartMutex<true> DependencyScanningToolStateLock;
120+
118121
/// A shared consumer that accumulates encountered diagnostics.
119122
DependencyScannerDiagnosticCollectingConsumer CDC;
120123
llvm::BumpPtrAllocator Alloc;

lib/AST/ModuleDependencies.cpp

Lines changed: 49 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -251,13 +251,6 @@ void SwiftDependencyScanningService::overlaySharedFilesystemCacheForCompilation(
251251
Instance.getSourceMgr().setFileSystem(depFS);
252252
}
253253

254-
SwiftDependencyScanningService::ContextSpecificGlobalCacheState *
255-
SwiftDependencyScanningService::getCurrentCache() const {
256-
assert(CurrentContextHash.has_value() &&
257-
"Global Module Dependencies Cache not configured with Triple.");
258-
return getCacheForScanningContextHash(CurrentContextHash.value());
259-
}
260-
261254
SwiftDependencyScanningService::ContextSpecificGlobalCacheState *
262255
SwiftDependencyScanningService::getCacheForScanningContextHash(StringRef scanningContextHash) const {
263256
auto contextSpecificCache = ContextSpecificCacheMap.find(scanningContextHash);
@@ -269,8 +262,8 @@ SwiftDependencyScanningService::getCacheForScanningContextHash(StringRef scannin
269262

270263
const ModuleNameToDependencyMap &
271264
SwiftDependencyScanningService::getDependenciesMap(
272-
ModuleDependencyKind kind) const {
273-
auto contextSpecificCache = getCurrentCache();
265+
ModuleDependencyKind kind, StringRef scanContextHash) const {
266+
auto contextSpecificCache = getCacheForScanningContextHash(scanContextHash);
274267
auto it = contextSpecificCache->ModuleDependenciesMap.find(kind);
275268
assert(it != contextSpecificCache->ModuleDependenciesMap.end() &&
276269
"invalid dependency kind");
@@ -279,40 +272,38 @@ SwiftDependencyScanningService::getDependenciesMap(
279272

280273
ModuleNameToDependencyMap &
281274
SwiftDependencyScanningService::getDependenciesMap(
282-
ModuleDependencyKind kind) {
283-
auto contextSpecificCache = getCurrentCache();
275+
ModuleDependencyKind kind, StringRef scanContextHash) {
276+
llvm::sys::SmartScopedLock<true> Lock(ScanningServiceGlobalLock);
277+
auto contextSpecificCache = getCacheForScanningContextHash(scanContextHash);
284278
auto it = contextSpecificCache->ModuleDependenciesMap.find(kind);
285279
assert(it != contextSpecificCache->ModuleDependenciesMap.end() &&
286280
"invalid dependency kind");
287281
return it->second;
288282
}
289283

290-
void SwiftDependencyScanningService::configureForContextHash(std::string scanningContextHash) {
284+
void SwiftDependencyScanningService::configureForContextHash(StringRef scanningContextHash) {
291285
auto knownContext = ContextSpecificCacheMap.find(scanningContextHash);
292-
if (knownContext != ContextSpecificCacheMap.end()) {
293-
// Set the current context and leave the rest as-is
294-
CurrentContextHash = scanningContextHash;
295-
} else {
296-
// First time scanning with this triple, initialize target-specific state.
286+
if (knownContext == ContextSpecificCacheMap.end()) {
287+
// First time scanning with this context, initialize context-specific state.
297288
std::unique_ptr<ContextSpecificGlobalCacheState> contextSpecificCache =
298289
std::make_unique<ContextSpecificGlobalCacheState>();
299290
for (auto kind = ModuleDependencyKind::FirstKind;
300291
kind != ModuleDependencyKind::LastKind; ++kind) {
301292
contextSpecificCache->ModuleDependenciesMap.insert({kind, ModuleNameToDependencyMap()});
302293
}
303-
304-
ContextSpecificCacheMap.insert({scanningContextHash, std::move(contextSpecificCache)});
305-
CurrentContextHash = scanningContextHash;
306-
AllContextHashes.push_back(scanningContextHash);
294+
llvm::sys::SmartScopedLock<true> Lock(ScanningServiceGlobalLock);
295+
ContextSpecificCacheMap.insert({scanningContextHash.str(), std::move(contextSpecificCache)});
296+
AllContextHashes.push_back(scanningContextHash.str());
307297
}
308298
}
309299

310300
Optional<const ModuleDependencyInfo*> SwiftDependencyScanningService::findDependency(
311-
StringRef moduleName, Optional<ModuleDependencyKind> kind) const {
301+
StringRef moduleName, Optional<ModuleDependencyKind> kind,
302+
StringRef scanningContextHash) const {
312303
if (!kind) {
313304
for (auto kind = ModuleDependencyKind::FirstKind;
314305
kind != ModuleDependencyKind::LastKind; ++kind) {
315-
auto dep = findDependency(moduleName, kind);
306+
auto dep = findDependency(moduleName, kind, scanningContextHash);
316307
if (dep.has_value())
317308
return dep.value();
318309
}
@@ -324,7 +315,7 @@ Optional<const ModuleDependencyInfo*> SwiftDependencyScanningService::findDepend
324315
return findSourceModuleDependency(moduleName);
325316
}
326317

327-
const auto &map = getDependenciesMap(kind.value());
318+
const auto &map = getDependenciesMap(kind.value(), scanningContextHash);
328319
auto known = map.find(moduleName);
329320
if (known != map.end())
330321
return &(known->second);
@@ -343,44 +334,55 @@ SwiftDependencyScanningService::findSourceModuleDependency(
343334
}
344335

345336
bool SwiftDependencyScanningService::hasDependency(
346-
StringRef moduleName, Optional<ModuleDependencyKind> kind) const {
347-
return findDependency(moduleName, kind).has_value();
337+
StringRef moduleName, Optional<ModuleDependencyKind> kind,
338+
StringRef scanContextHash) const {
339+
return findDependency(moduleName, kind, scanContextHash).has_value();
348340
}
349341

350342
const ModuleDependencyInfo *SwiftDependencyScanningService::recordDependency(
351-
StringRef moduleName, ModuleDependencyInfo dependencies) {
343+
StringRef moduleName, ModuleDependencyInfo dependencies,
344+
StringRef scanContextHash) {
352345
auto kind = dependencies.getKind();
353346
// Source-based dependencies are recorded independently of the invocation's
354347
// target triple.
355-
if (kind == swift::ModuleDependencyKind::SwiftSource) {
356-
assert(SwiftSourceModuleDependenciesMap.count(moduleName) == 0 &&
357-
"Attempting to record duplicate SwiftSource dependency.");
358-
SwiftSourceModuleDependenciesMap.insert(
359-
{moduleName, std::move(dependencies)});
360-
AllSourceModules.push_back({moduleName.str(), kind});
361-
return &(SwiftSourceModuleDependenciesMap.find(moduleName)->second);
362-
}
348+
if (kind == swift::ModuleDependencyKind::SwiftSource)
349+
return recordSourceDependency(moduleName, std::move(dependencies));
363350

364351
// All other dependencies are recorded according to the target triple of the
365352
// scanning invocation that discovers them.
366-
auto &map = getDependenciesMap(kind);
353+
auto &map = getDependenciesMap(kind, scanContextHash);
367354
map.insert({moduleName, dependencies});
368355
return &(map[moduleName]);
369356
}
370357

358+
const ModuleDependencyInfo *SwiftDependencyScanningService::recordSourceDependency(
359+
StringRef moduleName, ModuleDependencyInfo dependencies) {
360+
llvm::sys::SmartScopedLock<true> Lock(ScanningServiceGlobalLock);
361+
auto kind = dependencies.getKind();
362+
assert(kind == swift::ModuleDependencyKind::SwiftSource && "Expected source module dependncy info");
363+
assert(SwiftSourceModuleDependenciesMap.count(moduleName) == 0 &&
364+
"Attempting to record duplicate SwiftSource dependency.");
365+
SwiftSourceModuleDependenciesMap.insert(
366+
{moduleName, std::move(dependencies)});
367+
AllSourceModules.push_back({moduleName.str(), kind});
368+
return &(SwiftSourceModuleDependenciesMap.find(moduleName)->second);
369+
}
370+
371371
const ModuleDependencyInfo *SwiftDependencyScanningService::updateDependency(
372-
ModuleDependencyID moduleID, ModuleDependencyInfo dependencies) {
372+
ModuleDependencyID moduleID, ModuleDependencyInfo dependencies,
373+
StringRef scanningContextHash) {
373374
auto kind = dependencies.getKind();
374375
// Source-based dependencies
375376
if (kind == swift::ModuleDependencyKind::SwiftSource) {
377+
llvm::sys::SmartScopedLock<true> Lock(ScanningServiceGlobalLock);
376378
assert(SwiftSourceModuleDependenciesMap.count(moduleID.first) == 1 &&
377379
"Attempting to update non-existing Swift Source dependency.");
378380
auto known = SwiftSourceModuleDependenciesMap.find(moduleID.first);
379381
known->second = std::move(dependencies);
380382
return &(known->second);
381383
}
382384

383-
auto &map = getDependenciesMap(moduleID.second);
385+
auto &map = getDependenciesMap(moduleID.second, scanningContextHash);
384386
auto known = map.find(moduleID.first);
385387
assert(known != map.end() && "Not yet added to map");
386388
known->second = std::move(dependencies);
@@ -422,9 +424,10 @@ ModuleDependenciesCache::ModuleDependenciesCache(
422424
Optional<const ModuleDependencyInfo*>
423425
ModuleDependenciesCache::findDependency(
424426
StringRef moduleName, Optional<ModuleDependencyKind> kind) const {
425-
auto optionalDep = globalScanningService.findDependency(moduleName, kind);
426-
// During a scan, only produce the cached source module info for the current module
427-
// under scan.
427+
auto optionalDep = globalScanningService.findDependency(moduleName, kind,
428+
scannerContextHash);
429+
// During a scan, only produce the cached source module info for the current
430+
// module under scan.
428431
if (optionalDep.hasValue()) {
429432
auto dep = optionalDep.getValue();
430433
if (dep->getAsSwiftSourceModule() &&
@@ -446,7 +449,8 @@ void ModuleDependenciesCache::recordDependency(
446449
StringRef moduleName, ModuleDependencyInfo dependencies) {
447450
auto dependenciesKind = dependencies.getKind();
448451
const ModuleDependencyInfo *recordedDependencies =
449-
globalScanningService.recordDependency(moduleName, dependencies);
452+
globalScanningService.recordDependency(moduleName, dependencies,
453+
scannerContextHash);
450454

451455
auto &map = getDependencyReferencesMap(dependenciesKind);
452456
assert(map.count(moduleName) == 0 && "Already added to map");
@@ -455,7 +459,9 @@ void ModuleDependenciesCache::recordDependency(
455459

456460
void ModuleDependenciesCache::updateDependency(
457461
ModuleDependencyID moduleID, ModuleDependencyInfo dependencyInfo) {
458-
const ModuleDependencyInfo *updatedDependencies = globalScanningService.updateDependency(moduleID, dependencyInfo);
462+
const ModuleDependencyInfo *updatedDependencies =
463+
globalScanningService.updateDependency(moduleID, dependencyInfo,
464+
scannerContextHash);
459465
auto &map = getDependencyReferencesMap(moduleID.second);
460466
auto known = map.find(moduleID.first);
461467
if (known != map.end())

lib/DependencyScan/DependencyScanningTool.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,13 @@
2626
namespace swift {
2727
namespace dependencies {
2828

29+
// Global mutex for target info queries since they are executed separately .
30+
llvm::sys::SmartMutex<true> TargetInfoMutex;
31+
2932
llvm::ErrorOr<swiftscan_string_ref_t> getTargetInfo(ArrayRef<const char *> Command,
3033
const char *main_executable_path) {
34+
llvm::sys::SmartScopedLock<true> Lock(TargetInfoMutex);
35+
3136
// We must reset option occurrences because we are handling an unrelated
3237
// command-line to those possibly parsed before using the same tool.
3338
// We must do so because LLVM options parsing is done using a managed
@@ -178,6 +183,7 @@ DependencyScanningTool::getDependencies(
178183
}
179184

180185
void DependencyScanningTool::serializeCache(llvm::StringRef path) {
186+
llvm::sys::SmartScopedLock<true> Lock(DependencyScanningToolStateLock);
181187
SourceManager SM;
182188
DiagnosticEngine Diags(SM);
183189
Diags.addConsumer(CDC);
@@ -186,6 +192,7 @@ void DependencyScanningTool::serializeCache(llvm::StringRef path) {
186192
}
187193

188194
bool DependencyScanningTool::loadCache(llvm::StringRef path) {
195+
llvm::sys::SmartScopedLock<true> Lock(DependencyScanningToolStateLock);
189196
SourceManager SM;
190197
DiagnosticEngine Diags(SM);
191198
Diags.addConsumer(CDC);
@@ -210,6 +217,10 @@ void DependencyScanningTool::resetDiagnostics() {
210217
llvm::ErrorOr<std::unique_ptr<CompilerInstance>>
211218
DependencyScanningTool::initScannerForAction(
212219
ArrayRef<const char *> Command) {
220+
// The remainder of this method operates on shared state in the
221+
// scanning service and global LLVM state with:
222+
// llvm::cl::ResetAllOptionOccurrences
223+
llvm::sys::SmartScopedLock<true> Lock(DependencyScanningToolStateLock);
213224
auto instanceOrErr = initCompilerInstanceForScan(Command);
214225
if (instanceOrErr.getError())
215226
return instanceOrErr;

0 commit comments

Comments
 (0)