~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lockdir.py

  • Committer: Martin Pool
  • Date: 2007-07-03 09:11:57 UTC
  • mto: This revision was merged to the branch mainline in revision 2584.
  • Revision ID: mbp@sourcefrog.net-20070703091157-r98tvj1rwxorobog
Clean up patch to make divergence from mainline smaller.

Separate out LockDir._remove_pending_dir.

Put some behaviour back that was depended upon by test cases that override
attempt_lock.

Typos.

Show diffs side-by-side

added added

removed removed

Lines of Context:
212
212
        :raises LockContention: If the lock is held by someone else.  The exception
213
213
            contains the info of the current holder of the lock.
214
214
        """
 
215
        self._trace("lock_write...")
 
216
        start_time = time.time()
 
217
        tmpname = self._create_pending_dir()
215
218
        try:
216
 
            self._trace("lock_write...")
217
 
            start_time = time.time()
218
 
            tmpname = self._create_pending_dir()
219
 
    
220
219
            self.transport.rename(tmpname, self._held_dir)
221
 
            # We must check we really got the lock, because Launchpad's sftp
222
 
            # server at one time had a bug were the rename would successfully
223
 
            # move the new directory into the existing directory, which was
224
 
            # incorrect.  It's possible some other servers or filesystems will
225
 
            # have a similar bug allowing someone to think they got the lock
226
 
            # when it's already held.
227
 
            info = self.peek()
228
 
            self._trace("after locking, info=%r", info)
229
 
            if info['nonce'] != self.nonce:
230
 
                self._trace("rename succeeded, "
231
 
                    "but lock is still held by someone else")
232
 
                raise LockContention(self)
233
 
            # we don't call confirm here because we don't want to set
234
 
            # _lock_held til we're sure it's true, and because it's really a
235
 
            # problem, not just regular contention, if this fails
236
 
            self._lock_held = True
237
 
            # FIXME: we should remove the pending lock if we fail, 
238
 
            # https://bugs.launchpad.net/bzr/+bug/109169
239
 
        except errors.PermissionDenied:
240
 
            self._trace("... lock failed, permission denied")
241
 
            raise
242
220
        except (PathError, DirectoryNotEmpty, FileExists, ResourceBusy), e:
243
221
            self._trace("... contention, %s", e)
244
 
            raise LockContention(self)
 
222
            self._remove_pending_dir(tmpname)
 
223
            raise LockContention(self)
 
224
        except Exception, e:
 
225
            self._trace("... lock failed, %s", e)
 
226
            self._remove_pending_dir(tmpname)
 
227
            raise
 
228
        # We must check we really got the lock, because Launchpad's sftp
 
229
        # server at one time had a bug were the rename would successfully
 
230
        # move the new directory into the existing directory, which was
 
231
        # incorrect.  It's possible some other servers or filesystems will
 
232
        # have a similar bug allowing someone to think they got the lock
 
233
        # when it's already held.
 
234
        info = self.peek()
 
235
        self._trace("after locking, info=%r", info)
 
236
        if info['nonce'] != self.nonce:
 
237
            self._trace("rename succeeded, "
 
238
                "but lock is still held by someone else")
 
239
            raise LockContention(self)
 
240
        # we don't call confirm here because we don't want to set
 
241
        # _lock_held til we're sure it's true, and because it's really a
 
242
        # problem, not just regular contention, if this fails
 
243
        self._lock_held = True
 
244
        # FIXME: we should remove the pending lock if we fail, 
 
245
        # https://bugs.launchpad.net/bzr/+bug/109169
245
246
        self._trace("... lock succeeded after %dms",
246
247
                (time.time() - start_time) * 1000)
247
248
        return self.nonce
248
249
 
 
250
    def _remove_pending_dir(self, tmpname):
 
251
        """Remove the pending directory
 
252
 
 
253
        This is called if we failed to rename into place, so that the pending 
 
254
        dirs don't clutter up the lockdir.
 
255
        """
 
256
        self._trace("remove %s", tmpname)
 
257
        self.transport.delete(tmpname + self.__INFO_NAME)
 
258
        self.transport.rmdir(tmpname)
 
259
 
249
260
    def _create_pending_dir(self):
250
261
        tmpname = '%s/%s.tmp' % (self.path, rand_chars(10))
251
262
        try:
435
446
        :return: The lock token.
436
447
        :raises LockContention: if the lock is held by someone else.
437
448
        """
438
 
        return self.wait_lock(max_attempts=1)
 
449
        if self._fake_read_lock:
 
450
            raise LockContention(self)
 
451
        if self.transport.is_readonly():
 
452
            raise UnlockableTransport(self.transport)
 
453
        return self._lock_core()
439
454
 
440
455
    def wait_lock(self, timeout=None, poll=None, max_attempts=None):
441
456
        """Wait a certain period for a lock.
466
481
        deadline_str = None
467
482
        last_info = None
468
483
        attempt_count = 0
469
 
        if self._fake_read_lock:
470
 
            raise LockContention(self)
471
 
        if self.transport.is_readonly():
472
 
            raise UnlockableTransport(self.transport)
473
484
        while True:
474
485
            attempt_count += 1
475
486
            try:
476
 
                return self._lock_core()
477
 
            except LockContention, err:
478
 
                # TODO: LockContention should only be raised when we're know
479
 
                # that the lock is held by someone else, in which case we
480
 
                # should include the locker info, so it can be used here.
481
 
                # In other cases, such as having a malformed lock present, we
482
 
                # should raise a different.
483
 
                #
484
 
                # we shouldn't need to peek again here, because _lock_core
485
 
                # does it
486
 
                new_info = self.peek()
487
 
                if new_info is not None and new_info != last_info:
488
 
                    if last_info is None:
489
 
                        start = 'Unable to obtain'
490
 
                    else:
491
 
                        start = 'Lock owner changed for'
492
 
                    last_info = new_info
493
 
                    formatted_info = self._format_lock_info(new_info)
494
 
                    if deadline_str is None:
495
 
                        deadline_str = time.strftime('%H:%M:%S',
496
 
                                                     time.localtime(deadline))
497
 
                    self._report_function('%s %s\n'
498
 
                                          '%s\n' # held by
499
 
                                          '%s\n' # locked ... ago
500
 
                                          'Will continue to try until %s\n',
501
 
                                          start,
502
 
                                          formatted_info[0],
503
 
                                          formatted_info[1],
504
 
                                          formatted_info[2],
505
 
                                          deadline_str)
506
 
 
507
 
                if (max_attempts is not None) and (attempt_count >= max_attempts):
508
 
                    self._trace("exceeded %d attempts")
509
 
                    raise LockContention(self)
510
 
                if time.time() + poll < deadline:
511
 
                    self._trace("waiting %ss", poll)
512
 
                    time.sleep(poll)
 
487
                return self.attempt_lock()
 
488
            except LockContention:
 
489
                # possibly report the blockage, then try again
 
490
                pass
 
491
            # TODO: In a few cases, we find out that there's contention by
 
492
            # reading the held info and observing that it's not ours.  In
 
493
            # those cases it's a bit redundant to read it again.  However,
 
494
            # the normal case (??) is that the rename fails and so we
 
495
            # don't know who holds the lock.  For simplicity we peek
 
496
            # always.
 
497
            new_info = self.peek()
 
498
            if new_info is not None and new_info != last_info:
 
499
                if last_info is None:
 
500
                    start = 'Unable to obtain'
513
501
                else:
514
 
                    self._trace("timeout after waiting %ss", timeout)
515
 
                    raise LockContention(self)
 
502
                    start = 'Lock owner changed for'
 
503
                last_info = new_info
 
504
                formatted_info = self._format_lock_info(new_info)
 
505
                if deadline_str is None:
 
506
                    deadline_str = time.strftime('%H:%M:%S',
 
507
                                                 time.localtime(deadline))
 
508
                self._report_function('%s %s\n'
 
509
                                      '%s\n' # held by
 
510
                                      '%s\n' # locked ... ago
 
511
                                      'Will continue to try until %s\n',
 
512
                                      start,
 
513
                                      formatted_info[0],
 
514
                                      formatted_info[1],
 
515
                                      formatted_info[2],
 
516
                                      deadline_str)
 
517
 
 
518
            if (max_attempts is not None) and (attempt_count >= max_attempts):
 
519
                self._trace("exceeded %d attempts")
 
520
                raise LockContention(self)
 
521
            if time.time() + poll < deadline:
 
522
                self._trace("waiting %ss", poll)
 
523
                time.sleep(poll)
 
524
            else:
 
525
                self._trace("timeout after waiting %ss", timeout)
 
526
                raise LockContention(self)
516
527
    
517
528
    def leave_in_place(self):
518
529
        self._locked_via_token = True
577
588
                self._trace("waiting %ss", poll)
578
589
                time.sleep(poll)
579
590
            else:
580
 
                self._trace("temeout after waiting %ss", timeout)
 
591
                self._trace("timeout after waiting %ss", timeout)
581
592
                raise LockContention(self)
582
593
 
583
594
    def _format_lock_info(self, info):
601
612
            if token != lock_token:
602
613
                raise errors.TokenMismatch(token, lock_token)
603
614
            else:
604
 
                self._trace("Revalidated by token %r", token)
 
615
                self._trace("revalidated by token %r", token)
605
616
 
606
617
    def _trace(self, format, *args):
607
618
        if 'lock' not in debug.debug_flags: