Gentoo Archives: gentoo-commits

From: Mike Frysinger <vapier@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/catalyst:master commit in: catalyst/
Date: Sun, 11 Oct 2015 17:26:44
Message-Id: 1444521276.f083637554bf5668ec856c56cfaaa76bb343d941.vapier@gentoo
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()