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