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
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__':