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