diff options
Diffstat (limited to 'lib/LTO/Caching.cpp')
| -rw-r--r-- | lib/LTO/Caching.cpp | 80 |
1 files changed, 53 insertions, 27 deletions
diff --git a/lib/LTO/Caching.cpp b/lib/LTO/Caching.cpp index e32e46c4c3c8..dd47eb584b7f 100644 --- a/lib/LTO/Caching.cpp +++ b/lib/LTO/Caching.cpp @@ -14,9 +14,9 @@ #include "llvm/LTO/Caching.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Errc.h" -#include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -36,7 +36,7 @@ Expected<NativeObjectCache> lto::localCache(StringRef CacheDirectoryPath, ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFile(EntryPath); if (MBOrErr) { - AddBuffer(Task, std::move(*MBOrErr)); + AddBuffer(Task, std::move(*MBOrErr), EntryPath); return AddStreamFn(); } @@ -48,54 +48,80 @@ Expected<NativeObjectCache> lto::localCache(StringRef CacheDirectoryPath, // file to the cache and calling AddBuffer to add it to the link. struct CacheStream : NativeObjectStream { AddBufferFn AddBuffer; - std::string TempFilename; + sys::fs::TempFile TempFile; std::string EntryPath; unsigned Task; CacheStream(std::unique_ptr<raw_pwrite_stream> OS, AddBufferFn AddBuffer, - std::string TempFilename, std::string EntryPath, + sys::fs::TempFile TempFile, std::string EntryPath, unsigned Task) : NativeObjectStream(std::move(OS)), AddBuffer(std::move(AddBuffer)), - TempFilename(std::move(TempFilename)), - EntryPath(std::move(EntryPath)), Task(Task) {} + TempFile(std::move(TempFile)), EntryPath(std::move(EntryPath)), + Task(Task) {} ~CacheStream() { - // FIXME: This code could race with the cache pruner, but it is unlikely - // that the cache pruner will choose to remove a newly created file. - - // Make sure the file is closed before committing it. + // Make sure the stream is closed before committing it. OS.reset(); - // This is atomic on POSIX systems. - if (auto EC = sys::fs::rename(TempFilename, EntryPath)) - report_fatal_error(Twine("Failed to rename temporary file ") + - TempFilename + ": " + EC.message() + "\n"); + // Open the file first to avoid racing with a cache pruner. ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = - MemoryBuffer::getFile(EntryPath); + MemoryBuffer::getOpenFile(TempFile.FD, TempFile.TmpName, + /*FileSize*/ -1, + /*RequiresNullTerminator*/ false); if (!MBOrErr) - report_fatal_error(Twine("Failed to open cache file ") + EntryPath + - ": " + MBOrErr.getError().message() + "\n"); - AddBuffer(Task, std::move(*MBOrErr)); + report_fatal_error(Twine("Failed to open new cache file ") + + TempFile.TmpName + ": " + + MBOrErr.getError().message() + "\n"); + + // On POSIX systems, this will atomically replace the destination if + // it already exists. We try to emulate this on Windows, but this may + // fail with a permission denied error (for example, if the destination + // is currently opened by another process that does not give us the + // sharing permissions we need). Since the existing file should be + // semantically equivalent to the one we are trying to write, we give + // AddBuffer a copy of the bytes we wrote in that case. We do this + // instead of just using the existing file, because the pruner might + // delete the file before we get a chance to use it. + Error E = TempFile.keep(EntryPath); + E = handleErrors(std::move(E), [&](const ECError &E) -> Error { + std::error_code EC = E.convertToErrorCode(); + if (EC != errc::permission_denied) + return errorCodeToError(EC); + + auto MBCopy = MemoryBuffer::getMemBufferCopy((*MBOrErr)->getBuffer(), + EntryPath); + MBOrErr = std::move(MBCopy); + + // FIXME: should we consume the discard error? + consumeError(TempFile.discard()); + + return Error::success(); + }); + + if (E) + report_fatal_error(Twine("Failed to rename temporary file ") + + TempFile.TmpName + " to " + EntryPath + ": " + + toString(std::move(E)) + "\n"); + + AddBuffer(Task, std::move(*MBOrErr), EntryPath); } }; return [=](size_t Task) -> std::unique_ptr<NativeObjectStream> { // Write to a temporary to avoid race condition - int TempFD; - SmallString<64> TempFilenameModel, TempFilename; + SmallString<64> TempFilenameModel; sys::path::append(TempFilenameModel, CacheDirectoryPath, "Thin-%%%%%%.tmp.o"); - std::error_code EC = - sys::fs::createUniqueFile(TempFilenameModel, TempFD, TempFilename, - sys::fs::owner_read | sys::fs::owner_write); - if (EC) { - errs() << "Error: " << EC.message() << "\n"; + Expected<sys::fs::TempFile> Temp = sys::fs::TempFile::create( + TempFilenameModel, sys::fs::owner_read | sys::fs::owner_write); + if (!Temp) { + errs() << "Error: " << toString(Temp.takeError()) << "\n"; report_fatal_error("ThinLTO: Can't get a temporary file"); } // This CacheStream will move the temporary file into the cache when done. return llvm::make_unique<CacheStream>( - llvm::make_unique<raw_fd_ostream>(TempFD, /* ShouldClose */ true), - AddBuffer, TempFilename.str(), EntryPath.str(), Task); + llvm::make_unique<raw_fd_ostream>(Temp->FD, /* ShouldClose */ false), + AddBuffer, std::move(*Temp), EntryPath.str(), Task); }; }; } |
