llvm.org GIT mirror llvm / 6965c8b
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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@334630 91177308-0d34-0410-b5e6-96231b3b80d8 Peter Collingbourne 2 years ago
7 changed file(s) with 108 addition(s) and 108 deletion(s). Raw diff Collapse all Expand all
10421042 /// Platform-specific mapping state.
10431043 size_t Size;
10441044 void *Mapping;
1045 int FD;
1045 #ifdef _WIN32
1046 void *FileHandle;
1047 #endif
10461048 mapmode Mode;
10471049
10481050 std::error_code init(int FD, uint64_t Offset, mapmode Mode);
1010
1111 #include "llvm/ADT/Optional.h"
1212 #include "llvm/ADT/SmallString.h"
13 #include "llvm/Support/FileSystem.h"
1413 #include
1514 #include // for std::pair
1615
5352 private:
5453 SmallString<128> FileName;
5554 SmallString<128> LockFileName;
56 Optional UniqueLockFile;
55 SmallString<128> UniqueLockFileName;
5756
5857 Optional > Owner;
5958 std::error_code ErrorCode;
3939 return AddStreamFn();
4040 }
4141
42 if (MBOrErr.getError() != errc::no_such_file_or_directory)
42 // On Windows we can fail to open a cache file with a permission denied
43 // error. This generally means that another process has requested to delete
44 // the file while it is still open, but it could also mean that another
45 // process has opened the file without the sharing permissions we need.
46 // Since the file is probably being deleted we handle it in the same way as
47 // if the file did not exist at all.
48 if (MBOrErr.getError() != errc::no_such_file_or_directory &&
49 MBOrErr.getError() != errc::permission_denied)
4350 report_fatal_error(Twine("Failed to open cache file ") + EntryPath +
4451 ": " + MBOrErr.getError().message() + "\n");
4552
88
99 #include "llvm/Support/LockFileManager.h"
1010 #include "llvm/ADT/None.h"
11 #include "llvm/ADT/ScopeExit.h"
1211 #include "llvm/ADT/SmallVector.h"
1312 #include "llvm/ADT/StringExtras.h"
14 #include "llvm/Config/llvm-config.h"
1513 #include "llvm/Support/Errc.h"
1614 #include "llvm/Support/ErrorOr.h"
1715 #include "llvm/Support/FileSystem.h"
122120 return true;
123121 }
124122
123 namespace {
124
125 /// An RAII helper object ensure that the unique lock file is removed.
126 ///
127 /// Ensures that if there is an error or a signal before we finish acquiring the
128 /// lock, the unique file will be removed. And if we successfully take the lock,
129 /// the signal handler is left in place so that signals while the lock is held
130 /// will remove the unique lock file. The caller should ensure there is a
131 /// matching call to sys::DontRemoveFileOnSignal when the lock is released.
132 class RemoveUniqueLockFileOnSignal {
133 StringRef Filename;
134 bool RemoveImmediately;
135 public:
136 RemoveUniqueLockFileOnSignal(StringRef Name)
137 : Filename(Name), RemoveImmediately(true) {
138 sys::RemoveFileOnSignal(Filename, nullptr);
139 }
140
141 ~RemoveUniqueLockFileOnSignal() {
142 if (!RemoveImmediately) {
143 // Leave the signal handler enabled. It will be removed when the lock is
144 // released.
145 return;
146 }
147 sys::fs::remove(Filename);
148 sys::DontRemoveFileOnSignal(Filename);
149 }
150
151 void lockAcquired() { RemoveImmediately = false; }
152 };
153
154 } // end anonymous namespace
155
125156 LockFileManager::LockFileManager(StringRef FileName)
126157 {
127158 this->FileName = FileName;
140171 return;
141172
142173 // Create a lock file that is unique to this instance.
143 Expected Temp =
144 sys::fs::TempFile::create(LockFileName + "-%%%%%%%%");
145 if (!Temp) {
146 std::error_code EC = errorToErrorCode(Temp.takeError());
147 std::string S("failed to create unique file with prefix ");
148 S.append(LockFileName.str());
174 UniqueLockFileName = LockFileName;
175 UniqueLockFileName += "-%%%%%%%%";
176 int UniqueLockFileID;
177 if (std::error_code EC = sys::fs::createUniqueFile(
178 UniqueLockFileName, UniqueLockFileID, UniqueLockFileName)) {
179 std::string S("failed to create unique file ");
180 S.append(UniqueLockFileName.str());
149181 setError(EC, S);
150182 return;
151183 }
152 UniqueLockFile = std::move(*Temp);
153
154 // Make sure we discard the temporary file on exit.
155 auto RemoveTempFile = llvm::make_scope_exit([&]() {
156 if (Error E = UniqueLockFile->discard())
157 setError(errorToErrorCode(std::move(E)));
158 });
159184
160185 // Write our process ID to our unique lock file.
161186 {
165190 return;
166191 }
167192
168 raw_fd_ostream Out(UniqueLockFile->FD, /*shouldClose=*/false);
193 raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
169194 Out << HostID << ' ';
170195 #if LLVM_ON_UNIX
171196 Out << getpid();
172197 #else
173198 Out << "1";
174199 #endif
175 Out.flush();
200 Out.close();
176201
177202 if (Out.has_error()) {
178203 // We failed to write out PID, so report the error, remove the
179204 // unique lock file, and fail.
180205 std::string S("failed to write to ");
181 S.append(UniqueLockFile->TmpName);
206 S.append(UniqueLockFileName.str());
182207 setError(Out.error(), S);
183 return;
184 }
185 }
208 sys::fs::remove(UniqueLockFileName);
209 return;
210 }
211 }
212
213 // Clean up the unique file on signal, which also releases the lock if it is
214 // held since the .lock symlink will point to a nonexistent file.
215 RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName);
186216
187217 while (true) {
188218 // Create a link from the lock file name. If this succeeds, we're done.
189219 std::error_code EC =
190 sys::fs::create_link(UniqueLockFile->TmpName, LockFileName);
220 sys::fs::create_link(UniqueLockFileName, LockFileName);
191221 if (!EC) {
192 RemoveTempFile.release();
222 RemoveUniqueFile.lockAcquired();
193223 return;
194224 }
195225
196226 if (EC != errc::file_exists) {
197227 std::string S("failed to create link ");
198228 raw_string_ostream OSS(S);
199 OSS << LockFileName.str() << " to " << UniqueLockFile->TmpName;
229 OSS << LockFileName.str() << " to " << UniqueLockFileName.str();
200230 setError(EC, OSS.str());
201231 return;
202232 }
203233
204234 // Someone else managed to create the lock file first. Read the process ID
205235 // from the lock file.
206 if ((Owner = readLockFile(LockFileName)))
207 return; // RemoveTempFile will delete out our unique lock file.
236 if ((Owner = readLockFile(LockFileName))) {
237 // Wipe out our unique lock file (it's useless now)
238 sys::fs::remove(UniqueLockFileName);
239 return;
240 }
208241
209242 if (!sys::fs::exists(LockFileName)) {
210243 // The previous owner released the lock file before we could read it.
216249 // ownership.
217250 if ((EC = sys::fs::remove(LockFileName))) {
218251 std::string S("failed to remove lockfile ");
219 S.append(LockFileName.str());
252 S.append(UniqueLockFileName.str());
220253 setError(EC, S);
221254 return;
222255 }
251284
252285 // Since we own the lock, remove the lock file and our own unique lock file.
253286 sys::fs::remove(LockFileName);
254 consumeError(UniqueLockFile->discard());
287 sys::fs::remove(UniqueLockFileName);
288 // The unique file is now gone, so remove it from the signal handler. This
289 // matches a sys::RemoveFileOnSignal() in LockFileManager().
290 sys::DontRemoveFileOnSignal(UniqueLockFileName);
255291 }
256292
257293 LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() {
11281128 // Always try to close and rename.
11291129 #ifdef _WIN32
11301130 // If we cant't cancel the delete don't rename.
1131 std::error_code RenameEC = cancelDeleteOnClose(FD);
1131 auto H = reinterpret_cast(_get_osfhandle(FD));
1132 std::error_code RenameEC = setDeleteDisposition(H, false);
11321133 if (!RenameEC)
11331134 RenameEC = rename_fd(FD, Name);
11341135 // If we can't rename, discard the temporary file.
11351136 if (RenameEC)
1136 removeFD(FD);
1137 setDeleteDisposition(H, true);
11371138 #else
11381139 std::error_code RenameEC = fs::rename(TmpName, Name);
11391140 // If we can't rename, discard the temporary file.
11591160 Done = true;
11601161
11611162 #ifdef _WIN32
1162 if (std::error_code EC = cancelDeleteOnClose(FD))
1163 auto H = reinterpret_cast(_get_osfhandle(FD));
1164 if (std::error_code EC = setDeleteDisposition(H, false))
11631165 return errorCodeToError(EC);
11641166 #else
11651167 sys::DontRemoveFileOnSignal(TmpName);
633633
634634 mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
635635 uint64_t offset, std::error_code &ec)
636 : Size(length), Mapping(), FD(fd), Mode(mode) {
637 (void)FD;
636 : Size(length), Mapping(), Mode(mode) {
638637 (void)Mode;
639638 ec = init(fd, offset, mode);
640639 if (ec)
400400 if (!SetFileInformationByHandle(Handle, FileDispositionInfo, &Disposition,
401401 sizeof(Disposition)))
402402 return mapWindowsError(::GetLastError());
403 return std::error_code();
404 }
405
406 static std::error_code removeFD(int FD) {
407 HANDLE Handle = reinterpret_cast(_get_osfhandle(FD));
408 return setDeleteDisposition(Handle, true);
409 }
410
411 /// In order to handle temporary files we want the following properties
412 ///
413 /// * The temporary file is deleted on crashes
414 /// * We can use (read, rename, etc) the temporary file.
415 /// * We can cancel the delete to keep the file.
416 ///
417 /// Using FILE_DISPOSITION_INFO with DeleteFile=true will create a file that is
418 /// deleted on close, but it has a few problems:
419 ///
420 /// * The file cannot be used. An attempt to open or rename the file will fail.
421 /// This makes the temporary file almost useless, as it cannot be part of
422 /// any other CreateFileW call in the current or in another process.
423 /// * It is not atomic. A crash just after CreateFileW or just after canceling
424 /// the delete will leave the file on disk.
425 ///
426 /// Using FILE_FLAG_DELETE_ON_CLOSE solves the first issues and the first part
427 /// of the second one, but there is no way to cancel it in place. What works is
428 /// to create a second handle to prevent the deletion, close the first one and
429 /// then clear DeleteFile with SetFileInformationByHandle. This requires
430 /// changing the handle and file descriptor the caller uses.
431 static std::error_code cancelDeleteOnClose(int &FD) {
432 HANDLE Handle = reinterpret_cast(_get_osfhandle(FD));
433 SmallVector Name;
434 if (std::error_code EC = realPathFromHandle(Handle, Name))
435 return EC;
436 HANDLE NewHandle =
437 ::CreateFileW(Name.data(), GENERIC_READ | GENERIC_WRITE | DELETE,
438 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
439 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
440 if (NewHandle == INVALID_HANDLE_VALUE)
441 return mapWindowsError(::GetLastError());
442 if (close(FD))
443 return mapWindowsError(::GetLastError());
444
445 if (std::error_code EC = setDeleteDisposition(NewHandle, false))
446 return EC;
447
448 FD = ::_open_osfhandle(intptr_t(NewHandle), 0);
449 if (FD == -1) {
450 ::CloseHandle(NewHandle);
451 return mapWindowsError(ERROR_INVALID_HANDLE);
452 }
453403 return std::error_code();
454404 }
455405
825775
826776 std::error_code mapped_file_region::init(int FD, uint64_t Offset,
827777 mapmode Mode) {
828 this->FD = FD;
829778 this->Mode = Mode;
830 HANDLE FileHandle = reinterpret_cast(_get_osfhandle(FD));
831 if (FileHandle == INVALID_HANDLE_VALUE)
779 HANDLE OrigFileHandle = reinterpret_cast(_get_osfhandle(FD));
780 if (OrigFileHandle == INVALID_HANDLE_VALUE)
832781 return make_error_code(errc::bad_file_descriptor);
833782
834783 DWORD flprotect;
839788 }
840789
841790 HANDLE FileMappingHandle =
842 ::CreateFileMappingW(FileHandle, 0, flprotect,
791 ::CreateFileMappingW(OrigFileHandle, 0, flprotect,
843792 Hi_32(Size),
844793 Lo_32(Size),
845794 0);
877826 Size = mbi.RegionSize;
878827 }
879828
880 // Close all the handles except for the view. It will keep the other handles
881 // alive.
829 // Close the file mapping handle, as it's kept alive by the file mapping. But
830 // neither the file mapping nor the file mapping handle keep the file handle
831 // alive, so we need to keep a reference to the file in case all other handles
832 // are closed and the file is deleted, which may cause invalid data to be read
833 // from the file.
882834 ::CloseHandle(FileMappingHandle);
835 if (!::DuplicateHandle(::GetCurrentProcess(), OrigFileHandle,
836 ::GetCurrentProcess(), &FileHandle, 0, 0,
837 DUPLICATE_SAME_ACCESS)) {
838 std::error_code ec = mapWindowsError(GetLastError());
839 ::UnmapViewOfFile(Mapping);
840 return ec;
841 }
842
883843 return std::error_code();
884844 }
885845
901861 // flushed and subsequent process's attempts to read a file can return
902862 // invalid data. Calling FlushFileBuffers on the write handle is
903863 // sufficient to ensure that this bug is not triggered.
904 HANDLE FileHandle = reinterpret_cast(_get_osfhandle(FD));
905 if (FileHandle != INVALID_HANDLE_VALUE)
906 ::FlushFileBuffers(FileHandle);
864 ::FlushFileBuffers(FileHandle);
907865 }
866
867 ::CloseHandle(FileHandle);
908868 }
909869 }
910870
10531013 return mapWindowsError(ERROR_INVALID_HANDLE);
10541014 }
10551015 return std::error_code();
1056 }
1057
1058 static DWORD nativeOpenFlags(OpenFlags Flags) {
1059 DWORD Result = 0;
1060 if (Flags & OF_Delete)
1061 Result |= FILE_FLAG_DELETE_ON_CLOSE;
1062
1063 if (Result == 0)
1064 Result = FILE_ATTRIBUTE_NORMAL;
1065 return Result;
10661016 }
10671017
10681018 static DWORD nativeDisposition(CreationDisposition Disp, OpenFlags Flags) {
11411091 assert((!(Disp == CD_CreateNew) || !(Flags & OF_Append)) &&
11421092 "Cannot specify both 'CreateNew' and 'Append' file creation flags!");
11431093
1144 DWORD NativeFlags = nativeOpenFlags(Flags);
11451094 DWORD NativeDisp = nativeDisposition(Disp, Flags);
11461095 DWORD NativeAccess = nativeAccess(Access, Flags);
11471096
11511100
11521101 file_t Result;
11531102 std::error_code EC = openNativeFileInternal(
1154 Name, Result, NativeDisp, NativeAccess, NativeFlags, Inherit);
1103 Name, Result, NativeDisp, NativeAccess, FILE_ATTRIBUTE_NORMAL, Inherit);
11551104 if (EC)
11561105 return errorCodeToError(EC);
1106 if (Flags & OF_Delete) {
1107 if ((EC = setDeleteDisposition(Result, true))) {
1108 ::CloseHandle(Result);
1109 return errorCodeToError(EC);
1110 }
1111 }
11571112 return Result;
11581113 }
11591114