aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2015-10-09 21:51:23 -0400
committerMike Frysinger <vapier@gentoo.org>2015-10-10 19:54:36 -0400
commitf083637554bf5668ec856c56cfaaa76bb343d941 (patch)
tree0b558dae54513529823ecf0bea91e5c96b347f4a /catalyst
parentstagebase: simplify lock calls a bit (diff)
downloadcatalyst-f083637554bf5668ec856c56cfaaa76bb343d941.tar.gz
catalyst-f083637554bf5668ec856c56cfaaa76bb343d941.tar.bz2
catalyst-f083637554bf5668ec856c56cfaaa76bb343d941.zip
lock: gut & replace with snakeoil
The hardlink logic is unused, so start by deleting all of that. If someone wants that (multiple builds on NFS?), we can look at restoring it.
Diffstat (limited to 'catalyst')
-rw-r--r--catalyst/lock.py467
1 files changed, 15 insertions, 452 deletions
diff --git a/catalyst/lock.py b/catalyst/lock.py
index 8095a82b..39926ddc 100644
--- a/catalyst/lock.py
+++ b/catalyst/lock.py
@@ -1,467 +1,30 @@
-
import os
-import fcntl
-import errno
-import sys
-import time
-from catalyst.support import CatalystError, normpath
-def writemsg(mystr):
- sys.stderr.write(mystr)
- sys.stderr.flush()
+from snakeoil import fileutils
+from snakeoil import osutils
-class LockInUse(Exception):
- def __init__(self, message):
- if message:
- #(type,value)=sys.exc_info()[:2]
- #if value!=None:
- #print
- #kprint traceback.print_exc(file=sys.stdout)
- print
- print "!!! catalyst lock file in use: "+message
- print
+LockInUse = osutils.LockException
class LockDir(object):
- locking_method=fcntl.flock
- lock_dirs_in_use=[]
- die_on_failed_lock=True
-
- def __del__(self):
- #print "Lock.__del__() 1"
- self.clean_my_hardlocks()
- #print "Lock.__del__() 2"
- self.delete_lock_from_path_list()
- #print "Lock.__del__() 3"
- if self.islocked():
- #print "Lock.__del__() 4"
- self.fcntl_unlock()
- #print "Lock.__del__() finnished"
-
- def __init__(self,lockdir):
- self.locked=False
- self.myfd=None
- self.set_gid(250)
- self.locking_method=LockDir.locking_method
- self.set_lockdir(lockdir)
- self.set_lockfilename(".catalyst_lock")
- self.set_lockfile()
-
- if LockDir.lock_dirs_in_use.count(lockdir)>0:
- raise "This directory already associated with a lock object"
- else:
- LockDir.lock_dirs_in_use.append(lockdir)
-
- self.myhardlock = None
- self.hardlock_paths={}
-
- def delete_lock_from_path_list(self):
- try:
- LockDir.lock_dirs_in_use.remove(self.lockdir)
- except ValueError:
- pass
-
- def islocked(self):
- if self.locked:
- return True
- else:
- return False
+ """An object that creates locks inside dirs"""
- def set_gid(self,gid):
- if not self.islocked():
-# if self.settings["DEBUG"]:
-# print "setting gid to", gid
- self.gid=gid
-
- def set_lockdir(self,lockdir):
- if not os.path.exists(lockdir):
- os.makedirs(lockdir)
- if os.path.isdir(lockdir):
- if not self.islocked():
- if lockdir[-1] == "/":
- lockdir=lockdir[:-1]
- self.lockdir=normpath(lockdir)
-# if self.settings["DEBUG"]:
-# print "setting lockdir to", self.lockdir
- else:
- raise "the lock object needs a path to a dir"
-
- def set_lockfilename(self,lockfilename):
- if not self.islocked():
- self.lockfilename=lockfilename
-# if self.settings["DEBUG"]:
-# print "setting lockfilename to", self.lockfilename
-
- def set_lockfile(self):
- if not self.islocked():
- self.lockfile=normpath(self.lockdir+'/'+self.lockfilename)
-# if self.settings["DEBUG"]:
-# print "setting lockfile to", self.lockfile
+ def __init__(self, lockdir):
+ self.gid = 250
+ self.lockfile = os.path.join(lockdir, '.catalyst_lock')
+ osutils.ensure_dirs(lockdir)
+ fileutils.touch(self.lockfile, mode=0o664)
+ os.chown(self.lockfile, -1, self.gid)
+ self.lock = osutils.FsLock(self.lockfile)
def read_lock(self):
- if not self.locking_method == "HARDLOCK":
- self.fcntl_lock("read")
- else:
- print "HARDLOCKING doesnt support shared-read locks"
- print "using exclusive write locks"
- self.hard_lock()
+ self.lock.acquire_read_lock()
def write_lock(self):
- if not self.locking_method == "HARDLOCK":
- self.fcntl_lock("write")
- else:
- self.hard_lock()
+ self.lock.acquire_write_lock()
def unlock(self):
- if not self.locking_method == "HARDLOCK":
- self.fcntl_unlock()
- else:
- self.hard_unlock()
-
- def fcntl_lock(self,locktype):
- if self.myfd==None:
- if not os.path.exists(os.path.dirname(self.lockdir)):
- raise CatalystError("DirectoryNotFound: %s"
- % os.path.dirname(self.lockdir), print_traceback=True)
- if not os.path.exists(self.lockfile):
- old_mask=os.umask(000)
- self.myfd = os.open(self.lockfile, os.O_CREAT|os.O_RDWR,0660)
- try:
- if os.stat(self.lockfile).st_gid != self.gid:
- os.chown(self.lockfile,os.getuid(),self.gid)
- except OSError, e:
- if e[0] == 2: #XXX: No such file or directory
- return self.fcntl_locking(locktype)
- else:
- writemsg("Cannot chown a lockfile. This could cause inconvenience later.\n")
-
- os.umask(old_mask)
- else:
- self.myfd = os.open(self.lockfile, os.O_CREAT|os.O_RDWR,0660)
-
- try:
- if locktype == "read":
- self.locking_method(self.myfd,fcntl.LOCK_SH|fcntl.LOCK_NB)
- else:
- self.locking_method(self.myfd,fcntl.LOCK_EX|fcntl.LOCK_NB)
- except IOError, e:
- if "errno" not in dir(e):
- raise
- if e.errno == errno.EAGAIN:
- if not LockDir.die_on_failed_lock:
- # Resource temp unavailable; eg, someone beat us to the lock.
- writemsg("waiting for lock on %s\n" % self.lockfile)
-
- # Try for the exclusive or shared lock again.
- if locktype == "read":
- self.locking_method(self.myfd,fcntl.LOCK_SH)
- else:
- self.locking_method(self.myfd,fcntl.LOCK_EX)
- else:
- raise LockInUse,self.lockfile
- elif e.errno == errno.ENOLCK:
- pass
- else:
- raise
- if not os.path.exists(self.lockfile):
- os.close(self.myfd)
- self.myfd=None
- #writemsg("lockfile recurse\n")
- self.fcntl_lock(locktype)
- else:
- self.locked=True
- #writemsg("Lockfile obtained\n")
-
- def fcntl_unlock(self):
- unlinkfile = 1
- if not os.path.exists(self.lockfile):
- print "lockfile does not exist '%s'" % self.lockfile
- #print "fcntl_unlock() , self.myfd:", self.myfd, type(self.myfd)
- if self.myfd != None:
- #print "fcntl_unlock() trying to close it "
- try:
- os.close(self.myfd)
- self.myfd=None
- except Exception:
- pass
- return False
-
- try:
- if self.myfd == None:
- self.myfd = os.open(self.lockfile, os.O_WRONLY,0660)
- unlinkfile = 1
- self.locking_method(self.myfd,fcntl.LOCK_UN)
- except Exception, e:
- #if self.myfd is not None:
- #print "fcntl_unlock() trying to close", self.myfd
- #os.close(self.myfd)
- #self.myfd=None
- #raise IOError, "Failed to unlock file '%s'\n%s" % (self.lockfile, str(e))
- try:
- # This sleep call was added to allow other processes that are
- # waiting for a lock to be able to grab it before it is deleted.
- # lockfile() already accounts for this situation, however, and
- # the sleep here adds more time than is saved overall, so am
- # commenting until it is proved necessary.
- #time.sleep(0.0001)
- if unlinkfile:
- InUse=False
- try:
- self.locking_method(self.myfd,fcntl.LOCK_EX|fcntl.LOCK_NB)
- except Exception:
- print "Read lock may be in effect. skipping lockfile delete..."
- InUse=True
- # We won the lock, so there isn't competition for it.
- # We can safely delete the file.
- #writemsg("Got the lockfile...\n")
- #writemsg("Unlinking...\n")
- self.locking_method(self.myfd,fcntl.LOCK_UN)
- if not InUse:
- os.unlink(self.lockfile)
- os.close(self.myfd)
- self.myfd=None
-# if self.settings["DEBUG"]:
-# print "Unlinked lockfile..."
- except Exception, e:
- # We really don't care... Someone else has the lock.
- # So it is their problem now.
- print "Failed to get lock... someone took it."
- print str(e)
-
- # Why test lockfilename? Because we may have been handed an
- # fd originally, and the caller might not like having their
- # open fd closed automatically on them.
- #if type(lockfilename) == types.StringType:
- # os.close(myfd)
- #print "fcntl_unlock() trying a last ditch close", self.myfd
- if self.myfd != None:
- os.close(self.myfd)
- self.myfd=None
- self.locked=False
- time.sleep(.0001)
-
- def hard_lock(self,max_wait=14400):
- """Does the NFS, hardlink shuffle to ensure locking on the disk.
- We create a PRIVATE lockfile, that is just a placeholder on the disk.
- Then we HARDLINK the real lockfile to that private file.
- If our file can 2 references, then we have the lock. :)
- Otherwise we lather, rise, and repeat.
- We default to a 4 hour timeout.
- """
-
- self.myhardlock = self.hardlock_name(self.lockdir)
-
- start_time = time.time()
- reported_waiting = False
-
- while time.time() < (start_time + max_wait):
- # We only need it to exist.
- self.myfd = os.open(self.myhardlock, os.O_CREAT|os.O_RDWR,0660)
- os.close(self.myfd)
-
- self.add_hardlock_file_to_cleanup()
- if not os.path.exists(self.myhardlock):
- raise CatalystError("FileNotFound: Created lockfile is missing: "
- "%(filename)s" % {"filename":self.myhardlock},
- print_traceback=True)
- try:
- os.link(self.myhardlock, self.lockfile)
- except Exception:
-# if self.settings["DEBUG"]:
-# print "lockfile(): Hardlink: Link failed."
-# print "Exception: ",e
- pass
-
- if self.hardlink_is_mine(self.myhardlock, self.lockfile):
- # We have the lock.
- if reported_waiting:
- print
- return True
-
- if reported_waiting:
- writemsg(".")
- else:
- reported_waiting = True
- print
- print "Waiting on (hardlink) lockfile: (one '.' per 3 seconds)"
- print "Lockfile: " + self.lockfile
- time.sleep(3)
-
- os.unlink(self.myhardlock)
- return False
-
- def hard_unlock(self):
- try:
- if os.path.exists(self.myhardlock):
- os.unlink(self.myhardlock)
- if os.path.exists(self.lockfile):
- os.unlink(self.lockfile)
- except Exception:
- writemsg("Something strange happened to our hardlink locks.\n")
-
- def add_hardlock_file_to_cleanup(self):
- #mypath = self.normpath(path)
- if os.path.isdir(self.lockdir) and os.path.isfile(self.myhardlock):
- self.hardlock_paths[self.lockdir]=self.myhardlock
-
- def remove_hardlock_file_from_cleanup(self):
- if self.lockdir in self.hardlock_paths:
- del self.hardlock_paths[self.lockdir]
- print self.hardlock_paths
-
- @staticmethod
- def hardlock_name(path):
- mypath=path+"/.hardlock-"+os.uname()[1]+"-"+str(os.getpid())
- newpath = os.path.normpath(mypath)
- if len(newpath) > 1:
- if newpath[1] == "/":
- newpath = "/"+newpath.lstrip("/")
- return newpath
-
- @staticmethod
- def hardlink_is_mine(link, lock):
- import stat
- try:
- myhls = os.stat(link)
- mylfs = os.stat(lock)
- except Exception:
- myhls = None
- mylfs = None
-
- if myhls:
- if myhls[stat.ST_NLINK] == 2:
- return True
- if mylfs:
- if mylfs[stat.ST_INO] == myhls[stat.ST_INO]:
- return True
- return False
-
- @staticmethod
- def hardlink_active(lock):
- if not os.path.exists(lock):
- return False
-
- def clean_my_hardlocks(self):
- try:
- for x in self.hardlock_paths.keys():
- self.hardlock_cleanup(x)
- except AttributeError:
- pass
-
- def hardlock_cleanup(self,path):
- #mypid = str(os.getpid())
- myhost = os.uname()[1]
- mydl = os.listdir(path)
- results = []
- mycount = 0
-
- mylist = {}
- for x in mydl:
- filepath=path+"/"+x
- if os.path.isfile(filepath):
- parts = filepath.split(".hardlock-")
- if len(parts) == 2:
- filename = parts[0]
- hostpid = parts[1].split("-")
- host = "-".join(hostpid[:-1])
- pid = hostpid[-1]
- if filename not in mylist:
- mylist[filename] = {}
-
- if host not in mylist[filename]:
- mylist[filename][host] = []
- mylist[filename][host].append(pid)
- mycount += 1
- else:
- mylist[filename][host].append(pid)
- mycount += 1
-
-
- results.append("Found %(count)s locks" % {"count":mycount})
- for x in mylist.keys():
- if myhost in mylist[x]:
- mylockname = self.hardlock_name(x)
- if self.hardlink_is_mine(mylockname, self.lockfile) or \
- not os.path.exists(self.lockfile):
- for y in mylist[x].keys():
- for z in mylist[x][y]:
- filename = x+".hardlock-"+y+"-"+z
- if filename == mylockname:
- self.hard_unlock()
- continue
- try:
- # We're sweeping through, unlinking everyone's locks.
- os.unlink(filename)
- results.append("Unlinked: " + filename)
- except Exception:
- pass
- try:
- os.unlink(x)
- results.append("Unlinked: " + x)
- os.unlink(mylockname)
- results.append("Unlinked: " + mylockname)
- except Exception:
- pass
- else:
- try:
- os.unlink(mylockname)
- results.append("Unlinked: " + mylockname)
- except Exception:
- pass
- return results
-
-
-if __name__ == "__main__":
-
- def lock_work():
- print
- for i in range(1,6):
- print i,time.time()
- time.sleep(1)
- print
-
- print "Lock 5 starting"
- Lock1=LockDir("/tmp/lock_path")
- Lock1.write_lock()
- print "Lock1 write lock"
-
- lock_work()
-
- Lock1.unlock()
- print "Lock1 unlock"
-
- Lock1.read_lock()
- print "Lock1 read lock"
-
- lock_work()
-
- Lock1.unlock()
- print "Lock1 unlock"
-
- Lock1.read_lock()
- print "Lock1 read lock"
-
- Lock1.write_lock()
- print "Lock1 write lock"
-
- lock_work()
-
- Lock1.unlock()
- print "Lock1 unlock"
-
- Lock1.read_lock()
- print "Lock1 read lock"
-
- lock_work()
-
- Lock1.unlock()
- print "Lock1 unlock"
-
-#Lock1.write_lock()
-#time.sleep(2)
-#Lock1.unlock()
- ##Lock1.write_lock()
- #time.sleep(2)
- #Lock1.unlock()
+ # Releasing a write lock is the same as a read lock.
+ self.lock.release_write_lock()