| 
									
										
										
										
											1995-06-23 22:03:28 +00:00
										 |  |  | """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: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |         - acquire the meta-lock | 
					
						
							|  |  |  |         - create the file "#cvs.rfl.<pid>" | 
					
						
							|  |  |  |         - release the meta-lock | 
					
						
							| 
									
										
										
										
											1995-06-23 22:03:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | - To set a write lock: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |         - 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>" | 
					
						
							| 
									
										
										
										
											1995-06-23 22:03:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | - To release a write lock: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |         - remove the file "#cvs.wfl.<pid>" | 
					
						
							|  |  |  |         - rmdir the meta-lock | 
					
						
							| 
									
										
										
										
											1995-06-23 22:03:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | - To release a read lock: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |         - remove the file "#cvs.rfl.<pid>" | 
					
						
							| 
									
										
										
										
											1995-06-23 22:03:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |     def __init__(self, msg): | 
					
						
							|  |  |  |         self.msg = msg | 
					
						
							| 
									
										
										
										
											1995-06-23 22:03:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         return repr(self.msg) | 
					
						
							| 
									
										
										
										
											1995-06-23 22:03:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |     def __str__(self): | 
					
						
							|  |  |  |         return str(self.msg) | 
					
						
							| 
									
										
										
										
											1995-06-23 22:03:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Locked(Error): | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |     pass | 
					
						
							| 
									
										
										
										
											1995-06-23 22:03:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Lock: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |     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): | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |         print("__del__") | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |         self.unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setlockdir(self): | 
					
						
							|  |  |  |         while 1: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 self.lockdir = self.cvslck | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |                 os.mkdir(self.cvslck, 0o777) | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |                 return | 
					
						
							| 
									
										
										
										
											2007-01-10 16:19:56 +00:00
										 |  |  |             except os.error as msg: | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |                 self.lockdir = None | 
					
						
							| 
									
										
										
										
											2008-01-06 21:13:42 +00:00
										 |  |  |                 if msg.args[0] == EEXIST: | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |                     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: | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |             print("unlink", self.lockfile) | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 os.unlink(self.lockfile) | 
					
						
							|  |  |  |             except os.error: | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  |             self.lockfile = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def unlockdir(self): | 
					
						
							|  |  |  |         if self.lockdir: | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |             print("rmdir", self.lockdir) | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |             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) | 
					
						
							| 
									
										
										
										
											1995-06-23 22:03:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def sleep(st, repository, delay): | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |     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 | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |     print("[%s]" % time.ctime(time.time())[11:19], end=' ') | 
					
						
							|  |  |  |     print("Waiting for %s's lock in" % user, repository) | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |     time.sleep(delay) | 
					
						
							| 
									
										
										
										
											1995-06-23 22:03:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ReadLock(Lock): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |     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() | 
					
						
							| 
									
										
										
										
											1995-06-23 22:03:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class WriteLock(Lock): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |     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 | 
					
						
							| 
									
										
										
										
											1995-06-23 22:03:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def MultipleWriteLock(repositories, delay = DELAY): | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |     while 1: | 
					
						
							|  |  |  |         locks = [] | 
					
						
							|  |  |  |         for r in repositories: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 locks.append(WriteLock(r, 0)) | 
					
						
							| 
									
										
										
										
											2007-01-10 16:19:56 +00:00
										 |  |  |             except Locked as instance: | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |                 del locks | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             break | 
					
						
							|  |  |  |         sleep(instance.msg, r, delay) | 
					
						
							|  |  |  |     return list | 
					
						
							| 
									
										
										
										
											1995-06-23 22:03:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test(): | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |     import sys | 
					
						
							|  |  |  |     if sys.argv[1:]: | 
					
						
							|  |  |  |         repository = sys.argv[1] | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         repository = "." | 
					
						
							|  |  |  |     rl = None | 
					
						
							|  |  |  |     wl = None | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |         print("attempting write lock ...") | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |         wl = WriteLock(repository) | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |         print("got it.") | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |         wl.unlock() | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |         print("attempting read lock ...") | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |         rl = ReadLock(repository) | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |         print("got it.") | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |         rl.unlock() | 
					
						
							|  |  |  |     finally: | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |         print([1]) | 
					
						
							|  |  |  |         print([2]) | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |         if rl: | 
					
						
							|  |  |  |             rl.unlock() | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |         print([3]) | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |         if wl: | 
					
						
							|  |  |  |             wl.unlock() | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |         print([4]) | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |         rl = None | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |         print([5]) | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |         wl = None | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |         print([6]) | 
					
						
							| 
									
										
										
										
											1995-06-23 22:03:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  |     test() |