]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
initial checkin
authorGuido van Rossum <guido@python.org>
Fri, 23 Jun 1995 22:03:28 +0000 (22:03 +0000)
committerGuido van Rossum <guido@python.org>
Fri, 23 Jun 1995 22:03:28 +0000 (22:03 +0000)
Demo/pdist/cvslock.py [new file with mode: 0755]

diff --git a/Demo/pdist/cvslock.py b/Demo/pdist/cvslock.py
new file mode 100755 (executable)
index 0000000..a421e1a
--- /dev/null
@@ -0,0 +1,280 @@
+"""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 = `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, 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, 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()