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