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/types.h>
14#include <sys/stat.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 Optional<std::pair<std::string, int> >();
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 Optional<std::pair<std::string, int> >();
49}
50
51bool LockFileManager::processStillExecuting(StringRef Hostname, int PID) {
52// getsid not supported in Android bionic library
53#if LLVM_ON_UNIX && !defined(ANDROID_TARGET_BUILD)
54  char MyHostname[256];
55  MyHostname[255] = 0;
56  MyHostname[0] = 0;
57  gethostname(MyHostname, 255);
58  // Check whether the process is dead. If so, we're done.
59  if (MyHostname == Hostname && getsid(PID) == -1 && errno == ESRCH)
60    return false;
61#endif
62
63  return true;
64}
65
66LockFileManager::LockFileManager(StringRef FileName)
67{
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 an hour for the file to appear.
178  const unsigned MaxSeconds = 3600;
179  do {
180    // Sleep for the designated interval, to allow the owning process time to
181    // finish up and remove the lock file.
182    // FIXME: Should we hook in to system APIs to get a notification when the
183    // lock file is deleted?
184#if LLVM_ON_WIN32
185    Sleep(Interval);
186#else
187    nanosleep(&Interval, NULL);
188#endif
189    // If the file no longer exists, we're done.
190    bool Exists = false;
191    if (!sys::fs::exists(LockFileName.str(), Exists) && !Exists)
192      return;
193
194    if (!processStillExecuting((*Owner).first, (*Owner).second))
195      return;
196
197    // Exponentially increase the time we wait for the lock to be removed.
198#if LLVM_ON_WIN32
199    Interval *= 2;
200#else
201    Interval.tv_sec *= 2;
202    Interval.tv_nsec *= 2;
203    if (Interval.tv_nsec >= 1000000000) {
204      ++Interval.tv_sec;
205      Interval.tv_nsec -= 1000000000;
206    }
207#endif
208  } while (
209#if LLVM_ON_WIN32
210           Interval < MaxSeconds * 1000
211#else
212           Interval.tv_sec < (time_t)MaxSeconds
213#endif
214           );
215
216  // Give up.
217}
218