mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	 b940e113bf
			
		
	
	
		b940e113bf
		
	
	
	
	
		
			
			(a) "except E, V" -> "except E as V" (b) V is now limited to a simple name (local variable) (c) V is now deleted at the end of the except block
		
			
				
	
	
		
			280 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			280 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| """CVS locking algorithm.
 | |
| 
 | |
| CVS locking strategy
 | |
| ====================
 | |
| 
 | |
| As reverse engineered from the CVS 1.3 sources (file lock.c):
 | |
| 
 | |
| - Locking is done on a per repository basis (but a process can hold
 | |
| write locks for multiple directories); all lock files are placed in
 | |
| the repository and have names beginning with "#cvs.".
 | |
| 
 | |
| - Before even attempting to lock, a file "#cvs.tfl.<pid>" is created
 | |
| (and removed again), to test that we can write the repository.  [The
 | |
| algorithm can still be fooled (1) if the repository's mode is changed
 | |
| while attempting to lock; (2) if this file exists and is writable but
 | |
| the directory is not.]
 | |
| 
 | |
| - While creating the actual read/write lock files (which may exist for
 | |
| a long time), a "meta-lock" is held.  The meta-lock is a directory
 | |
| named "#cvs.lock" in the repository.  The meta-lock is also held while
 | |
| a write lock is held.
 | |
| 
 | |
| - To set a read lock:
 | |
| 
 | |
|         - acquire the meta-lock
 | |
|         - create the file "#cvs.rfl.<pid>"
 | |
|         - release the meta-lock
 | |
| 
 | |
| - To set a write lock:
 | |
| 
 | |
|         - acquire the meta-lock
 | |
|         - check that there are no files called "#cvs.rfl.*"
 | |
|                 - if there are, release the meta-lock, sleep, try again
 | |
|         - create the file "#cvs.wfl.<pid>"
 | |
| 
 | |
| - To release a write lock:
 | |
| 
 | |
|         - remove the file "#cvs.wfl.<pid>"
 | |
|         - rmdir the meta-lock
 | |
| 
 | |
| - To release a read lock:
 | |
| 
 | |
|         - remove the file "#cvs.rfl.<pid>"
 | |
| 
 | |
| 
 | |
| Additional notes
 | |
| ----------------
 | |
| 
 | |
| - A process should read-lock at most one repository at a time.
 | |
| 
 | |
| - A process may write-lock as many repositories as it wishes (to avoid
 | |
| deadlocks, I presume it should always lock them top-down in the
 | |
| directory hierarchy).
 | |
| 
 | |
| - A process should make sure it removes all its lock files and
 | |
| directories when it crashes.
 | |
| 
 | |
| - Limitation: one user id should not be committing files into the same
 | |
| repository at the same time.
 | |
| 
 | |
| 
 | |
| Turn this into Python code
 | |
| --------------------------
 | |
| 
 | |
| rl = ReadLock(repository, waittime)
 | |
| 
 | |
| wl = WriteLock(repository, waittime)
 | |
| 
 | |
| list = MultipleWriteLock([repository1, repository2, ...], waittime)
 | |
| 
 | |
| """
 | |
| 
 | |
| 
 | |
| import os
 | |
| import time
 | |
| import stat
 | |
| import pwd
 | |
| 
 | |
| 
 | |
| # Default wait time
 | |
| DELAY = 10
 | |
| 
 | |
| 
 | |
| # XXX This should be the same on all Unix versions
 | |
| EEXIST = 17
 | |
| 
 | |
| 
 | |
| # Files used for locking (must match cvs.h in the CVS sources)
 | |
| CVSLCK = "#cvs.lck"
 | |
| CVSRFL = "#cvs.rfl."
 | |
| CVSWFL = "#cvs.wfl."
 | |
| 
 | |
| 
 | |
| class Error:
 | |
| 
 | |
|     def __init__(self, msg):
 | |
|         self.msg = msg
 | |
| 
 | |
|     def __repr__(self):
 | |
|         return repr(self.msg)
 | |
| 
 | |
|     def __str__(self):
 | |
|         return str(self.msg)
 | |
| 
 | |
| 
 | |
| class Locked(Error):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| class Lock:
 | |
| 
 | |
|     def __init__(self, repository = ".", delay = DELAY):
 | |
|         self.repository = repository
 | |
|         self.delay = delay
 | |
|         self.lockdir = None
 | |
|         self.lockfile = None
 | |
|         pid = repr(os.getpid())
 | |
|         self.cvslck = self.join(CVSLCK)
 | |
|         self.cvsrfl = self.join(CVSRFL + pid)
 | |
|         self.cvswfl = self.join(CVSWFL + pid)
 | |
| 
 | |
|     def __del__(self):
 | |
|         print "__del__"
 | |
|         self.unlock()
 | |
| 
 | |
|     def setlockdir(self):
 | |
|         while 1:
 | |
|             try:
 | |
|                 self.lockdir = self.cvslck
 | |
|                 os.mkdir(self.cvslck, 0777)
 | |
|                 return
 | |
|             except os.error as msg:
 | |
|                 self.lockdir = None
 | |
|                 if msg[0] == EEXIST:
 | |
|                     try:
 | |
|                         st = os.stat(self.cvslck)
 | |
|                     except os.error:
 | |
|                         continue
 | |
|                     self.sleep(st)
 | |
|                     continue
 | |
|                 raise Error("failed to lock %s: %s" % (
 | |
|                         self.repository, msg))
 | |
| 
 | |
|     def unlock(self):
 | |
|         self.unlockfile()
 | |
|         self.unlockdir()
 | |
| 
 | |
|     def unlockfile(self):
 | |
|         if self.lockfile:
 | |
|             print "unlink", self.lockfile
 | |
|             try:
 | |
|                 os.unlink(self.lockfile)
 | |
|             except os.error:
 | |
|                 pass
 | |
|             self.lockfile = None
 | |
| 
 | |
|     def unlockdir(self):
 | |
|         if self.lockdir:
 | |
|             print "rmdir", self.lockdir
 | |
|             try:
 | |
|                 os.rmdir(self.lockdir)
 | |
|             except os.error:
 | |
|                 pass
 | |
|             self.lockdir = None
 | |
| 
 | |
|     def sleep(self, st):
 | |
|         sleep(st, self.repository, self.delay)
 | |
| 
 | |
|     def join(self, name):
 | |
|         return os.path.join(self.repository, name)
 | |
| 
 | |
| 
 | |
| def sleep(st, repository, delay):
 | |
|     if delay <= 0:
 | |
|         raise Locked(st)
 | |
|     uid = st[stat.ST_UID]
 | |
|     try:
 | |
|         pwent = pwd.getpwuid(uid)
 | |
|         user = pwent[0]
 | |
|     except KeyError:
 | |
|         user = "uid %d" % uid
 | |
|     print "[%s]" % time.ctime(time.time())[11:19],
 | |
|     print "Waiting for %s's lock in" % user, repository
 | |
|     time.sleep(delay)
 | |
| 
 | |
| 
 | |
| class ReadLock(Lock):
 | |
| 
 | |
|     def __init__(self, repository, delay = DELAY):
 | |
|         Lock.__init__(self, repository, delay)
 | |
|         ok = 0
 | |
|         try:
 | |
|             self.setlockdir()
 | |
|             self.lockfile = self.cvsrfl
 | |
|             fp = open(self.lockfile, 'w')
 | |
|             fp.close()
 | |
|             ok = 1
 | |
|         finally:
 | |
|             if not ok:
 | |
|                 self.unlockfile()
 | |
|             self.unlockdir()
 | |
| 
 | |
| 
 | |
| class WriteLock(Lock):
 | |
| 
 | |
|     def __init__(self, repository, delay = DELAY):
 | |
|         Lock.__init__(self, repository, delay)
 | |
|         self.setlockdir()
 | |
|         while 1:
 | |
|             uid = self.readers_exist()
 | |
|             if not uid:
 | |
|                 break
 | |
|             self.unlockdir()
 | |
|             self.sleep(uid)
 | |
|         self.lockfile = self.cvswfl
 | |
|         fp = open(self.lockfile, 'w')
 | |
|         fp.close()
 | |
| 
 | |
|     def readers_exist(self):
 | |
|         n = len(CVSRFL)
 | |
|         for name in os.listdir(self.repository):
 | |
|             if name[:n] == CVSRFL:
 | |
|                 try:
 | |
|                     st = os.stat(self.join(name))
 | |
|                 except os.error:
 | |
|                     continue
 | |
|                 return st
 | |
|         return None
 | |
| 
 | |
| 
 | |
| def MultipleWriteLock(repositories, delay = DELAY):
 | |
|     while 1:
 | |
|         locks = []
 | |
|         for r in repositories:
 | |
|             try:
 | |
|                 locks.append(WriteLock(r, 0))
 | |
|             except Locked as instance:
 | |
|                 del locks
 | |
|                 break
 | |
|         else:
 | |
|             break
 | |
|         sleep(instance.msg, r, delay)
 | |
|     return list
 | |
| 
 | |
| 
 | |
| def test():
 | |
|     import sys
 | |
|     if sys.argv[1:]:
 | |
|         repository = sys.argv[1]
 | |
|     else:
 | |
|         repository = "."
 | |
|     rl = None
 | |
|     wl = None
 | |
|     try:
 | |
|         print "attempting write lock ..."
 | |
|         wl = WriteLock(repository)
 | |
|         print "got it."
 | |
|         wl.unlock()
 | |
|         print "attempting read lock ..."
 | |
|         rl = ReadLock(repository)
 | |
|         print "got it."
 | |
|         rl.unlock()
 | |
|     finally:
 | |
|         print [1]
 | |
|         sys.exc_traceback = None
 | |
|         print [2]
 | |
|         if rl:
 | |
|             rl.unlock()
 | |
|         print [3]
 | |
|         if wl:
 | |
|             wl.unlock()
 | |
|         print [4]
 | |
|         rl = None
 | |
|         print [5]
 | |
|         wl = None
 | |
|         print [6]
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     test()
 |