Skip to content

Commit 881ba10

Browse files
committed
LTO: Keep file handles open for memory mapped files.
On Windows we've observed that if you open a file, write to it, map it into memory and close the file handle, the contents of the memory mapping can sometimes be incorrect. That was what we did when adding an entry to the ThinLTO cache using the TempFile and MemoryBuffer classes, and it was causing intermittent build failures on Chromium's ThinLTO bots on Windows. More details are in the associated Chromium bug (crbug.com/786127). We can prevent this from happening by keeping a handle to the file open while the mapping is active. So this patch changes the mapped_file_region class to duplicate the file handle when mapping the file and close it upon unmapping it. One gotcha is that the file handle that we keep open must not have been created with FILE_FLAG_DELETE_ON_CLOSE, as otherwise the operating system will prevent other processes from opening the file. We can achieve this by avoiding the use of FILE_FLAG_DELETE_ON_CLOSE altogether. Instead, we use SetFileInformationByHandle with FileDispositionInfo to manage the delete-on-close bit. This lets us remove the hack that we used to use to clear the delete-on-close bit on a file opened with FILE_FLAG_DELETE_ON_CLOSE. A downside of using SetFileInformationByHandle/FileDispositionInfo as opposed to FILE_FLAG_DELETE_ON_CLOSE is that it prevents us from using CreateFile to open the file while the flag is set, even within the same process. This doesn't seem to matter for almost every client of TempFile, except for LockFileManager, which calls sys::fs::create_link to create a hard link from the lock file, and in the process of doing so tries to open the file. To prevent this change from breaking LockFileManager I changed it to stop using TempFile by effectively reverting r318550. Differential Revision: https://reviews.llvm.org/D48051 llvm-svn: 334630
1 parent e399f55 commit 881ba10

File tree

7 files changed

+105
-105
lines changed

7 files changed

+105
-105
lines changed

llvm/include/llvm/Support/FileSystem.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,9 @@ class mapped_file_region {
10431043
/// Platform-specific mapping state.
10441044
size_t Size;
10451045
void *Mapping;
1046-
int FD;
1046+
#ifdef _WIN32
1047+
void *FileHandle;
1048+
#endif
10471049
mapmode Mode;
10481050

10491051
std::error_code init(int FD, uint64_t Offset, mapmode Mode);

llvm/include/llvm/Support/LockFileManager.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
#include "llvm/ADT/Optional.h"
1313
#include "llvm/ADT/SmallString.h"
14-
#include "llvm/Support/FileSystem.h"
1514
#include <system_error>
1615
#include <utility> // for std::pair
1716

@@ -54,7 +53,7 @@ class LockFileManager {
5453
private:
5554
SmallString<128> FileName;
5655
SmallString<128> LockFileName;
57-
Optional<sys::fs::TempFile> UniqueLockFile;
56+
SmallString<128> UniqueLockFileName;
5857

5958
Optional<std::pair<std::string, int> > Owner;
6059
std::error_code ErrorCode;

llvm/lib/LTO/Caching.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,14 @@ Expected<NativeObjectCache> lto::localCache(StringRef CacheDirectoryPath,
4040
return AddStreamFn();
4141
}
4242

43-
if (MBOrErr.getError() != errc::no_such_file_or_directory)
43+
// On Windows we can fail to open a cache file with a permission denied
44+
// error. This generally means that another process has requested to delete
45+
// the file while it is still open, but it could also mean that another
46+
// process has opened the file without the sharing permissions we need.
47+
// Since the file is probably being deleted we handle it in the same way as
48+
// if the file did not exist at all.
49+
if (MBOrErr.getError() != errc::no_such_file_or_directory &&
50+
MBOrErr.getError() != errc::permission_denied)
4451
report_fatal_error(Twine("Failed to open cache file ") + EntryPath +
4552
": " + MBOrErr.getError().message() + "\n");
4653

llvm/lib/Support/LockFileManager.cpp

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@
99

1010
#include "llvm/Support/LockFileManager.h"
1111
#include "llvm/ADT/None.h"
12-
#include "llvm/ADT/ScopeExit.h"
1312
#include "llvm/ADT/SmallVector.h"
1413
#include "llvm/ADT/StringExtras.h"
15-
#include "llvm/Config/llvm-config.h"
1614
#include "llvm/Support/Errc.h"
1715
#include "llvm/Support/ErrorOr.h"
1816
#include "llvm/Support/FileSystem.h"
@@ -123,6 +121,39 @@ bool LockFileManager::processStillExecuting(StringRef HostID, int PID) {
123121
return true;
124122
}
125123

124+
namespace {
125+
126+
/// An RAII helper object ensure that the unique lock file is removed.
127+
///
128+
/// Ensures that if there is an error or a signal before we finish acquiring the
129+
/// lock, the unique file will be removed. And if we successfully take the lock,
130+
/// the signal handler is left in place so that signals while the lock is held
131+
/// will remove the unique lock file. The caller should ensure there is a
132+
/// matching call to sys::DontRemoveFileOnSignal when the lock is released.
133+
class RemoveUniqueLockFileOnSignal {
134+
StringRef Filename;
135+
bool RemoveImmediately;
136+
public:
137+
RemoveUniqueLockFileOnSignal(StringRef Name)
138+
: Filename(Name), RemoveImmediately(true) {
139+
sys::RemoveFileOnSignal(Filename, nullptr);
140+
}
141+
142+
~RemoveUniqueLockFileOnSignal() {
143+
if (!RemoveImmediately) {
144+
// Leave the signal handler enabled. It will be removed when the lock is
145+
// released.
146+
return;
147+
}
148+
sys::fs::remove(Filename);
149+
sys::DontRemoveFileOnSignal(Filename);
150+
}
151+
152+
void lockAcquired() { RemoveImmediately = false; }
153+
};
154+
155+
} // end anonymous namespace
156+
126157
LockFileManager::LockFileManager(StringRef FileName)
127158
{
128159
this->FileName = FileName;
@@ -141,22 +172,16 @@ LockFileManager::LockFileManager(StringRef FileName)
141172
return;
142173

143174
// Create a lock file that is unique to this instance.
144-
Expected<sys::fs::TempFile> Temp =
145-
sys::fs::TempFile::create(LockFileName + "-%%%%%%%%");
146-
if (!Temp) {
147-
std::error_code EC = errorToErrorCode(Temp.takeError());
148-
std::string S("failed to create unique file with prefix ");
149-
S.append(LockFileName.str());
175+
UniqueLockFileName = LockFileName;
176+
UniqueLockFileName += "-%%%%%%%%";
177+
int UniqueLockFileID;
178+
if (std::error_code EC = sys::fs::createUniqueFile(
179+
UniqueLockFileName, UniqueLockFileID, UniqueLockFileName)) {
180+
std::string S("failed to create unique file ");
181+
S.append(UniqueLockFileName.str());
150182
setError(EC, S);
151183
return;
152184
}
153-
UniqueLockFile = std::move(*Temp);
154-
155-
// Make sure we discard the temporary file on exit.
156-
auto RemoveTempFile = llvm::make_scope_exit([&]() {
157-
if (Error E = UniqueLockFile->discard())
158-
setError(errorToErrorCode(std::move(E)));
159-
});
160185

161186
// Write our process ID to our unique lock file.
162187
{
@@ -166,46 +191,54 @@ LockFileManager::LockFileManager(StringRef FileName)
166191
return;
167192
}
168193

169-
raw_fd_ostream Out(UniqueLockFile->FD, /*shouldClose=*/false);
194+
raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
170195
Out << HostID << ' ';
171196
#if LLVM_ON_UNIX
172197
Out << getpid();
173198
#else
174199
Out << "1";
175200
#endif
176-
Out.flush();
201+
Out.close();
177202

178203
if (Out.has_error()) {
179204
// We failed to write out PID, so report the error, remove the
180205
// unique lock file, and fail.
181206
std::string S("failed to write to ");
182-
S.append(UniqueLockFile->TmpName);
207+
S.append(UniqueLockFileName.str());
183208
setError(Out.error(), S);
209+
sys::fs::remove(UniqueLockFileName);
184210
return;
185211
}
186212
}
187213

214+
// Clean up the unique file on signal, which also releases the lock if it is
215+
// held since the .lock symlink will point to a nonexistent file.
216+
RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName);
217+
188218
while (true) {
189219
// Create a link from the lock file name. If this succeeds, we're done.
190220
std::error_code EC =
191-
sys::fs::create_link(UniqueLockFile->TmpName, LockFileName);
221+
sys::fs::create_link(UniqueLockFileName, LockFileName);
192222
if (!EC) {
193-
RemoveTempFile.release();
223+
RemoveUniqueFile.lockAcquired();
194224
return;
195225
}
196226

197227
if (EC != errc::file_exists) {
198228
std::string S("failed to create link ");
199229
raw_string_ostream OSS(S);
200-
OSS << LockFileName.str() << " to " << UniqueLockFile->TmpName;
230+
OSS << LockFileName.str() << " to " << UniqueLockFileName.str();
201231
setError(EC, OSS.str());
202232
return;
203233
}
204234

205235
// Someone else managed to create the lock file first. Read the process ID
206236
// from the lock file.
207-
if ((Owner = readLockFile(LockFileName)))
208-
return; // RemoveTempFile will delete out our unique lock file.
237+
if ((Owner = readLockFile(LockFileName))) {
238+
// Wipe out our unique lock file (it's useless now)
239+
sys::fs::remove(UniqueLockFileName);
240+
return;
241+
}
209242

210243
if (!sys::fs::exists(LockFileName)) {
211244
// The previous owner released the lock file before we could read it.
@@ -217,7 +250,7 @@ LockFileManager::LockFileManager(StringRef FileName)
217250
// ownership.
218251
if ((EC = sys::fs::remove(LockFileName))) {
219252
std::string S("failed to remove lockfile ");
220-
S.append(LockFileName.str());
253+
S.append(UniqueLockFileName.str());
221254
setError(EC, S);
222255
return;
223256
}
@@ -252,7 +285,10 @@ LockFileManager::~LockFileManager() {
252285

253286
// Since we own the lock, remove the lock file and our own unique lock file.
254287
sys::fs::remove(LockFileName);
255-
consumeError(UniqueLockFile->discard());
288+
sys::fs::remove(UniqueLockFileName);
289+
// The unique file is now gone, so remove it from the signal handler. This
290+
// matches a sys::RemoveFileOnSignal() in LockFileManager().
291+
sys::DontRemoveFileOnSignal(UniqueLockFileName);
256292
}
257293

258294
LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() {

llvm/lib/Support/Path.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,12 +1129,13 @@ Error TempFile::keep(const Twine &Name) {
11291129
// Always try to close and rename.
11301130
#ifdef _WIN32
11311131
// If we cant't cancel the delete don't rename.
1132-
std::error_code RenameEC = cancelDeleteOnClose(FD);
1132+
auto H = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
1133+
std::error_code RenameEC = setDeleteDisposition(H, false);
11331134
if (!RenameEC)
11341135
RenameEC = rename_fd(FD, Name);
11351136
// If we can't rename, discard the temporary file.
11361137
if (RenameEC)
1137-
removeFD(FD);
1138+
setDeleteDisposition(H, true);
11381139
#else
11391140
std::error_code RenameEC = fs::rename(TmpName, Name);
11401141
// If we can't rename, discard the temporary file.
@@ -1160,7 +1161,8 @@ Error TempFile::keep() {
11601161
Done = true;
11611162

11621163
#ifdef _WIN32
1163-
if (std::error_code EC = cancelDeleteOnClose(FD))
1164+
auto H = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
1165+
if (std::error_code EC = setDeleteDisposition(H, false))
11641166
return errorCodeToError(EC);
11651167
#else
11661168
sys::DontRemoveFileOnSignal(TmpName);

llvm/lib/Support/Unix/Path.inc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -634,8 +634,7 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset,
634634

635635
mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
636636
uint64_t offset, std::error_code &ec)
637-
: Size(length), Mapping(), FD(fd), Mode(mode) {
638-
(void)FD;
637+
: Size(length), Mapping(), Mode(mode) {
639638
(void)Mode;
640639
ec = init(fd, offset, mode);
641640
if (ec)

0 commit comments

Comments
 (0)