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"
10234285Sdim#include "llvm/Support/FileSystem.h"
11234285Sdim#include "llvm/Support/raw_ostream.h"
12234285Sdim#include <fstream>
13249423Sdim#include <sys/stat.h>
14234285Sdim#include <sys/types.h>
15234285Sdim#if LLVM_ON_WIN32
16234285Sdim#include <windows.h>
17234285Sdim#endif
18234285Sdim#if LLVM_ON_UNIX
19234285Sdim#include <unistd.h>
20234285Sdim#endif
21234285Sdimusing namespace llvm;
22234285Sdim
23234285Sdim/// \brief Attempt to read the lock file with the given name, if it exists.
24234285Sdim///
25234285Sdim/// \param LockFileName The name of the lock file to read.
26234285Sdim///
27234285Sdim/// \returns The process ID of the process that owns this lock file
28234285SdimOptional<std::pair<std::string, int> >
29234285SdimLockFileManager::readLockFile(StringRef LockFileName) {
30234285Sdim  // Check whether the lock file exists. If not, clearly there's nothing
31234285Sdim  // to read, so we just return.
32234285Sdim  bool Exists = false;
33234285Sdim  if (sys::fs::exists(LockFileName, Exists) || !Exists)
34249423Sdim    return None;
35234285Sdim
36234285Sdim  // Read the owning host and PID out of the lock file. If it appears that the
37234285Sdim  // owning process is dead, the lock file is invalid.
38234285Sdim  int PID = 0;
39234285Sdim  std::string Hostname;
40234285Sdim  std::ifstream Input(LockFileName.str().c_str());
41234285Sdim  if (Input >> Hostname >> PID && PID > 0 &&
42234285Sdim      processStillExecuting(Hostname, PID))
43234285Sdim    return std::make_pair(Hostname, PID);
44234285Sdim
45234285Sdim  // Delete the lock file. It's invalid anyway.
46234285Sdim  bool Existed;
47234285Sdim  sys::fs::remove(LockFileName, Existed);
48249423Sdim  return None;
49234285Sdim}
50234285Sdim
51234285Sdimbool LockFileManager::processStillExecuting(StringRef Hostname, int PID) {
52243830Sdim#if LLVM_ON_UNIX && !defined(__ANDROID__)
53234285Sdim  char MyHostname[256];
54234285Sdim  MyHostname[255] = 0;
55234285Sdim  MyHostname[0] = 0;
56234285Sdim  gethostname(MyHostname, 255);
57234285Sdim  // Check whether the process is dead. If so, we're done.
58234285Sdim  if (MyHostname == Hostname && getsid(PID) == -1 && errno == ESRCH)
59234285Sdim    return false;
60234285Sdim#endif
61234285Sdim
62234285Sdim  return true;
63234285Sdim}
64234285Sdim
65234285SdimLockFileManager::LockFileManager(StringRef FileName)
66234285Sdim{
67249423Sdim  this->FileName = FileName;
68234285Sdim  LockFileName = FileName;
69234285Sdim  LockFileName += ".lock";
70234285Sdim
71234285Sdim  // If the lock file already exists, don't bother to try to create our own
72234285Sdim  // lock file; it won't work anyway. Just figure out who owns this lock file.
73234285Sdim  if ((Owner = readLockFile(LockFileName)))
74234285Sdim    return;
75234285Sdim
76234285Sdim  // Create a lock file that is unique to this instance.
77234285Sdim  UniqueLockFileName = LockFileName;
78234285Sdim  UniqueLockFileName += "-%%%%%%%%";
79234285Sdim  int UniqueLockFileID;
80234285Sdim  if (error_code EC
81234285Sdim        = sys::fs::unique_file(UniqueLockFileName.str(),
82234285Sdim                                     UniqueLockFileID,
83234285Sdim                                     UniqueLockFileName,
84234285Sdim                                     /*makeAbsolute=*/false)) {
85234285Sdim    Error = EC;
86234285Sdim    return;
87234285Sdim  }
88234285Sdim
89234285Sdim  // Write our process ID to our unique lock file.
90234285Sdim  {
91234285Sdim    raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
92234285Sdim
93234285Sdim#if LLVM_ON_UNIX
94234285Sdim    // FIXME: move getpid() call into LLVM
95234285Sdim    char hostname[256];
96234285Sdim    hostname[255] = 0;
97234285Sdim    hostname[0] = 0;
98234285Sdim    gethostname(hostname, 255);
99234285Sdim    Out << hostname << ' ' << getpid();
100234285Sdim#else
101234285Sdim    Out << "localhost 1";
102234285Sdim#endif
103234285Sdim    Out.close();
104234285Sdim
105234285Sdim    if (Out.has_error()) {
106234285Sdim      // We failed to write out PID, so make up an excuse, remove the
107234285Sdim      // unique lock file, and fail.
108234285Sdim      Error = make_error_code(errc::no_space_on_device);
109234285Sdim      bool Existed;
110234285Sdim      sys::fs::remove(UniqueLockFileName.c_str(), Existed);
111234285Sdim      return;
112234285Sdim    }
113234285Sdim  }
114234285Sdim
115234285Sdim  // Create a hard link from the lock file name. If this succeeds, we're done.
116234285Sdim  error_code EC
117234285Sdim    = sys::fs::create_hard_link(UniqueLockFileName.str(),
118234285Sdim                                      LockFileName.str());
119234285Sdim  if (EC == errc::success)
120234285Sdim    return;
121234285Sdim
122234285Sdim  // Creating the hard link failed.
123234285Sdim
124234285Sdim#ifdef LLVM_ON_UNIX
125234285Sdim  // The creation of the hard link may appear to fail, but if stat'ing the
126234285Sdim  // unique file returns a link count of 2, then we can still declare success.
127234285Sdim  struct stat StatBuf;
128234285Sdim  if (stat(UniqueLockFileName.c_str(), &StatBuf) == 0 &&
129234285Sdim      StatBuf.st_nlink == 2)
130234285Sdim    return;
131234285Sdim#endif
132234285Sdim
133234285Sdim  // Someone else managed to create the lock file first. Wipe out our unique
134234285Sdim  // lock file (it's useless now) and read the process ID from the lock file.
135234285Sdim  bool Existed;
136234285Sdim  sys::fs::remove(UniqueLockFileName.str(), Existed);
137234285Sdim  if ((Owner = readLockFile(LockFileName)))
138234285Sdim    return;
139234285Sdim
140234285Sdim  // There is a lock file that nobody owns; try to clean it up and report
141234285Sdim  // an error.
142234285Sdim  sys::fs::remove(LockFileName.str(), Existed);
143234285Sdim  Error = EC;
144234285Sdim}
145234285Sdim
146234285SdimLockFileManager::LockFileState LockFileManager::getState() const {
147234285Sdim  if (Owner)
148234285Sdim    return LFS_Shared;
149234285Sdim
150234285Sdim  if (Error)
151234285Sdim    return LFS_Error;
152234285Sdim
153234285Sdim  return LFS_Owned;
154234285Sdim}
155234285Sdim
156234285SdimLockFileManager::~LockFileManager() {
157234285Sdim  if (getState() != LFS_Owned)
158234285Sdim    return;
159234285Sdim
160234285Sdim  // Since we own the lock, remove the lock file and our own unique lock file.
161234285Sdim  bool Existed;
162234285Sdim  sys::fs::remove(LockFileName.str(), Existed);
163234285Sdim  sys::fs::remove(UniqueLockFileName.str(), Existed);
164234285Sdim}
165234285Sdim
166234285Sdimvoid LockFileManager::waitForUnlock() {
167234285Sdim  if (getState() != LFS_Shared)
168234285Sdim    return;
169234285Sdim
170234285Sdim#if LLVM_ON_WIN32
171234285Sdim  unsigned long Interval = 1;
172234285Sdim#else
173234285Sdim  struct timespec Interval;
174234285Sdim  Interval.tv_sec = 0;
175234285Sdim  Interval.tv_nsec = 1000000;
176234285Sdim#endif
177251662Sdim  // Don't wait more than five minutes for the file to appear.
178251662Sdim  unsigned MaxSeconds = 300;
179249423Sdim  bool LockFileGone = false;
180234285Sdim  do {
181234285Sdim    // Sleep for the designated interval, to allow the owning process time to
182234285Sdim    // finish up and remove the lock file.
183234285Sdim    // FIXME: Should we hook in to system APIs to get a notification when the
184234285Sdim    // lock file is deleted?
185234285Sdim#if LLVM_ON_WIN32
186234285Sdim    Sleep(Interval);
187234285Sdim#else
188234285Sdim    nanosleep(&Interval, NULL);
189234285Sdim#endif
190234285Sdim    bool Exists = false;
191251662Sdim    bool LockFileJustDisappeared = false;
192251662Sdim
193251662Sdim    // If the lock file is still expected to be there, check whether it still
194251662Sdim    // is.
195249423Sdim    if (!LockFileGone) {
196249423Sdim      if (!sys::fs::exists(LockFileName.str(), Exists) && !Exists) {
197249423Sdim        LockFileGone = true;
198251662Sdim        LockFileJustDisappeared = true;
199249423Sdim        Exists = false;
200249423Sdim      }
201249423Sdim    }
202251662Sdim
203251662Sdim    // If the lock file is no longer there, check if the original file is
204251662Sdim    // available now.
205249423Sdim    if (LockFileGone) {
206251662Sdim      if (!sys::fs::exists(FileName.str(), Exists) && Exists) {
207249423Sdim        return;
208251662Sdim      }
209251662Sdim
210251662Sdim      // The lock file is gone, so now we're waiting for the original file to
211251662Sdim      // show up. If this just happened, reset our waiting intervals and keep
212251662Sdim      // waiting.
213251662Sdim      if (LockFileJustDisappeared) {
214251662Sdim        MaxSeconds = 5;
215251662Sdim
216251662Sdim#if LLVM_ON_WIN32
217251662Sdim        Interval = 1;
218251662Sdim#else
219251662Sdim        Interval.tv_sec = 0;
220251662Sdim        Interval.tv_nsec = 1000000;
221251662Sdim#endif
222251662Sdim        continue;
223251662Sdim      }
224249423Sdim    }
225234285Sdim
226251662Sdim    // If we're looking for the lock file to disappear, but the process
227251662Sdim    // owning the lock died without cleaning up, just bail out.
228251662Sdim    if (!LockFileGone &&
229251662Sdim        !processStillExecuting((*Owner).first, (*Owner).second)) {
230234285Sdim      return;
231251662Sdim    }
232234285Sdim
233234285Sdim    // Exponentially increase the time we wait for the lock to be removed.
234234285Sdim#if LLVM_ON_WIN32
235234285Sdim    Interval *= 2;
236234285Sdim#else
237234285Sdim    Interval.tv_sec *= 2;
238234285Sdim    Interval.tv_nsec *= 2;
239234285Sdim    if (Interval.tv_nsec >= 1000000000) {
240234285Sdim      ++Interval.tv_sec;
241234285Sdim      Interval.tv_nsec -= 1000000000;
242234285Sdim    }
243234285Sdim#endif
244234285Sdim  } while (
245234285Sdim#if LLVM_ON_WIN32
246234285Sdim           Interval < MaxSeconds * 1000
247234285Sdim#else
248234285Sdim           Interval.tv_sec < (time_t)MaxSeconds
249234285Sdim#endif
250234285Sdim           );
251234285Sdim
252234285Sdim  // Give up.
253234285Sdim}
254