~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lockdir.py

  • Committer: INADA Naoki
  • Date: 2011-05-18 06:01:08 UTC
  • mto: This revision was merged to the branch mainline in revision 5894.
  • Revision ID: songofacandy@gmail.com-20110518060108-86t2kffcrzu0nf6i
Update Japanese docs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2006-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
110
110
    debug,
111
111
    errors,
112
112
    lock,
 
113
    osutils,
113
114
    )
114
115
import bzrlib.config
 
116
from bzrlib.decorators import only_raises
115
117
from bzrlib.errors import (
116
118
        DirectoryNotEmpty,
117
119
        FileExists,
118
120
        LockBreakMismatch,
119
121
        LockBroken,
120
122
        LockContention,
 
123
        LockCorrupt,
121
124
        LockFailed,
122
125
        LockNotHeld,
123
126
        NoSuchFile,
149
152
# files/dirs created.
150
153
 
151
154
 
152
 
_DEFAULT_TIMEOUT_SECONDS = 300
 
155
_DEFAULT_TIMEOUT_SECONDS = 30
153
156
_DEFAULT_POLL_SECONDS = 1.0
154
157
 
155
158
 
240
243
        # incorrect.  It's possible some other servers or filesystems will
241
244
        # have a similar bug allowing someone to think they got the lock
242
245
        # when it's already held.
 
246
        #
 
247
        # See <https://bugs.launchpad.net/bzr/+bug/498378> for one case.
 
248
        #
 
249
        # Strictly the check is unnecessary and a waste of time for most
 
250
        # people, but probably worth trapping if something is wrong.
243
251
        info = self.peek()
244
252
        self._trace("after locking, info=%r", info)
245
 
        if info['nonce'] != self.nonce:
 
253
        if info is None:
 
254
            raise LockFailed(self, "lock was renamed into place, but "
 
255
                "now is missing!")
 
256
        if info.get('nonce') != self.nonce:
246
257
            self._trace("rename succeeded, "
247
258
                "but lock is still held by someone else")
248
259
            raise LockContention(self)
286
297
                                            info_bytes)
287
298
        return tmpname
288
299
 
 
300
    @only_raises(LockNotHeld, LockBroken)
289
301
    def unlock(self):
290
302
        """Release a held lock
291
303
        """
335
347
        This is a UI centric function: it uses the bzrlib.ui.ui_factory to
336
348
        prompt for input if a lock is detected and there is any doubt about
337
349
        it possibly being still active.
 
350
 
 
351
        :returns: LockResult for the broken lock.
338
352
        """
339
353
        self._check_not_locked()
340
 
        holder_info = self.peek()
 
354
        try:
 
355
            holder_info = self.peek()
 
356
        except LockCorrupt, e:
 
357
            # The lock info is corrupt.
 
358
            if bzrlib.ui.ui_factory.get_boolean("Break (corrupt %r)" % (self,)):
 
359
                self.force_break_corrupt(e.file_data)
 
360
            return
341
361
        if holder_info is not None:
342
362
            lock_info = '\n'.join(self._format_lock_info(holder_info))
343
 
            if bzrlib.ui.ui_factory.get_boolean("Break %s" % lock_info):
344
 
                self.force_break(holder_info)
 
363
            if bzrlib.ui.ui_factory.confirm_action(
 
364
                "Break %(lock_info)s", 'bzrlib.lockdir.break', 
 
365
                dict(lock_info=lock_info)):
 
366
                result = self.force_break(holder_info)
 
367
                bzrlib.ui.ui_factory.show_message(
 
368
                    "Broke lock %s" % result.lock_url)
345
369
 
346
370
    def force_break(self, dead_holder_info):
347
371
        """Release a lock held by another process.
358
382
        After the lock is broken it will not be held by any process.
359
383
        It is possible that another process may sneak in and take the
360
384
        lock before the breaking process acquires it.
 
385
 
 
386
        :returns: LockResult for the broken lock.
361
387
        """
362
388
        if not isinstance(dead_holder_info, dict):
363
389
            raise ValueError("dead_holder_info: %r" % dead_holder_info)
383
409
                                 current_info.get('nonce'))
384
410
        for hook in self.hooks['lock_broken']:
385
411
            hook(result)
 
412
        return result
 
413
 
 
414
    def force_break_corrupt(self, corrupt_info_lines):
 
415
        """Release a lock that has been corrupted.
 
416
        
 
417
        This is very similar to force_break, it except it doesn't assume that
 
418
        self.peek() can work.
 
419
        
 
420
        :param corrupt_info_lines: the lines of the corrupted info file, used
 
421
            to check that the lock hasn't changed between reading the (corrupt)
 
422
            info file and calling force_break_corrupt.
 
423
        """
 
424
        # XXX: this copes with unparseable info files, but what about missing
 
425
        # info files?  Or missing lock dirs?
 
426
        self._check_not_locked()
 
427
        tmpname = '%s/broken.%s.tmp' % (self.path, rand_chars(20))
 
428
        self.transport.rename(self._held_dir, tmpname)
 
429
        # check that we actually broke the right lock, not someone else;
 
430
        # there's a small race window between checking it and doing the
 
431
        # rename.
 
432
        broken_info_path = tmpname + self.__INFO_NAME
 
433
        broken_content = self.transport.get_bytes(broken_info_path)
 
434
        broken_lines = osutils.split_lines(broken_content)
 
435
        if broken_lines != corrupt_info_lines:
 
436
            raise LockBreakMismatch(self, broken_lines, corrupt_info_lines)
 
437
        self.transport.delete(broken_info_path)
 
438
        self.transport.rmdir(tmpname)
 
439
        result = lock.LockResult(self.transport.abspath(self.path))
 
440
        for hook in self.hooks['lock_broken']:
 
441
            hook(result)
386
442
 
387
443
    def _check_not_locked(self):
388
444
        """If the lock is held by this instance, raise an error."""
414
470
 
415
471
        peek() reads the info file of the lock holder, if any.
416
472
        """
417
 
        return self._parse_info(self.transport.get(path))
 
473
        return self._parse_info(self.transport.get_bytes(path))
418
474
 
419
475
    def peek(self):
420
476
        """Check if the lock is held by anyone.
421
477
 
422
 
        If it is held, this returns the lock info structure as a rio Stanza,
 
478
        If it is held, this returns the lock info structure as a dict
423
479
        which contains some information about the current lock holder.
424
480
        Otherwise returns None.
425
481
        """
436
492
        # XXX: is creating this here inefficient?
437
493
        config = bzrlib.config.GlobalConfig()
438
494
        try:
439
 
            user = config.user_email()
440
 
        except errors.NoEmailInUsername:
441
495
            user = config.username()
 
496
        except errors.NoWhoami:
 
497
            user = osutils.getuser_unicode()
442
498
        s = rio.Stanza(hostname=get_host_name(),
443
499
                   pid=str(os.getpid()),
444
500
                   start_time=str(int(time.time())),
447
503
                   )
448
504
        return s.to_string()
449
505
 
450
 
    def _parse_info(self, info_file):
451
 
        return rio.read_stanza(info_file.readlines()).as_dict()
 
506
    def _parse_info(self, info_bytes):
 
507
        lines = osutils.split_lines(info_bytes)
 
508
        try:
 
509
            stanza = rio.read_stanza(lines)
 
510
        except ValueError, e:
 
511
            mutter('Corrupt lock info file: %r', lines)
 
512
            raise LockCorrupt("could not parse lock info file: " + str(e),
 
513
                              lines)
 
514
        if stanza is None:
 
515
            # see bug 185013; we fairly often end up with the info file being
 
516
            # empty after an interruption; we could log a message here but
 
517
            # there may not be much we can say
 
518
            return {}
 
519
        else:
 
520
            return stanza.as_dict()
452
521
 
453
522
    def attempt_lock(self):
454
523
        """Take the lock; fail if it's already held.
468
537
            hook(hook_result)
469
538
        return result
470
539
 
 
540
    def lock_url_for_display(self):
 
541
        """Give a nicely-printable representation of the URL of this lock."""
 
542
        # As local lock urls are correct we display them.
 
543
        # We avoid displaying remote lock urls.
 
544
        lock_url = self.transport.abspath(self.path)
 
545
        if lock_url.startswith('file://'):
 
546
            lock_url = lock_url.split('.bzr/')[0]
 
547
        else:
 
548
            lock_url = ''
 
549
        return lock_url
 
550
 
471
551
    def wait_lock(self, timeout=None, poll=None, max_attempts=None):
472
552
        """Wait a certain period for a lock.
473
553
 
497
577
        deadline_str = None
498
578
        last_info = None
499
579
        attempt_count = 0
 
580
        lock_url = self.lock_url_for_display()
500
581
        while True:
501
582
            attempt_count += 1
502
583
            try:
521
602
                if deadline_str is None:
522
603
                    deadline_str = time.strftime('%H:%M:%S',
523
604
                                                 time.localtime(deadline))
524
 
                lock_url = self.transport.abspath(self.path)
525
 
                self._report_function('%s %s\n'
526
 
                                      '%s\n' # held by
527
 
                                      '%s\n' # locked ... ago
528
 
                                      'Will continue to try until %s, unless '
529
 
                                      'you press Ctrl-C\n'
530
 
                                      'If you\'re sure that it\'s not being '
531
 
                                      'modified, use bzr break-lock %s',
532
 
                                      start,
533
 
                                      formatted_info[0],
534
 
                                      formatted_info[1],
535
 
                                      formatted_info[2],
536
 
                                      deadline_str,
537
 
                                      lock_url)
538
 
 
 
605
                user, hostname, pid, time_ago = formatted_info
 
606
                msg = ('%s lock %s '        # lock_url
 
607
                    'held by '              # start
 
608
                    '%s\n'                  # user
 
609
                    'at %s '                # hostname
 
610
                    '[process #%s], '       # pid
 
611
                    'acquired %s.')         # time ago
 
612
                msg_args = [start, lock_url, user, hostname, pid, time_ago]
 
613
                if timeout > 0:
 
614
                    msg += ('\nWill continue to try until %s, unless '
 
615
                        'you press Ctrl-C.')
 
616
                    msg_args.append(deadline_str)
 
617
                msg += '\nSee "bzr help break-lock" for more.'
 
618
                self._report_function(msg, *msg_args)
539
619
            if (max_attempts is not None) and (attempt_count >= max_attempts):
540
620
                self._trace("exceeded %d attempts")
541
621
                raise LockContention(self)
543
623
                self._trace("waiting %ss", poll)
544
624
                time.sleep(poll)
545
625
            else:
 
626
                # As timeout is always 0 for remote locks
 
627
                # this block is applicable only for local
 
628
                # lock contention
546
629
                self._trace("timeout after waiting %ss", timeout)
547
 
                raise LockContention(self)
 
630
                raise LockContention('(local)', lock_url)
548
631
 
549
632
    def leave_in_place(self):
550
633
        self._locked_via_token = True
595
678
 
596
679
    def _format_lock_info(self, info):
597
680
        """Turn the contents of peek() into something for the user"""
598
 
        lock_url = self.transport.abspath(self.path)
599
 
        delta = time.time() - int(info['start_time'])
 
681
        start_time = info.get('start_time')
 
682
        if start_time is None:
 
683
            time_ago = '(unknown)'
 
684
        else:
 
685
            time_ago = format_delta(time.time() - int(info['start_time']))
 
686
        user = info.get('user', '<unknown>')
 
687
        hostname = info.get('hostname', '<unknown>')
 
688
        pid = info.get('pid', '<unknown>')
600
689
        return [
601
 
            'lock %s' % (lock_url,),
602
 
            'held by %(user)s on host %(hostname)s [process #%(pid)s]' % info,
603
 
            'locked %s' % (format_delta(delta),),
 
690
            user,
 
691
            hostname,
 
692
            pid,
 
693
            time_ago,
604
694
            ]
605
695
 
606
696
    def validate_token(self, token):