Skip to content

Commit 94355be

Browse files
authored
Merge pull request #39888 from artemcm/AtomicModuleTrace
Emit module trace atomically by using LLVM's `LockFileManager`
2 parents 31b44c8 + 2905433 commit 94355be

File tree

2 files changed

+69
-11
lines changed

2 files changed

+69
-11
lines changed

lib/FrontendTool/LoadedModuleTrace.cpp

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@
2323

2424
#include "llvm/ADT/SmallPtrSet.h"
2525
#include "llvm/ADT/SmallString.h"
26+
#include "llvm/Support/Path.h"
2627
#include "llvm/Support/YAMLTraits.h"
28+
#include "llvm/Support/FileUtilities.h"
29+
#include "llvm/Support/LockFileManager.h"
2730

2831
#if !defined(_MSC_VER) && !defined(__MINGW32__)
2932
#include <unistd.h>
@@ -706,15 +709,6 @@ bool swift::emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule,
706709
auto loadedModuleTracePath = input.getLoadedModuleTracePath();
707710
if (loadedModuleTracePath.empty())
708711
return false;
709-
std::error_code EC;
710-
llvm::raw_fd_ostream out(loadedModuleTracePath, EC, llvm::sys::fs::OF_Append);
711-
712-
if (out.has_error() || EC) {
713-
ctxt.Diags.diagnose(SourceLoc(), diag::error_opening_output,
714-
loadedModuleTracePath, EC.message());
715-
out.clear_error();
716-
return true;
717-
}
718712

719713
SmallPtrSet<ModuleDecl *, 32> abiDependencies;
720714
{
@@ -762,7 +756,71 @@ bool swift::emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule,
762756
json::jsonize(jsonOutput, trace, /*Required=*/true);
763757
}
764758
stringBuffer += "\n";
765-
out << stringBuffer;
766759

760+
// If writing to stdout, just perform a normal write.
761+
// If writing to a file, ensure the write is atomic by creating a filesystem lock
762+
// on the output file path.
763+
std::error_code EC;
764+
if (loadedModuleTracePath == "-") {
765+
llvm::raw_fd_ostream out(loadedModuleTracePath, EC, llvm::sys::fs::OF_Append);
766+
if (out.has_error() || EC) {
767+
ctxt.Diags.diagnose(SourceLoc(), diag::error_opening_output,
768+
loadedModuleTracePath, EC.message());
769+
out.clear_error();
770+
return true;
771+
}
772+
out << stringBuffer;
773+
} else {
774+
while (1) {
775+
// Attempt to lock the output file.
776+
// Only one process is allowed to append to this file at a time.
777+
llvm::LockFileManager Locked(loadedModuleTracePath);
778+
switch (Locked) {
779+
case llvm::LockFileManager::LFS_Error:{
780+
// If we error acquiring a lock, we cannot ensure appends
781+
// to the trace file are atomic - cannot ensure output correctness.
782+
ctxt.Diags.diagnose(SourceLoc(), diag::error_opening_output,
783+
loadedModuleTracePath,
784+
"Failed to acquire filesystem lock");
785+
Locked.unsafeRemoveLockFile();
786+
return true;
787+
}
788+
case llvm::LockFileManager::LFS_Owned: {
789+
// Lock acquired, perform the write and release the lock.
790+
llvm::raw_fd_ostream out(loadedModuleTracePath, EC, llvm::sys::fs::OF_Append);
791+
if (out.has_error() || EC) {
792+
ctxt.Diags.diagnose(SourceLoc(), diag::error_opening_output,
793+
loadedModuleTracePath, EC.message());
794+
out.clear_error();
795+
return true;
796+
}
797+
out << stringBuffer;
798+
out.close();
799+
Locked.unsafeRemoveLockFile();
800+
return false;
801+
}
802+
case llvm::LockFileManager::LFS_Shared: {
803+
// Someone else owns the lock on this file, wait.
804+
switch (Locked.waitForUnlock(256)) {
805+
case llvm::LockFileManager::Res_Success:
806+
LLVM_FALLTHROUGH;
807+
case llvm::LockFileManager::Res_OwnerDied: {
808+
continue; // try again to get the lock.
809+
}
810+
case llvm::LockFileManager::Res_Timeout: {
811+
// We could error on timeout to avoid potentially hanging forever, but
812+
// it may be more likely that an interrupted process failed to clear the lock,
813+
// causing other waiting processes to time-out. Let's clear the lock and try
814+
// again right away. If we do start seeing compiler hangs in this location,
815+
// we will need to re-consider.
816+
Locked.unsafeRemoveLockFile();
817+
continue;
818+
}
819+
}
820+
break;
821+
}
822+
}
823+
}
824+
}
767825
return true;
768826
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// Check that this doesn't crash due to a self-cycle. rdar://67435472
2-
// RUN: %target-swift-frontend %s -emit-module -o /dev/null -emit-loaded-module-trace-path /dev/null -I %S/Inputs/imported_modules/SelfImport
2+
// RUN: %target-swift-frontend %s -emit-module -o /dev/null -emit-loaded-module-trace-path - -I %S/Inputs/imported_modules/SelfImport
33

44
import Outer

0 commit comments

Comments
 (0)