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