Skip to content

Commit 0f96a5e

Browse files
authored
Merge pull request #29123 from nkcsgexi/reapply-file-lock
Re-apply "ModuleInterface: lock .swiftinterface while generating module cache"
2 parents a584202 + e1f6e84 commit 0f96a5e

File tree

5 files changed

+98
-7
lines changed

5 files changed

+98
-7
lines changed

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,12 @@ ERROR(unknown_forced_module_loading_mode,none,
343343
"unknown value for SWIFT_FORCE_MODULE_LOADING variable: '%0'",
344344
(StringRef))
345345

346+
REMARK(interface_file_lock_failure,none,
347+
"could not acquire lock file for module interface '%0'", (StringRef))
348+
349+
REMARK(interface_file_lock_timed_out,none,
350+
"timed out waiting to acquire lock file for module interface '%0'", (StringRef))
351+
346352
#ifndef DIAG_NO_UNDEF
347353
# if defined(DIAG)
348354
# undef DIAG

lib/Frontend/ModuleInterfaceBuilder.cpp

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "llvm/Support/Errc.h"
3737
#include "llvm/Support/Regex.h"
3838
#include "llvm/Support/StringSaver.h"
39+
#include "llvm/Support/LockFileManager.h"
3940

4041
using namespace swift;
4142
using FileDependency = SerializationOptions::FileDependency;
@@ -240,7 +241,7 @@ bool ModuleInterfaceBuilder::collectDepsForSerialization(
240241
return false;
241242
}
242243

243-
bool ModuleInterfaceBuilder::buildSwiftModule(
244+
bool ModuleInterfaceBuilder::buildSwiftModuleInternal(
244245
StringRef OutPath, bool ShouldSerializeDeps,
245246
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer) {
246247
bool SubError = false;
@@ -384,3 +385,71 @@ bool ModuleInterfaceBuilder::buildSwiftModule(
384385
});
385386
return !RunSuccess || SubError;
386387
}
388+
389+
bool ModuleInterfaceBuilder::buildSwiftModule(StringRef OutPath,
390+
bool ShouldSerializeDeps,
391+
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
392+
llvm::function_ref<void()> RemarkRebuild) {
393+
394+
while (1) {
395+
// Attempt to lock the interface file. Only one process is allowed to build
396+
// module from the interface so we don't consume too much memory when multiple
397+
// processes are doing the same.
398+
// FIXME: We should surface the module building step to the build system so
399+
// we don't need to synchronize here.
400+
llvm::LockFileManager Locked(interfacePath);
401+
switch (Locked) {
402+
case llvm::LockFileManager::LFS_Error:{
403+
// ModuleInterfaceBuilder takes care of correctness and locks are only
404+
// necessary for performance. Fallback to building the module in case of any lock
405+
// related errors.
406+
if (RemarkRebuild) {
407+
diags.diagnose(SourceLoc(), diag::interface_file_lock_failure,
408+
interfacePath);
409+
}
410+
// Clear out any potential leftover.
411+
Locked.unsafeRemoveLockFile();
412+
LLVM_FALLTHROUGH;
413+
}
414+
case llvm::LockFileManager::LFS_Owned: {
415+
if (RemarkRebuild) {
416+
RemarkRebuild();
417+
}
418+
return buildSwiftModuleInternal(OutPath, ShouldSerializeDeps, ModuleBuffer);
419+
}
420+
case llvm::LockFileManager::LFS_Shared: {
421+
// Someone else is responsible for building the module. Wait for them to
422+
// finish.
423+
switch (Locked.waitForUnlock()) {
424+
case llvm::LockFileManager::Res_Success: {
425+
// This process may have a different module output path. If the other
426+
// process doesn't build the interface to this output path, we should try
427+
// building ourselves.
428+
auto bufferOrError = llvm::MemoryBuffer::getFile(OutPath);
429+
if (!bufferOrError)
430+
continue;
431+
if (ModuleBuffer)
432+
*ModuleBuffer = std::move(bufferOrError.get());
433+
return false;
434+
}
435+
case llvm::LockFileManager::Res_OwnerDied: {
436+
continue; // try again to get the lock.
437+
}
438+
case llvm::LockFileManager::Res_Timeout: {
439+
// Since ModuleInterfaceBuilder takes care of correctness, we try waiting for
440+
// another process to complete the build so swift does not do it done
441+
// twice. If case of timeout, build it ourselves.
442+
if (RemarkRebuild) {
443+
diags.diagnose(SourceLoc(), diag::interface_file_lock_timed_out,
444+
interfacePath);
445+
}
446+
// Clear the lock file so that future invocations can make progress.
447+
Locked.unsafeRemoveLockFile();
448+
continue;
449+
}
450+
}
451+
break;
452+
}
453+
}
454+
}
455+
}

lib/Frontend/ModuleInterfaceBuilder.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ class ModuleInterfaceBuilder {
6767
version::Version &Vers, llvm::StringSaver &SubArgSaver,
6868
SmallVectorImpl<const char *> &SubArgs);
6969

70+
bool buildSwiftModuleInternal(StringRef OutPath, bool ShouldSerializeDeps,
71+
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer);
7072
public:
7173
ModuleInterfaceBuilder(SourceManager &sourceMgr, DiagnosticEngine &diags,
7274
const SearchPathOptions &searchPathOpts,
@@ -102,7 +104,8 @@ class ModuleInterfaceBuilder {
102104
}
103105

104106
bool buildSwiftModule(StringRef OutPath, bool ShouldSerializeDeps,
105-
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer);
107+
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
108+
llvm::function_ref<void()> RemarkRebuild = nullptr);
106109
};
107110

108111
} // end namespace swift

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -950,13 +950,11 @@ class ModuleInterfaceLoaderImpl {
950950
std::unique_ptr<llvm::MemoryBuffer> moduleBuffer;
951951

952952
// We didn't discover a module corresponding to this interface.
953-
954953
// Diagnose that we didn't find a loadable module, if we were asked to.
955-
if (remarkOnRebuildFromInterface) {
954+
auto remarkRebuild = [&]() {
956955
rebuildInfo.diagnose(ctx, diagnosticLoc, moduleName,
957956
interfacePath);
958-
}
959-
957+
};
960958
// If we found an out-of-date .swiftmodule, we still want to add it as
961959
// a dependency of the .swiftinterface. That way if it's updated, but
962960
// the .swiftinterface remains the same, we invalidate the cache and
@@ -966,7 +964,9 @@ class ModuleInterfaceLoaderImpl {
966964
builder.addExtraDependency(modulePath);
967965

968966
if (builder.buildSwiftModule(cachedOutputPath, /*shouldSerializeDeps*/true,
969-
&moduleBuffer))
967+
&moduleBuffer,
968+
remarkOnRebuildFromInterface ? remarkRebuild:
969+
llvm::function_ref<void()>()))
970970
return std::make_error_code(std::errc::invalid_argument);
971971

972972
assert(moduleBuffer &&

test/Driver/lock_interface.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: echo 'public func foo() {}' > %t/Foo.swift
3+
// RUN: %target-swift-frontend-typecheck -emit-module-interface-path %t/Foo.swiftinterface %t/Foo.swift -enable-library-evolution
4+
// RUN: touch %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift
5+
// RUN: echo 'import Foo' > %t/file-01.swift
6+
// RUN: echo 'import Foo' > %t/file-02.swift
7+
// RUN: echo 'import Foo' > %t/file-03.swift
8+
// RUN: %target-swiftc_driver -j20 %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift -I %t -Xfrontend -Rmodule-interface-rebuild &> %t/result.txt
9+
// RUN: %FileCheck %s -check-prefix=CHECK-REBUILD < %t/result.txt
10+
11+
// Ensure we only build Foo module once from the interface
12+
// CHECK-REBUILD: rebuilding module 'Foo' from interface
13+
// CHECK-REBUILD-NOT: rebuilding module 'Foo' from interface

0 commit comments

Comments
 (0)