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