1 """CVS locking algorithm.
6 As reverse engineered from the CVS 1.3 sources (file lock.c):
8 - Locking is done on a per repository basis (but a process can hold
9 write locks for multiple directories); all lock files are placed in
10 the repository and have names beginning with "#cvs.".
12 - Before even attempting to lock, a file "#cvs.tfl.<pid>" is created
13 (and removed again), to test that we can write the repository. [The
14 algorithm can still be fooled (1) if the repository's mode is changed
15 while attempting to lock; (2) if this file exists and is writable but
16 the directory is not.]
18 - While creating the actual read/write lock files (which may exist for
19 a long time), a "meta-lock" is held. The meta-lock is a directory
20 named "#cvs.lock" in the repository. The meta-lock is also held while
25 - acquire the meta-lock
26 - create the file "#cvs.rfl.<pid>"
27 - release the meta-lock
29 - To set a write lock:
31 - acquire the meta-lock
32 - check that there are no files called "#cvs.rfl.*"
33 - if there are, release the meta-lock, sleep, try again
34 - create the file "#cvs.wfl.<pid>"
36 - To release a write lock:
38 - remove the file "#cvs.wfl.<pid>"
41 - To release a read lock:
43 - remove the file "#cvs.rfl.<pid>"
49 - A process should read-lock at most one repository at a time.
51 - A process may write-lock as many repositories as it wishes (to avoid
52 deadlocks, I presume it should always lock them top-down in the
55 - A process should make sure it removes all its lock files and
56 directories when it crashes.
58 - Limitation: one user id should not be committing files into the same
59 repository at the same time.
62 Turn this into Python code
63 --------------------------
65 rl = ReadLock(repository, waittime)
67 wl = WriteLock(repository, waittime)
69 list = MultipleWriteLock([repository1, repository2, ...], waittime)
84 # XXX This should be the same on all Unix versions
88 # Files used for locking (must match cvs.h in the CVS sources)
96 def __init__(self, msg):
100 return repr(self.msg)
112 def __init__(self, repository = ".", delay = DELAY):
113 self.repository = repository
117 pid = repr(os.getpid())
118 self.cvslck = self.join(CVSLCK)
119 self.cvsrfl = self.join(CVSRFL + pid)
120 self.cvswfl = self.join(CVSWFL + pid)
126 def setlockdir(self):
129 self.lockdir = self.cvslck
130 os.mkdir(self.cvslck, 0777)
132 except os.error, msg:
136 st = os.stat(self.cvslck)
141 raise Error("failed to lock %s: %s" % (
142 self.repository, msg))
148 def unlockfile(self):
150 print "unlink", self.lockfile
152 os.unlink(self.lockfile)
159 print "rmdir", self.lockdir
161 os.rmdir(self.lockdir)
167 sleep(st, self.repository, self.delay)
169 def join(self, name):
170 return os.path.join(self.repository, name)
173 def sleep(st, repository, delay):
176 uid = st[stat.ST_UID]
178 pwent = pwd.getpwuid(uid)
181 user = "uid %d" % uid
182 print "[%s]" % time.ctime(time.time())[11:19],
183 print "Waiting for %s's lock in" % user, repository
187 class ReadLock(Lock):
189 def __init__(self, repository, delay = DELAY):
190 Lock.__init__(self, repository, delay)
194 self.lockfile = self.cvsrfl
195 fp = open(self.lockfile, 'w')
204 class WriteLock(Lock):
206 def __init__(self, repository, delay = DELAY):
207 Lock.__init__(self, repository, delay)
210 uid = self.readers_exist()
215 self.lockfile = self.cvswfl
216 fp = open(self.lockfile, 'w')
219 def readers_exist(self):
221 for name in os.listdir(self.repository):
222 if name[:n] == CVSRFL:
224 st = os.stat(self.join(name))
231 def MultipleWriteLock(repositories, delay = DELAY):
234 for r in repositories:
236 locks.append(WriteLock(r, 0))
237 except Locked, instance:
242 sleep(instance.msg, r, delay)
249 repository = sys.argv[1]
255 print "attempting write lock ..."
256 wl = WriteLock(repository)
259 print "attempting read lock ..."
260 rl = ReadLock(repository)
265 sys.exc_traceback = None
279 if __name__ == '__main__':