LTO: Try to open cache files before renaming them.

It appears that a potential race between the cache client and the cache
pruner that I thought was unlikely actually happened in practice [1].
Try to avoid the race condition by opening the temporary file before
renaming it. Do this only on non-Windows platforms because we cannot
rename open files on Windows using the sys::fs::rename function.

[1] https://luci-logdog.appspot.com/v/?s=chromium%2Fbb%2Fchromium.memory%2FLinux_CFI%2F1610%2F%2B%2Frecipes%2Fsteps%2Fcompile%2F0%2Fstdout

Differential Revision: https://reviews.llvm.org/D37410

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@312567 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Peter Collingbourne 2017-09-05 19:51:38 +00:00
parent 8c5b337a87
commit a3886c11ee
4 changed files with 31 additions and 13 deletions

View File

@ -24,12 +24,13 @@ namespace lto {
/// This type defines the callback to add a pre-existing native object file
/// (e.g. in a cache).
///
/// MB->getBufferIdentifier() is a valid path for the file at the time that it
/// was opened, but clients should prefer to access MB directly in order to
/// avoid a potential race condition.
/// Path is generally expected to be a valid path for the file at the point when
/// the AddBufferFn function is called, but clients should prefer to access MB
/// directly in order to avoid a potential race condition.
///
/// Buffer callbacks must be thread safe.
typedef std::function<void(unsigned Task, std::unique_ptr<MemoryBuffer> MB)>
typedef std::function<void(unsigned Task, std::unique_ptr<MemoryBuffer> MB,
StringRef Path)>
AddBufferFn;
/// Create a local file system cache which uses the given cache directory and

View File

@ -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();
}
@ -60,22 +60,37 @@ Expected<NativeObjectCache> lto::localCache(StringRef CacheDirectoryPath,
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.
OS.reset();
// This is atomic on POSIX systems.
#ifdef _WIN32
// Rename the file first on Windows because we cannot rename an open
// file on that platform using the sys::fs::rename function.
// 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.
// We should look at using the SetFileInformationByHandle function to
// rename the file while it is open.
if (auto EC = sys::fs::rename(TempFilename, EntryPath))
report_fatal_error(Twine("Failed to rename temporary file ") +
TempFilename + ": " + EC.message() + "\n");
ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
MemoryBuffer::getFile(EntryPath);
#else
// Open the file first to avoid racing with a cache pruner.
ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
MemoryBuffer::getFile(TempFilename);
// 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");
#endif
if (!MBOrErr)
report_fatal_error(Twine("Failed to open cache file ") + EntryPath +
": " + MBOrErr.getError().message() + "\n");
AddBuffer(Task, std::move(*MBOrErr));
AddBuffer(Task, std::move(*MBOrErr), EntryPath);
}
};

View File

@ -908,10 +908,11 @@ static ld_plugin_status allSymbolsReadHook() {
llvm::make_unique<llvm::raw_fd_ostream>(FD, true));
};
auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB,
StringRef Path) {
// Note that this requires that the memory buffers provided to AddBuffer are
// backed by a file.
Filenames[Task] = MB->getBufferIdentifier();
Filenames[Task] = Path;
};
NativeObjectCache Cache;

View File

@ -296,7 +296,8 @@ static int run(int argc, char **argv) {
return llvm::make_unique<lto::NativeObjectStream>(std::move(S));
};
auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB,
StringRef Path) {
*AddStream(Task)->OS << MB->getBuffer();
};