~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lockdir.py

  • Committer: Martin Pool
  • Date: 2006-02-21 22:46:09 UTC
  • mto: This revision was merged to the branch mainline in revision 1569.
  • Revision ID: mbp@sourcefrog.net-20060221224609-a7f433d0488c080d
Confirm that only the intended holder of a lock was broken.

Show diffs side-by-side

added added

removed removed

Lines of Context:
101
101
from bzrlib.errors import (
102
102
        DirectoryNotEmpty,
103
103
        FileExists,
 
104
        LockBreakMismatch,
104
105
        LockBroken,
105
106
        LockContention,
106
107
        LockError,
203
204
        self.transport.delete(tmpname + self.INFO_NAME)
204
205
        self.transport.rmdir(tmpname)
205
206
 
206
 
    def force_break(self):
 
207
    def force_break(self, dead_holder_info):
207
208
        """Release a lock held by another process.
208
209
 
209
210
        WARNING: This should only be used when the other process is dead; if
210
211
        it still thinks it has the lock there will be two concurrent writers.
211
212
        In general the user's approval should be sought for lock breaks.
212
213
 
 
214
        dead_holder_info must be the result of a previous LockDir.peek() call;
 
215
        this is used to check that it's still held by the same process that
 
216
        the user decided was dead.  If this is not the current holder,
 
217
        LockBreakMismatch is raised.
 
218
 
213
219
        After the lock is broken it will not be held by any process.
214
220
        It is possible that another process may sneak in and take the 
215
221
        lock before the breaking process acquires it.
216
222
        """
 
223
        if not isinstance(dead_holder_info, dict):
 
224
            raise ValueError("dead_holder_info: %r" % dead_holder_info)
217
225
        if self._lock_held:
218
226
            raise AssertionError("can't break own lock: %r" % self)
219
 
        lock_info = self.peek()
220
 
        if lock_info is None:
 
227
        current_info = self.peek()
 
228
        if current_info is None:
221
229
            # must have been recently released
222
230
            return
 
231
        if current_info != dead_holder_info:
 
232
            raise LockBreakMismatch(self, current_info, dead_holder_info)
223
233
        tmpname = '%s.broken.%s.tmp' % (self.path, rand_chars(20))
224
234
        self.transport.rename(self.path, tmpname)
225
 
        self.transport.delete(tmpname + self.INFO_NAME)
 
235
        # check that we actually broke the right lock, not someone else;
 
236
        # there's a small race window between checking it and doing the 
 
237
        # rename.
 
238
        broken_info_path = tmpname + self.INFO_NAME
 
239
        broken_info = self._parse_info(self.transport.get(broken_info_path))
 
240
        if broken_info != dead_holder_info:
 
241
            raise LockBreakMismatch(self, broken_info, dead_holder_info)
 
242
        self.transport.delete(broken_info_path)
226
243
        self.transport.rmdir(tmpname)
227
244
 
228
245
    def confirm(self):
254
271
        """
255
272
        try:
256
273
            info = self._parse_info(self.transport.get(self._info_path))
257
 
            assert isinstance(info, Stanza), \
 
274
            assert isinstance(info, dict), \
258
275
                    "bad parse result %r" % info
259
 
            return info.as_dict()
 
276
            return info
260
277
        except NoSuchFile, e:
261
278
            return None
262
279
 
275
292
        RioWriter(outf).write_stanza(s)
276
293
 
277
294
    def _parse_info(self, info_file):
278
 
        return read_stanza(info_file.readlines())
 
295
        return read_stanza(info_file.readlines()).as_dict()
279
296
 
280
297
    def wait_lock(self, timeout=_DEFAULT_TIMEOUT_SECONDS,
281
298
                  poll=_DEFAULT_POLL_SECONDS):