xref: /minix3/external/bsd/llvm/dist/llvm/lib/Support/LockFileManager.cpp (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1f4a2713aSLionel Sambuc //===--- LockFileManager.cpp - File-level Locking Utility------------------===//
2f4a2713aSLionel Sambuc //
3f4a2713aSLionel Sambuc //                     The LLVM Compiler Infrastructure
4f4a2713aSLionel Sambuc //
5f4a2713aSLionel Sambuc // This file is distributed under the University of Illinois Open Source
6f4a2713aSLionel Sambuc // License. See LICENSE.TXT for details.
7f4a2713aSLionel Sambuc //
8f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
9f4a2713aSLionel Sambuc #include "llvm/Support/LockFileManager.h"
10f4a2713aSLionel Sambuc #include "llvm/ADT/STLExtras.h"
11f4a2713aSLionel Sambuc #include "llvm/ADT/StringExtras.h"
12*0a6a1f1dSLionel Sambuc #include "llvm/Support/Errc.h"
13f4a2713aSLionel Sambuc #include "llvm/Support/FileSystem.h"
14f4a2713aSLionel Sambuc #include "llvm/Support/MemoryBuffer.h"
15*0a6a1f1dSLionel Sambuc #include "llvm/Support/Path.h"
16f4a2713aSLionel Sambuc #include "llvm/Support/raw_ostream.h"
17f4a2713aSLionel Sambuc #include <sys/stat.h>
18f4a2713aSLionel Sambuc #include <sys/types.h>
19f4a2713aSLionel Sambuc #if LLVM_ON_WIN32
20f4a2713aSLionel Sambuc #include <windows.h>
21f4a2713aSLionel Sambuc #endif
22f4a2713aSLionel Sambuc #if LLVM_ON_UNIX
23f4a2713aSLionel Sambuc #include <unistd.h>
24f4a2713aSLionel Sambuc #endif
25f4a2713aSLionel Sambuc using namespace llvm;
26f4a2713aSLionel Sambuc 
27f4a2713aSLionel Sambuc /// \brief Attempt to read the lock file with the given name, if it exists.
28f4a2713aSLionel Sambuc ///
29f4a2713aSLionel Sambuc /// \param LockFileName The name of the lock file to read.
30f4a2713aSLionel Sambuc ///
31f4a2713aSLionel Sambuc /// \returns The process ID of the process that owns this lock file
32f4a2713aSLionel Sambuc Optional<std::pair<std::string, int> >
readLockFile(StringRef LockFileName)33f4a2713aSLionel Sambuc LockFileManager::readLockFile(StringRef LockFileName) {
34f4a2713aSLionel Sambuc   // Read the owning host and PID out of the lock file. If it appears that the
35f4a2713aSLionel Sambuc   // owning process is dead, the lock file is invalid.
36*0a6a1f1dSLionel Sambuc   ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
37*0a6a1f1dSLionel Sambuc       MemoryBuffer::getFile(LockFileName);
38*0a6a1f1dSLionel Sambuc   if (!MBOrErr) {
39*0a6a1f1dSLionel Sambuc     sys::fs::remove(LockFileName);
40f4a2713aSLionel Sambuc     return None;
41*0a6a1f1dSLionel Sambuc   }
42*0a6a1f1dSLionel Sambuc   MemoryBuffer &MB = *MBOrErr.get();
43f4a2713aSLionel Sambuc 
44f4a2713aSLionel Sambuc   StringRef Hostname;
45f4a2713aSLionel Sambuc   StringRef PIDStr;
46*0a6a1f1dSLionel Sambuc   std::tie(Hostname, PIDStr) = getToken(MB.getBuffer(), " ");
47f4a2713aSLionel Sambuc   PIDStr = PIDStr.substr(PIDStr.find_first_not_of(" "));
48f4a2713aSLionel Sambuc   int PID;
49*0a6a1f1dSLionel Sambuc   if (!PIDStr.getAsInteger(10, PID)) {
50*0a6a1f1dSLionel Sambuc     auto Owner = std::make_pair(std::string(Hostname), PID);
51*0a6a1f1dSLionel Sambuc     if (processStillExecuting(Owner.first, Owner.second))
52*0a6a1f1dSLionel Sambuc       return Owner;
53*0a6a1f1dSLionel Sambuc   }
54f4a2713aSLionel Sambuc 
55f4a2713aSLionel Sambuc   // Delete the lock file. It's invalid anyway.
56f4a2713aSLionel Sambuc   sys::fs::remove(LockFileName);
57f4a2713aSLionel Sambuc   return None;
58f4a2713aSLionel Sambuc }
59f4a2713aSLionel Sambuc 
processStillExecuting(StringRef Hostname,int PID)60f4a2713aSLionel Sambuc bool LockFileManager::processStillExecuting(StringRef Hostname, int PID) {
61f4a2713aSLionel Sambuc #if LLVM_ON_UNIX && !defined(__ANDROID__)
62f4a2713aSLionel Sambuc   char MyHostname[256];
63f4a2713aSLionel Sambuc   MyHostname[255] = 0;
64f4a2713aSLionel Sambuc   MyHostname[0] = 0;
65f4a2713aSLionel Sambuc   gethostname(MyHostname, 255);
66f4a2713aSLionel Sambuc   // Check whether the process is dead. If so, we're done.
67f4a2713aSLionel Sambuc   if (MyHostname == Hostname && getsid(PID) == -1 && errno == ESRCH)
68f4a2713aSLionel Sambuc     return false;
69f4a2713aSLionel Sambuc #endif
70f4a2713aSLionel Sambuc 
71f4a2713aSLionel Sambuc   return true;
72f4a2713aSLionel Sambuc }
73f4a2713aSLionel Sambuc 
LockFileManager(StringRef FileName)74f4a2713aSLionel Sambuc LockFileManager::LockFileManager(StringRef FileName)
75f4a2713aSLionel Sambuc {
76f4a2713aSLionel Sambuc   this->FileName = FileName;
77*0a6a1f1dSLionel Sambuc   if (std::error_code EC = sys::fs::make_absolute(this->FileName)) {
78*0a6a1f1dSLionel Sambuc     Error = EC;
79*0a6a1f1dSLionel Sambuc     return;
80*0a6a1f1dSLionel Sambuc   }
81*0a6a1f1dSLionel Sambuc   LockFileName = this->FileName;
82f4a2713aSLionel Sambuc   LockFileName += ".lock";
83f4a2713aSLionel Sambuc 
84f4a2713aSLionel Sambuc   // If the lock file already exists, don't bother to try to create our own
85f4a2713aSLionel Sambuc   // lock file; it won't work anyway. Just figure out who owns this lock file.
86f4a2713aSLionel Sambuc   if ((Owner = readLockFile(LockFileName)))
87f4a2713aSLionel Sambuc     return;
88f4a2713aSLionel Sambuc 
89f4a2713aSLionel Sambuc   // Create a lock file that is unique to this instance.
90f4a2713aSLionel Sambuc   UniqueLockFileName = LockFileName;
91f4a2713aSLionel Sambuc   UniqueLockFileName += "-%%%%%%%%";
92f4a2713aSLionel Sambuc   int UniqueLockFileID;
93*0a6a1f1dSLionel Sambuc   if (std::error_code EC = sys::fs::createUniqueFile(
94*0a6a1f1dSLionel Sambuc           UniqueLockFileName.str(), UniqueLockFileID, UniqueLockFileName)) {
95f4a2713aSLionel Sambuc     Error = EC;
96f4a2713aSLionel Sambuc     return;
97f4a2713aSLionel Sambuc   }
98f4a2713aSLionel Sambuc 
99f4a2713aSLionel Sambuc   // Write our process ID to our unique lock file.
100f4a2713aSLionel Sambuc   {
101f4a2713aSLionel Sambuc     raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
102f4a2713aSLionel Sambuc 
103f4a2713aSLionel Sambuc #if LLVM_ON_UNIX
104f4a2713aSLionel Sambuc     // FIXME: move getpid() call into LLVM
105f4a2713aSLionel Sambuc     char hostname[256];
106f4a2713aSLionel Sambuc     hostname[255] = 0;
107f4a2713aSLionel Sambuc     hostname[0] = 0;
108f4a2713aSLionel Sambuc     gethostname(hostname, 255);
109f4a2713aSLionel Sambuc     Out << hostname << ' ' << getpid();
110f4a2713aSLionel Sambuc #else
111f4a2713aSLionel Sambuc     Out << "localhost 1";
112f4a2713aSLionel Sambuc #endif
113f4a2713aSLionel Sambuc     Out.close();
114f4a2713aSLionel Sambuc 
115f4a2713aSLionel Sambuc     if (Out.has_error()) {
116f4a2713aSLionel Sambuc       // We failed to write out PID, so make up an excuse, remove the
117f4a2713aSLionel Sambuc       // unique lock file, and fail.
118f4a2713aSLionel Sambuc       Error = make_error_code(errc::no_space_on_device);
119*0a6a1f1dSLionel Sambuc       sys::fs::remove(UniqueLockFileName.c_str());
120f4a2713aSLionel Sambuc       return;
121f4a2713aSLionel Sambuc     }
122f4a2713aSLionel Sambuc   }
123f4a2713aSLionel Sambuc 
124*0a6a1f1dSLionel Sambuc   while (1) {
125*0a6a1f1dSLionel Sambuc     // Create a link from the lock file name. If this succeeds, we're done.
126*0a6a1f1dSLionel Sambuc     std::error_code EC =
127*0a6a1f1dSLionel Sambuc         sys::fs::create_link(UniqueLockFileName.str(), LockFileName.str());
128*0a6a1f1dSLionel Sambuc     if (!EC)
129f4a2713aSLionel Sambuc       return;
130f4a2713aSLionel Sambuc 
131*0a6a1f1dSLionel Sambuc     if (EC != errc::file_exists) {
132f4a2713aSLionel Sambuc       Error = EC;
133*0a6a1f1dSLionel Sambuc       return;
134*0a6a1f1dSLionel Sambuc     }
135*0a6a1f1dSLionel Sambuc 
136*0a6a1f1dSLionel Sambuc     // Someone else managed to create the lock file first. Read the process ID
137*0a6a1f1dSLionel Sambuc     // from the lock file.
138*0a6a1f1dSLionel Sambuc     if ((Owner = readLockFile(LockFileName))) {
139*0a6a1f1dSLionel Sambuc       // Wipe out our unique lock file (it's useless now)
140*0a6a1f1dSLionel Sambuc       sys::fs::remove(UniqueLockFileName.str());
141*0a6a1f1dSLionel Sambuc       return;
142*0a6a1f1dSLionel Sambuc     }
143*0a6a1f1dSLionel Sambuc 
144*0a6a1f1dSLionel Sambuc     if (!sys::fs::exists(LockFileName.str())) {
145*0a6a1f1dSLionel Sambuc       // The previous owner released the lock file before we could read it.
146*0a6a1f1dSLionel Sambuc       // Try to get ownership again.
147*0a6a1f1dSLionel Sambuc       continue;
148*0a6a1f1dSLionel Sambuc     }
149*0a6a1f1dSLionel Sambuc 
150*0a6a1f1dSLionel Sambuc     // There is a lock file that nobody owns; try to clean it up and get
151*0a6a1f1dSLionel Sambuc     // ownership.
152*0a6a1f1dSLionel Sambuc     if ((EC = sys::fs::remove(LockFileName.str()))) {
153*0a6a1f1dSLionel Sambuc       Error = EC;
154*0a6a1f1dSLionel Sambuc       return;
155*0a6a1f1dSLionel Sambuc     }
156*0a6a1f1dSLionel Sambuc   }
157f4a2713aSLionel Sambuc }
158f4a2713aSLionel Sambuc 
getState() const159f4a2713aSLionel Sambuc LockFileManager::LockFileState LockFileManager::getState() const {
160f4a2713aSLionel Sambuc   if (Owner)
161f4a2713aSLionel Sambuc     return LFS_Shared;
162f4a2713aSLionel Sambuc 
163f4a2713aSLionel Sambuc   if (Error)
164f4a2713aSLionel Sambuc     return LFS_Error;
165f4a2713aSLionel Sambuc 
166f4a2713aSLionel Sambuc   return LFS_Owned;
167f4a2713aSLionel Sambuc }
168f4a2713aSLionel Sambuc 
~LockFileManager()169f4a2713aSLionel Sambuc LockFileManager::~LockFileManager() {
170f4a2713aSLionel Sambuc   if (getState() != LFS_Owned)
171f4a2713aSLionel Sambuc     return;
172f4a2713aSLionel Sambuc 
173f4a2713aSLionel Sambuc   // Since we own the lock, remove the lock file and our own unique lock file.
174*0a6a1f1dSLionel Sambuc   sys::fs::remove(LockFileName.str());
175*0a6a1f1dSLionel Sambuc   sys::fs::remove(UniqueLockFileName.str());
176f4a2713aSLionel Sambuc }
177f4a2713aSLionel Sambuc 
waitForUnlock()178*0a6a1f1dSLionel Sambuc LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() {
179f4a2713aSLionel Sambuc   if (getState() != LFS_Shared)
180*0a6a1f1dSLionel Sambuc     return Res_Success;
181f4a2713aSLionel Sambuc 
182f4a2713aSLionel Sambuc #if LLVM_ON_WIN32
183f4a2713aSLionel Sambuc   unsigned long Interval = 1;
184f4a2713aSLionel Sambuc #else
185f4a2713aSLionel Sambuc   struct timespec Interval;
186f4a2713aSLionel Sambuc   Interval.tv_sec = 0;
187f4a2713aSLionel Sambuc   Interval.tv_nsec = 1000000;
188f4a2713aSLionel Sambuc #endif
189f4a2713aSLionel Sambuc   // Don't wait more than five minutes for the file to appear.
190f4a2713aSLionel Sambuc   unsigned MaxSeconds = 300;
191f4a2713aSLionel Sambuc   bool LockFileGone = false;
192f4a2713aSLionel Sambuc   do {
193f4a2713aSLionel Sambuc     // Sleep for the designated interval, to allow the owning process time to
194f4a2713aSLionel Sambuc     // finish up and remove the lock file.
195f4a2713aSLionel Sambuc     // FIXME: Should we hook in to system APIs to get a notification when the
196f4a2713aSLionel Sambuc     // lock file is deleted?
197f4a2713aSLionel Sambuc #if LLVM_ON_WIN32
198f4a2713aSLionel Sambuc     Sleep(Interval);
199f4a2713aSLionel Sambuc #else
200*0a6a1f1dSLionel Sambuc     nanosleep(&Interval, nullptr);
201f4a2713aSLionel Sambuc #endif
202f4a2713aSLionel Sambuc     bool LockFileJustDisappeared = false;
203f4a2713aSLionel Sambuc 
204f4a2713aSLionel Sambuc     // If the lock file is still expected to be there, check whether it still
205f4a2713aSLionel Sambuc     // is.
206f4a2713aSLionel Sambuc     if (!LockFileGone) {
207*0a6a1f1dSLionel Sambuc       if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) ==
208*0a6a1f1dSLionel Sambuc           errc::no_such_file_or_directory) {
209f4a2713aSLionel Sambuc         LockFileGone = true;
210f4a2713aSLionel Sambuc         LockFileJustDisappeared = true;
211f4a2713aSLionel Sambuc       }
212f4a2713aSLionel Sambuc     }
213f4a2713aSLionel Sambuc 
214f4a2713aSLionel Sambuc     // If the lock file is no longer there, check if the original file is
215f4a2713aSLionel Sambuc     // available now.
216f4a2713aSLionel Sambuc     if (LockFileGone) {
217*0a6a1f1dSLionel Sambuc       if (sys::fs::exists(FileName.str())) {
218*0a6a1f1dSLionel Sambuc         return Res_Success;
219f4a2713aSLionel Sambuc       }
220f4a2713aSLionel Sambuc 
221f4a2713aSLionel Sambuc       // The lock file is gone, so now we're waiting for the original file to
222f4a2713aSLionel Sambuc       // show up. If this just happened, reset our waiting intervals and keep
223f4a2713aSLionel Sambuc       // waiting.
224f4a2713aSLionel Sambuc       if (LockFileJustDisappeared) {
225f4a2713aSLionel Sambuc         MaxSeconds = 5;
226f4a2713aSLionel Sambuc 
227f4a2713aSLionel Sambuc #if LLVM_ON_WIN32
228f4a2713aSLionel Sambuc         Interval = 1;
229f4a2713aSLionel Sambuc #else
230f4a2713aSLionel Sambuc         Interval.tv_sec = 0;
231f4a2713aSLionel Sambuc         Interval.tv_nsec = 1000000;
232f4a2713aSLionel Sambuc #endif
233f4a2713aSLionel Sambuc         continue;
234f4a2713aSLionel Sambuc       }
235f4a2713aSLionel Sambuc     }
236f4a2713aSLionel Sambuc 
237f4a2713aSLionel Sambuc     // If we're looking for the lock file to disappear, but the process
238f4a2713aSLionel Sambuc     // owning the lock died without cleaning up, just bail out.
239f4a2713aSLionel Sambuc     if (!LockFileGone &&
240f4a2713aSLionel Sambuc         !processStillExecuting((*Owner).first, (*Owner).second)) {
241*0a6a1f1dSLionel Sambuc       return Res_OwnerDied;
242f4a2713aSLionel Sambuc     }
243f4a2713aSLionel Sambuc 
244f4a2713aSLionel Sambuc     // Exponentially increase the time we wait for the lock to be removed.
245f4a2713aSLionel Sambuc #if LLVM_ON_WIN32
246f4a2713aSLionel Sambuc     Interval *= 2;
247f4a2713aSLionel Sambuc #else
248f4a2713aSLionel Sambuc     Interval.tv_sec *= 2;
249f4a2713aSLionel Sambuc     Interval.tv_nsec *= 2;
250f4a2713aSLionel Sambuc     if (Interval.tv_nsec >= 1000000000) {
251f4a2713aSLionel Sambuc       ++Interval.tv_sec;
252f4a2713aSLionel Sambuc       Interval.tv_nsec -= 1000000000;
253f4a2713aSLionel Sambuc     }
254f4a2713aSLionel Sambuc #endif
255f4a2713aSLionel Sambuc   } while (
256f4a2713aSLionel Sambuc #if LLVM_ON_WIN32
257f4a2713aSLionel Sambuc            Interval < MaxSeconds * 1000
258f4a2713aSLionel Sambuc #else
259f4a2713aSLionel Sambuc            Interval.tv_sec < (time_t)MaxSeconds
260f4a2713aSLionel Sambuc #endif
261f4a2713aSLionel Sambuc            );
262f4a2713aSLionel Sambuc 
263f4a2713aSLionel Sambuc   // Give up.
264*0a6a1f1dSLionel Sambuc   return Res_Timeout;
265f4a2713aSLionel Sambuc }
266