1 |
commit: f083637554bf5668ec856c56cfaaa76bb343d941 |
2 |
Author: Mike Frysinger <vapier <AT> gentoo <DOT> org> |
3 |
AuthorDate: Sat Oct 10 01:51:23 2015 +0000 |
4 |
Commit: Mike Frysinger <vapier <AT> gentoo <DOT> org> |
5 |
CommitDate: Sat Oct 10 23:54:36 2015 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/catalyst.git/commit/?id=f0836375 |
7 |
|
8 |
lock: gut & replace with snakeoil |
9 |
|
10 |
The hardlink logic is unused, so start by deleting all of that. |
11 |
If someone wants that (multiple builds on NFS?), we can look at |
12 |
restoring it. |
13 |
|
14 |
catalyst/lock.py | 467 ++----------------------------------------------------- |
15 |
1 file changed, 15 insertions(+), 452 deletions(-) |
16 |
|
17 |
diff --git a/catalyst/lock.py b/catalyst/lock.py |
18 |
index 8095a82..39926dd 100644 |
19 |
--- a/catalyst/lock.py |
20 |
+++ b/catalyst/lock.py |
21 |
@@ -1,467 +1,30 @@ |
22 |
|
23 |
- |
24 |
import os |
25 |
-import fcntl |
26 |
-import errno |
27 |
-import sys |
28 |
-import time |
29 |
-from catalyst.support import CatalystError, normpath |
30 |
|
31 |
-def writemsg(mystr): |
32 |
- sys.stderr.write(mystr) |
33 |
- sys.stderr.flush() |
34 |
+from snakeoil import fileutils |
35 |
+from snakeoil import osutils |
36 |
|
37 |
|
38 |
-class LockInUse(Exception): |
39 |
- def __init__(self, message): |
40 |
- if message: |
41 |
- #(type,value)=sys.exc_info()[:2] |
42 |
- #if value!=None: |
43 |
- #print |
44 |
- #kprint traceback.print_exc(file=sys.stdout) |
45 |
- print |
46 |
- print "!!! catalyst lock file in use: "+message |
47 |
- print |
48 |
+LockInUse = osutils.LockException |
49 |
|
50 |
|
51 |
class LockDir(object): |
52 |
- locking_method=fcntl.flock |
53 |
- lock_dirs_in_use=[] |
54 |
- die_on_failed_lock=True |
55 |
- |
56 |
- def __del__(self): |
57 |
- #print "Lock.__del__() 1" |
58 |
- self.clean_my_hardlocks() |
59 |
- #print "Lock.__del__() 2" |
60 |
- self.delete_lock_from_path_list() |
61 |
- #print "Lock.__del__() 3" |
62 |
- if self.islocked(): |
63 |
- #print "Lock.__del__() 4" |
64 |
- self.fcntl_unlock() |
65 |
- #print "Lock.__del__() finnished" |
66 |
- |
67 |
- def __init__(self,lockdir): |
68 |
- self.locked=False |
69 |
- self.myfd=None |
70 |
- self.set_gid(250) |
71 |
- self.locking_method=LockDir.locking_method |
72 |
- self.set_lockdir(lockdir) |
73 |
- self.set_lockfilename(".catalyst_lock") |
74 |
- self.set_lockfile() |
75 |
- |
76 |
- if LockDir.lock_dirs_in_use.count(lockdir)>0: |
77 |
- raise "This directory already associated with a lock object" |
78 |
- else: |
79 |
- LockDir.lock_dirs_in_use.append(lockdir) |
80 |
- |
81 |
- self.myhardlock = None |
82 |
- self.hardlock_paths={} |
83 |
- |
84 |
- def delete_lock_from_path_list(self): |
85 |
- try: |
86 |
- LockDir.lock_dirs_in_use.remove(self.lockdir) |
87 |
- except ValueError: |
88 |
- pass |
89 |
- |
90 |
- def islocked(self): |
91 |
- if self.locked: |
92 |
- return True |
93 |
- else: |
94 |
- return False |
95 |
+ """An object that creates locks inside dirs""" |
96 |
|
97 |
- def set_gid(self,gid): |
98 |
- if not self.islocked(): |
99 |
-# if self.settings["DEBUG"]: |
100 |
-# print "setting gid to", gid |
101 |
- self.gid=gid |
102 |
- |
103 |
- def set_lockdir(self,lockdir): |
104 |
- if not os.path.exists(lockdir): |
105 |
- os.makedirs(lockdir) |
106 |
- if os.path.isdir(lockdir): |
107 |
- if not self.islocked(): |
108 |
- if lockdir[-1] == "/": |
109 |
- lockdir=lockdir[:-1] |
110 |
- self.lockdir=normpath(lockdir) |
111 |
-# if self.settings["DEBUG"]: |
112 |
-# print "setting lockdir to", self.lockdir |
113 |
- else: |
114 |
- raise "the lock object needs a path to a dir" |
115 |
- |
116 |
- def set_lockfilename(self,lockfilename): |
117 |
- if not self.islocked(): |
118 |
- self.lockfilename=lockfilename |
119 |
-# if self.settings["DEBUG"]: |
120 |
-# print "setting lockfilename to", self.lockfilename |
121 |
- |
122 |
- def set_lockfile(self): |
123 |
- if not self.islocked(): |
124 |
- self.lockfile=normpath(self.lockdir+'/'+self.lockfilename) |
125 |
-# if self.settings["DEBUG"]: |
126 |
-# print "setting lockfile to", self.lockfile |
127 |
+ def __init__(self, lockdir): |
128 |
+ self.gid = 250 |
129 |
+ self.lockfile = os.path.join(lockdir, '.catalyst_lock') |
130 |
+ osutils.ensure_dirs(lockdir) |
131 |
+ fileutils.touch(self.lockfile, mode=0o664) |
132 |
+ os.chown(self.lockfile, -1, self.gid) |
133 |
+ self.lock = osutils.FsLock(self.lockfile) |
134 |
|
135 |
def read_lock(self): |
136 |
- if not self.locking_method == "HARDLOCK": |
137 |
- self.fcntl_lock("read") |
138 |
- else: |
139 |
- print "HARDLOCKING doesnt support shared-read locks" |
140 |
- print "using exclusive write locks" |
141 |
- self.hard_lock() |
142 |
+ self.lock.acquire_read_lock() |
143 |
|
144 |
def write_lock(self): |
145 |
- if not self.locking_method == "HARDLOCK": |
146 |
- self.fcntl_lock("write") |
147 |
- else: |
148 |
- self.hard_lock() |
149 |
+ self.lock.acquire_write_lock() |
150 |
|
151 |
def unlock(self): |
152 |
- if not self.locking_method == "HARDLOCK": |
153 |
- self.fcntl_unlock() |
154 |
- else: |
155 |
- self.hard_unlock() |
156 |
- |
157 |
- def fcntl_lock(self,locktype): |
158 |
- if self.myfd==None: |
159 |
- if not os.path.exists(os.path.dirname(self.lockdir)): |
160 |
- raise CatalystError("DirectoryNotFound: %s" |
161 |
- % os.path.dirname(self.lockdir), print_traceback=True) |
162 |
- if not os.path.exists(self.lockfile): |
163 |
- old_mask=os.umask(000) |
164 |
- self.myfd = os.open(self.lockfile, os.O_CREAT|os.O_RDWR,0660) |
165 |
- try: |
166 |
- if os.stat(self.lockfile).st_gid != self.gid: |
167 |
- os.chown(self.lockfile,os.getuid(),self.gid) |
168 |
- except OSError, e: |
169 |
- if e[0] == 2: #XXX: No such file or directory |
170 |
- return self.fcntl_locking(locktype) |
171 |
- else: |
172 |
- writemsg("Cannot chown a lockfile. This could cause inconvenience later.\n") |
173 |
- |
174 |
- os.umask(old_mask) |
175 |
- else: |
176 |
- self.myfd = os.open(self.lockfile, os.O_CREAT|os.O_RDWR,0660) |
177 |
- |
178 |
- try: |
179 |
- if locktype == "read": |
180 |
- self.locking_method(self.myfd,fcntl.LOCK_SH|fcntl.LOCK_NB) |
181 |
- else: |
182 |
- self.locking_method(self.myfd,fcntl.LOCK_EX|fcntl.LOCK_NB) |
183 |
- except IOError, e: |
184 |
- if "errno" not in dir(e): |
185 |
- raise |
186 |
- if e.errno == errno.EAGAIN: |
187 |
- if not LockDir.die_on_failed_lock: |
188 |
- # Resource temp unavailable; eg, someone beat us to the lock. |
189 |
- writemsg("waiting for lock on %s\n" % self.lockfile) |
190 |
- |
191 |
- # Try for the exclusive or shared lock again. |
192 |
- if locktype == "read": |
193 |
- self.locking_method(self.myfd,fcntl.LOCK_SH) |
194 |
- else: |
195 |
- self.locking_method(self.myfd,fcntl.LOCK_EX) |
196 |
- else: |
197 |
- raise LockInUse,self.lockfile |
198 |
- elif e.errno == errno.ENOLCK: |
199 |
- pass |
200 |
- else: |
201 |
- raise |
202 |
- if not os.path.exists(self.lockfile): |
203 |
- os.close(self.myfd) |
204 |
- self.myfd=None |
205 |
- #writemsg("lockfile recurse\n") |
206 |
- self.fcntl_lock(locktype) |
207 |
- else: |
208 |
- self.locked=True |
209 |
- #writemsg("Lockfile obtained\n") |
210 |
- |
211 |
- def fcntl_unlock(self): |
212 |
- unlinkfile = 1 |
213 |
- if not os.path.exists(self.lockfile): |
214 |
- print "lockfile does not exist '%s'" % self.lockfile |
215 |
- #print "fcntl_unlock() , self.myfd:", self.myfd, type(self.myfd) |
216 |
- if self.myfd != None: |
217 |
- #print "fcntl_unlock() trying to close it " |
218 |
- try: |
219 |
- os.close(self.myfd) |
220 |
- self.myfd=None |
221 |
- except Exception: |
222 |
- pass |
223 |
- return False |
224 |
- |
225 |
- try: |
226 |
- if self.myfd == None: |
227 |
- self.myfd = os.open(self.lockfile, os.O_WRONLY,0660) |
228 |
- unlinkfile = 1 |
229 |
- self.locking_method(self.myfd,fcntl.LOCK_UN) |
230 |
- except Exception, e: |
231 |
- #if self.myfd is not None: |
232 |
- #print "fcntl_unlock() trying to close", self.myfd |
233 |
- #os.close(self.myfd) |
234 |
- #self.myfd=None |
235 |
- #raise IOError, "Failed to unlock file '%s'\n%s" % (self.lockfile, str(e)) |
236 |
- try: |
237 |
- # This sleep call was added to allow other processes that are |
238 |
- # waiting for a lock to be able to grab it before it is deleted. |
239 |
- # lockfile() already accounts for this situation, however, and |
240 |
- # the sleep here adds more time than is saved overall, so am |
241 |
- # commenting until it is proved necessary. |
242 |
- #time.sleep(0.0001) |
243 |
- if unlinkfile: |
244 |
- InUse=False |
245 |
- try: |
246 |
- self.locking_method(self.myfd,fcntl.LOCK_EX|fcntl.LOCK_NB) |
247 |
- except Exception: |
248 |
- print "Read lock may be in effect. skipping lockfile delete..." |
249 |
- InUse=True |
250 |
- # We won the lock, so there isn't competition for it. |
251 |
- # We can safely delete the file. |
252 |
- #writemsg("Got the lockfile...\n") |
253 |
- #writemsg("Unlinking...\n") |
254 |
- self.locking_method(self.myfd,fcntl.LOCK_UN) |
255 |
- if not InUse: |
256 |
- os.unlink(self.lockfile) |
257 |
- os.close(self.myfd) |
258 |
- self.myfd=None |
259 |
-# if self.settings["DEBUG"]: |
260 |
-# print "Unlinked lockfile..." |
261 |
- except Exception, e: |
262 |
- # We really don't care... Someone else has the lock. |
263 |
- # So it is their problem now. |
264 |
- print "Failed to get lock... someone took it." |
265 |
- print str(e) |
266 |
- |
267 |
- # Why test lockfilename? Because we may have been handed an |
268 |
- # fd originally, and the caller might not like having their |
269 |
- # open fd closed automatically on them. |
270 |
- #if type(lockfilename) == types.StringType: |
271 |
- # os.close(myfd) |
272 |
- #print "fcntl_unlock() trying a last ditch close", self.myfd |
273 |
- if self.myfd != None: |
274 |
- os.close(self.myfd) |
275 |
- self.myfd=None |
276 |
- self.locked=False |
277 |
- time.sleep(.0001) |
278 |
- |
279 |
- def hard_lock(self,max_wait=14400): |
280 |
- """Does the NFS, hardlink shuffle to ensure locking on the disk. |
281 |
- We create a PRIVATE lockfile, that is just a placeholder on the disk. |
282 |
- Then we HARDLINK the real lockfile to that private file. |
283 |
- If our file can 2 references, then we have the lock. :) |
284 |
- Otherwise we lather, rise, and repeat. |
285 |
- We default to a 4 hour timeout. |
286 |
- """ |
287 |
- |
288 |
- self.myhardlock = self.hardlock_name(self.lockdir) |
289 |
- |
290 |
- start_time = time.time() |
291 |
- reported_waiting = False |
292 |
- |
293 |
- while time.time() < (start_time + max_wait): |
294 |
- # We only need it to exist. |
295 |
- self.myfd = os.open(self.myhardlock, os.O_CREAT|os.O_RDWR,0660) |
296 |
- os.close(self.myfd) |
297 |
- |
298 |
- self.add_hardlock_file_to_cleanup() |
299 |
- if not os.path.exists(self.myhardlock): |
300 |
- raise CatalystError("FileNotFound: Created lockfile is missing: " |
301 |
- "%(filename)s" % {"filename":self.myhardlock}, |
302 |
- print_traceback=True) |
303 |
- try: |
304 |
- os.link(self.myhardlock, self.lockfile) |
305 |
- except Exception: |
306 |
-# if self.settings["DEBUG"]: |
307 |
-# print "lockfile(): Hardlink: Link failed." |
308 |
-# print "Exception: ",e |
309 |
- pass |
310 |
- |
311 |
- if self.hardlink_is_mine(self.myhardlock, self.lockfile): |
312 |
- # We have the lock. |
313 |
- if reported_waiting: |
314 |
- print |
315 |
- return True |
316 |
- |
317 |
- if reported_waiting: |
318 |
- writemsg(".") |
319 |
- else: |
320 |
- reported_waiting = True |
321 |
- print |
322 |
- print "Waiting on (hardlink) lockfile: (one '.' per 3 seconds)" |
323 |
- print "Lockfile: " + self.lockfile |
324 |
- time.sleep(3) |
325 |
- |
326 |
- os.unlink(self.myhardlock) |
327 |
- return False |
328 |
- |
329 |
- def hard_unlock(self): |
330 |
- try: |
331 |
- if os.path.exists(self.myhardlock): |
332 |
- os.unlink(self.myhardlock) |
333 |
- if os.path.exists(self.lockfile): |
334 |
- os.unlink(self.lockfile) |
335 |
- except Exception: |
336 |
- writemsg("Something strange happened to our hardlink locks.\n") |
337 |
- |
338 |
- def add_hardlock_file_to_cleanup(self): |
339 |
- #mypath = self.normpath(path) |
340 |
- if os.path.isdir(self.lockdir) and os.path.isfile(self.myhardlock): |
341 |
- self.hardlock_paths[self.lockdir]=self.myhardlock |
342 |
- |
343 |
- def remove_hardlock_file_from_cleanup(self): |
344 |
- if self.lockdir in self.hardlock_paths: |
345 |
- del self.hardlock_paths[self.lockdir] |
346 |
- print self.hardlock_paths |
347 |
- |
348 |
- @staticmethod |
349 |
- def hardlock_name(path): |
350 |
- mypath=path+"/.hardlock-"+os.uname()[1]+"-"+str(os.getpid()) |
351 |
- newpath = os.path.normpath(mypath) |
352 |
- if len(newpath) > 1: |
353 |
- if newpath[1] == "/": |
354 |
- newpath = "/"+newpath.lstrip("/") |
355 |
- return newpath |
356 |
- |
357 |
- @staticmethod |
358 |
- def hardlink_is_mine(link, lock): |
359 |
- import stat |
360 |
- try: |
361 |
- myhls = os.stat(link) |
362 |
- mylfs = os.stat(lock) |
363 |
- except Exception: |
364 |
- myhls = None |
365 |
- mylfs = None |
366 |
- |
367 |
- if myhls: |
368 |
- if myhls[stat.ST_NLINK] == 2: |
369 |
- return True |
370 |
- if mylfs: |
371 |
- if mylfs[stat.ST_INO] == myhls[stat.ST_INO]: |
372 |
- return True |
373 |
- return False |
374 |
- |
375 |
- @staticmethod |
376 |
- def hardlink_active(lock): |
377 |
- if not os.path.exists(lock): |
378 |
- return False |
379 |
- |
380 |
- def clean_my_hardlocks(self): |
381 |
- try: |
382 |
- for x in self.hardlock_paths.keys(): |
383 |
- self.hardlock_cleanup(x) |
384 |
- except AttributeError: |
385 |
- pass |
386 |
- |
387 |
- def hardlock_cleanup(self,path): |
388 |
- #mypid = str(os.getpid()) |
389 |
- myhost = os.uname()[1] |
390 |
- mydl = os.listdir(path) |
391 |
- results = [] |
392 |
- mycount = 0 |
393 |
- |
394 |
- mylist = {} |
395 |
- for x in mydl: |
396 |
- filepath=path+"/"+x |
397 |
- if os.path.isfile(filepath): |
398 |
- parts = filepath.split(".hardlock-") |
399 |
- if len(parts) == 2: |
400 |
- filename = parts[0] |
401 |
- hostpid = parts[1].split("-") |
402 |
- host = "-".join(hostpid[:-1]) |
403 |
- pid = hostpid[-1] |
404 |
- if filename not in mylist: |
405 |
- mylist[filename] = {} |
406 |
- |
407 |
- if host not in mylist[filename]: |
408 |
- mylist[filename][host] = [] |
409 |
- mylist[filename][host].append(pid) |
410 |
- mycount += 1 |
411 |
- else: |
412 |
- mylist[filename][host].append(pid) |
413 |
- mycount += 1 |
414 |
- |
415 |
- |
416 |
- results.append("Found %(count)s locks" % {"count":mycount}) |
417 |
- for x in mylist.keys(): |
418 |
- if myhost in mylist[x]: |
419 |
- mylockname = self.hardlock_name(x) |
420 |
- if self.hardlink_is_mine(mylockname, self.lockfile) or \ |
421 |
- not os.path.exists(self.lockfile): |
422 |
- for y in mylist[x].keys(): |
423 |
- for z in mylist[x][y]: |
424 |
- filename = x+".hardlock-"+y+"-"+z |
425 |
- if filename == mylockname: |
426 |
- self.hard_unlock() |
427 |
- continue |
428 |
- try: |
429 |
- # We're sweeping through, unlinking everyone's locks. |
430 |
- os.unlink(filename) |
431 |
- results.append("Unlinked: " + filename) |
432 |
- except Exception: |
433 |
- pass |
434 |
- try: |
435 |
- os.unlink(x) |
436 |
- results.append("Unlinked: " + x) |
437 |
- os.unlink(mylockname) |
438 |
- results.append("Unlinked: " + mylockname) |
439 |
- except Exception: |
440 |
- pass |
441 |
- else: |
442 |
- try: |
443 |
- os.unlink(mylockname) |
444 |
- results.append("Unlinked: " + mylockname) |
445 |
- except Exception: |
446 |
- pass |
447 |
- return results |
448 |
- |
449 |
- |
450 |
-if __name__ == "__main__": |
451 |
- |
452 |
- def lock_work(): |
453 |
- print |
454 |
- for i in range(1,6): |
455 |
- print i,time.time() |
456 |
- time.sleep(1) |
457 |
- print |
458 |
- |
459 |
- print "Lock 5 starting" |
460 |
- Lock1=LockDir("/tmp/lock_path") |
461 |
- Lock1.write_lock() |
462 |
- print "Lock1 write lock" |
463 |
- |
464 |
- lock_work() |
465 |
- |
466 |
- Lock1.unlock() |
467 |
- print "Lock1 unlock" |
468 |
- |
469 |
- Lock1.read_lock() |
470 |
- print "Lock1 read lock" |
471 |
- |
472 |
- lock_work() |
473 |
- |
474 |
- Lock1.unlock() |
475 |
- print "Lock1 unlock" |
476 |
- |
477 |
- Lock1.read_lock() |
478 |
- print "Lock1 read lock" |
479 |
- |
480 |
- Lock1.write_lock() |
481 |
- print "Lock1 write lock" |
482 |
- |
483 |
- lock_work() |
484 |
- |
485 |
- Lock1.unlock() |
486 |
- print "Lock1 unlock" |
487 |
- |
488 |
- Lock1.read_lock() |
489 |
- print "Lock1 read lock" |
490 |
- |
491 |
- lock_work() |
492 |
- |
493 |
- Lock1.unlock() |
494 |
- print "Lock1 unlock" |
495 |
- |
496 |
-#Lock1.write_lock() |
497 |
-#time.sleep(2) |
498 |
-#Lock1.unlock() |
499 |
- ##Lock1.write_lock() |
500 |
- #time.sleep(2) |
501 |
- #Lock1.unlock() |
502 |
+ # Releasing a write lock is the same as a read lock. |
503 |
+ self.lock.release_write_lock() |