|
23 | 23 |
|
24 | 24 | #include "llvm/ADT/SmallPtrSet.h"
|
25 | 25 | #include "llvm/ADT/SmallString.h"
|
| 26 | +#include "llvm/Support/Path.h" |
26 | 27 | #include "llvm/Support/YAMLTraits.h"
|
| 28 | +#include "llvm/Support/FileUtilities.h" |
| 29 | +#include "llvm/Support/LockFileManager.h" |
27 | 30 |
|
28 | 31 | #if !defined(_MSC_VER) && !defined(__MINGW32__)
|
29 | 32 | #include <unistd.h>
|
@@ -706,15 +709,6 @@ bool swift::emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule,
|
706 | 709 | auto loadedModuleTracePath = input.getLoadedModuleTracePath();
|
707 | 710 | if (loadedModuleTracePath.empty())
|
708 | 711 | 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 |
| - } |
718 | 712 |
|
719 | 713 | SmallPtrSet<ModuleDecl *, 32> abiDependencies;
|
720 | 714 | {
|
@@ -762,7 +756,71 @@ bool swift::emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule,
|
762 | 756 | json::jsonize(jsonOutput, trace, /*Required=*/true);
|
763 | 757 | }
|
764 | 758 | stringBuffer += "\n";
|
765 |
| - out << stringBuffer; |
766 | 759 |
|
| 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 | + } |
767 | 825 | return true;
|
768 | 826 | }
|
0 commit comments