1234285Sdim//===--- LockFileManager.cpp - File-level Locking Utility------------------===//
2234285Sdim//
3234285Sdim//                     The LLVM Compiler Infrastructure
4234285Sdim//
5234285Sdim// This file is distributed under the University of Illinois Open Source
6234285Sdim// License. See LICENSE.TXT for details.
7234285Sdim//
8234285Sdim//===----------------------------------------------------------------------===//
9234285Sdim#include "llvm/Support/LockFileManager.h"
10263509Sdim#include "llvm/ADT/STLExtras.h"
11263509Sdim#include "llvm/ADT/StringExtras.h"
12234285Sdim#include "llvm/Support/FileSystem.h"
13263509Sdim#include "llvm/Support/MemoryBuffer.h"
14234285Sdim#include "llvm/Support/raw_ostream.h"
15252723Sdim#include <sys/stat.h>
16234285Sdim#include <sys/types.h>
17234285Sdim#if LLVM_ON_WIN32
18234285Sdim#include <windows.h>
19234285Sdim#endif
20234285Sdim#if LLVM_ON_UNIX
21234285Sdim#include <unistd.h>
22234285Sdim#endif
23234285Sdimusing namespace llvm;
24234285Sdim
25234285Sdim/// \brief Attempt to read the lock file with the given name, if it exists.
26234285Sdim///
27234285Sdim/// \param LockFileName The name of the lock file to read.
28234285Sdim///
29234285Sdim/// \returns The process ID of the process that owns this lock file
30234285SdimOptional<std::pair<std::string, int> >
31234285SdimLockFileManager::readLockFile(StringRef LockFileName) {
32234285Sdim  // Check whether the lock file exists. If not, clearly there's nothing
33234285Sdim  // to read, so we just return.
34234285Sdim  bool Exists = false;
35234285Sdim  if (sys::fs::exists(LockFileName, Exists) || !Exists)
36252723Sdim    return None;
37234285Sdim
38234285Sdim  // Read the owning host and PID out of the lock file. If it appears that the
39234285Sdim  // owning process is dead, the lock file is invalid.
40263509Sdim  OwningPtr<MemoryBuffer> MB;
41263509Sdim  if (MemoryBuffer::getFile(LockFileName, MB))
42263509Sdim    return None;
43234285Sdim
44263509Sdim  StringRef Hostname;
45263509Sdim  StringRef PIDStr;
46263509Sdim  tie(Hostname, PIDStr) = getToken(MB->getBuffer(), " ");
47263509Sdim  PIDStr = PIDStr.substr(PIDStr.find_first_not_of(" "));
48263509Sdim  int PID;
49263509Sdim  if (!PIDStr.getAsInteger(10, PID))
50263509Sdim    return std::make_pair(std::string(Hostname), PID);
51263509Sdim
52234285Sdim  // Delete the lock file. It's invalid anyway.
53263509Sdim  sys::fs::remove(LockFileName);
54252723Sdim  return None;
55234285Sdim}
56234285Sdim
57234285Sdimbool LockFileManager::processStillExecuting(StringRef Hostname, int PID) {
58245431Sdim#if LLVM_ON_UNIX && !defined(__ANDROID__)
59234285Sdim  char MyHostname[256];
60234285Sdim  MyHostname[255] = 0;
61234285Sdim  MyHostname[0] = 0;
62234285Sdim  gethostname(MyHostname, 255);
63234285Sdim  // Check whether the process is dead. If so, we're done.
64234285Sdim  if (MyHostname == Hostname && getsid(PID) == -1 && errno == ESRCH)
65234285Sdim    return false;
66234285Sdim#endif
67234285Sdim
68234285Sdim  return true;
69234285Sdim}
70234285Sdim
71234285SdimLockFileManager::LockFileManager(StringRef FileName)
72234285Sdim{
73252723Sdim  this->FileName = FileName;
74234285Sdim  LockFileName = FileName;
75234285Sdim  LockFileName += ".lock";
76234285Sdim
77234285Sdim  // If the lock file already exists, don't bother to try to create our own
78234285Sdim  // lock file; it won't work anyway. Just figure out who owns this lock file.
79234285Sdim  if ((Owner = readLockFile(LockFileName)))
80234285Sdim    return;
81234285Sdim
82234285Sdim  // Create a lock file that is unique to this instance.
83234285Sdim  UniqueLockFileName = LockFileName;
84234285Sdim  UniqueLockFileName += "-%%%%%%%%";
85234285Sdim  int UniqueLockFileID;
86234285Sdim  if (error_code EC
87263509Sdim        = sys::fs::createUniqueFile(UniqueLockFileName.str(),
88263509Sdim                                    UniqueLockFileID,
89263509Sdim                                    UniqueLockFileName)) {
90234285Sdim    Error = EC;
91234285Sdim    return;
92234285Sdim  }
93234285Sdim
94234285Sdim  // Write our process ID to our unique lock file.
95234285Sdim  {
96234285Sdim    raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
97234285Sdim
98234285Sdim#if LLVM_ON_UNIX
99234285Sdim    // FIXME: move getpid() call into LLVM
100234285Sdim    char hostname[256];
101234285Sdim    hostname[255] = 0;
102234285Sdim    hostname[0] = 0;
103234285Sdim    gethostname(hostname, 255);
104234285Sdim    Out << hostname << ' ' << getpid();
105234285Sdim#else
106234285Sdim    Out << "localhost 1";
107234285Sdim#endif
108234285Sdim    Out.close();
109234285Sdim
110234285Sdim    if (Out.has_error()) {
111234285Sdim      // We failed to write out PID, so make up an excuse, remove the
112234285Sdim      // unique lock file, and fail.
113234285Sdim      Error = make_error_code(errc::no_space_on_device);
114234285Sdim      bool Existed;
115234285Sdim      sys::fs::remove(UniqueLockFileName.c_str(), Existed);
116234285Sdim      return;
117234285Sdim    }
118234285Sdim  }
119234285Sdim
120234285Sdim  // Create a hard link from the lock file name. If this succeeds, we're done.
121234285Sdim  error_code EC
122234285Sdim    = sys::fs::create_hard_link(UniqueLockFileName.str(),
123234285Sdim                                      LockFileName.str());
124234285Sdim  if (EC == errc::success)
125234285Sdim    return;
126234285Sdim
127234285Sdim  // Creating the hard link failed.
128234285Sdim
129234285Sdim#ifdef LLVM_ON_UNIX
130234285Sdim  // The creation of the hard link may appear to fail, but if stat'ing the
131234285Sdim  // unique file returns a link count of 2, then we can still declare success.
132234285Sdim  struct stat StatBuf;
133234285Sdim  if (stat(UniqueLockFileName.c_str(), &StatBuf) == 0 &&
134234285Sdim      StatBuf.st_nlink == 2)
135234285Sdim    return;
136234285Sdim#endif
137234285Sdim
138234285Sdim  // Someone else managed to create the lock file first. Wipe out our unique
139234285Sdim  // lock file (it's useless now) and read the process ID from the lock file.
140234285Sdim  bool Existed;
141234285Sdim  sys::fs::remove(UniqueLockFileName.str(), Existed);
142234285Sdim  if ((Owner = readLockFile(LockFileName)))
143234285Sdim    return;
144234285Sdim
145234285Sdim  // There is a lock file that nobody owns; try to clean it up and report
146234285Sdim  // an error.
147234285Sdim  sys::fs::remove(LockFileName.str(), Existed);
148234285Sdim  Error = EC;
149234285Sdim}
150234285Sdim
151234285SdimLockFileManager::LockFileState LockFileManager::getState() const {
152234285Sdim  if (Owner)
153234285Sdim    return LFS_Shared;
154234285Sdim
155234285Sdim  if (Error)
156234285Sdim    return LFS_Error;
157234285Sdim
158234285Sdim  return LFS_Owned;
159234285Sdim}
160234285Sdim
161234285SdimLockFileManager::~LockFileManager() {
162234285Sdim  if (getState() != LFS_Owned)
163234285Sdim    return;
164234285Sdim
165234285Sdim  // Since we own the lock, remove the lock file and our own unique lock file.
166234285Sdim  bool Existed;
167234285Sdim  sys::fs::remove(LockFileName.str(), Existed);
168234285Sdim  sys::fs::remove(UniqueLockFileName.str(), Existed);
169234285Sdim}
170234285Sdim
171234285Sdimvoid LockFileManager::waitForUnlock() {
172234285Sdim  if (getState() != LFS_Shared)
173234285Sdim    return;
174234285Sdim
175234285Sdim#if LLVM_ON_WIN32
176234285Sdim  unsigned long Interval = 1;
177234285Sdim#else
178234285Sdim  struct timespec Interval;
179234285Sdim  Interval.tv_sec = 0;
180234285Sdim  Interval.tv_nsec = 1000000;
181234285Sdim#endif
182252723Sdim  // Don't wait more than five minutes for the file to appear.
183252723Sdim  unsigned MaxSeconds = 300;
184252723Sdim  bool LockFileGone = false;
185234285Sdim  do {
186234285Sdim    // Sleep for the designated interval, to allow the owning process time to
187234285Sdim    // finish up and remove the lock file.
188234285Sdim    // FIXME: Should we hook in to system APIs to get a notification when the
189234285Sdim    // lock file is deleted?
190234285Sdim#if LLVM_ON_WIN32
191234285Sdim    Sleep(Interval);
192234285Sdim#else
193234285Sdim    nanosleep(&Interval, NULL);
194234285Sdim#endif
195234285Sdim    bool Exists = false;
196252723Sdim    bool LockFileJustDisappeared = false;
197234285Sdim
198252723Sdim    // If the lock file is still expected to be there, check whether it still
199252723Sdim    // is.
200252723Sdim    if (!LockFileGone) {
201252723Sdim      if (!sys::fs::exists(LockFileName.str(), Exists) && !Exists) {
202252723Sdim        LockFileGone = true;
203252723Sdim        LockFileJustDisappeared = true;
204252723Sdim        Exists = false;
205252723Sdim      }
206252723Sdim    }
207252723Sdim
208252723Sdim    // If the lock file is no longer there, check if the original file is
209252723Sdim    // available now.
210252723Sdim    if (LockFileGone) {
211252723Sdim      if (!sys::fs::exists(FileName.str(), Exists) && Exists) {
212252723Sdim        return;
213252723Sdim      }
214252723Sdim
215252723Sdim      // The lock file is gone, so now we're waiting for the original file to
216252723Sdim      // show up. If this just happened, reset our waiting intervals and keep
217252723Sdim      // waiting.
218252723Sdim      if (LockFileJustDisappeared) {
219252723Sdim        MaxSeconds = 5;
220252723Sdim
221252723Sdim#if LLVM_ON_WIN32
222252723Sdim        Interval = 1;
223252723Sdim#else
224252723Sdim        Interval.tv_sec = 0;
225252723Sdim        Interval.tv_nsec = 1000000;
226252723Sdim#endif
227252723Sdim        continue;
228252723Sdim      }
229252723Sdim    }
230252723Sdim
231252723Sdim    // If we're looking for the lock file to disappear, but the process
232252723Sdim    // owning the lock died without cleaning up, just bail out.
233252723Sdim    if (!LockFileGone &&
234252723Sdim        !processStillExecuting((*Owner).first, (*Owner).second)) {
235234285Sdim      return;
236252723Sdim    }
237234285Sdim
238234285Sdim    // Exponentially increase the time we wait for the lock to be removed.
239234285Sdim#if LLVM_ON_WIN32
240234285Sdim    Interval *= 2;
241234285Sdim#else
242234285Sdim    Interval.tv_sec *= 2;
243234285Sdim    Interval.tv_nsec *= 2;
244234285Sdim    if (Interval.tv_nsec >= 1000000000) {
245234285Sdim      ++Interval.tv_sec;
246234285Sdim      Interval.tv_nsec -= 1000000000;
247234285Sdim    }
248234285Sdim#endif
249234285Sdim  } while (
250234285Sdim#if LLVM_ON_WIN32
251234285Sdim           Interval < MaxSeconds * 1000
252234285Sdim#else
253234285Sdim           Interval.tv_sec < (time_t)MaxSeconds
254234285Sdim#endif
255234285Sdim           );
256234285Sdim
257234285Sdim  // Give up.
258234285Sdim}
259