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