~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lockdir.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-02-26 03:15:58 UTC
  • mfrom: (4050.1.3 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20090226031558-1ubr618vdn4r5f07
(robertc) Fix race condition with branch hooks during cloning when
        the new branch is stacked. (Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008 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
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
"""On-disk mutex protecting a resource
18
18
 
110
110
    debug,
111
111
    errors,
112
112
    lock,
113
 
    osutils,
114
113
    )
115
114
import bzrlib.config
116
 
from bzrlib.decorators import only_raises
117
115
from bzrlib.errors import (
118
116
        DirectoryNotEmpty,
119
117
        FileExists,
127
125
        ResourceBusy,
128
126
        TransportError,
129
127
        )
 
128
from bzrlib.hooks import Hooks
130
129
from bzrlib.trace import mutter, note
131
130
from bzrlib.osutils import format_delta, rand_chars, get_host_name
132
131
import bzrlib.ui
242
241
        # incorrect.  It's possible some other servers or filesystems will
243
242
        # have a similar bug allowing someone to think they got the lock
244
243
        # when it's already held.
245
 
        #
246
 
        # See <https://bugs.edge.launchpad.net/bzr/+bug/498378> for one case.
247
 
        #
248
 
        # Strictly the check is unnecessary and a waste of time for most
249
 
        # people, but probably worth trapping if something is wrong.
250
244
        info = self.peek()
251
245
        self._trace("after locking, info=%r", info)
252
 
        if info is None:
253
 
            raise LockFailed(self, "lock was renamed into place, but "
254
 
                "now is missing!")
255
 
        if info.get('nonce') != self.nonce:
 
246
        if info['nonce'] != self.nonce:
256
247
            self._trace("rename succeeded, "
257
248
                "but lock is still held by someone else")
258
249
            raise LockContention(self)
296
287
                                            info_bytes)
297
288
        return tmpname
298
289
 
299
 
    @only_raises(LockNotHeld, LockBroken)
300
290
    def unlock(self):
301
291
        """Release a held lock
302
292
        """
304
294
            self._fake_read_lock = False
305
295
            return
306
296
        if not self._lock_held:
307
 
            return lock.cant_unlock_not_held(self)
 
297
            raise LockNotHeld(self)
308
298
        if self._locked_via_token:
309
299
            self._locked_via_token = False
310
300
            self._lock_held = False
336
326
            self._trace("... unlock succeeded after %dms",
337
327
                    (time.time() - start_time) * 1000)
338
328
            result = lock.LockResult(self.transport.abspath(self.path),
339
 
                                     old_nonce)
 
329
                old_nonce)
340
330
            for hook in self.hooks['lock_released']:
341
331
                hook(result)
342
332
 
390
380
            raise LockBreakMismatch(self, broken_info, dead_holder_info)
391
381
        self.transport.delete(broken_info_path)
392
382
        self.transport.rmdir(tmpname)
393
 
        result = lock.LockResult(self.transport.abspath(self.path),
394
 
                                 current_info.get('nonce'))
395
 
        for hook in self.hooks['lock_broken']:
396
 
            hook(result)
397
383
 
398
384
    def _check_not_locked(self):
399
385
        """If the lock is held by this instance, raise an error."""
425
411
 
426
412
        peek() reads the info file of the lock holder, if any.
427
413
        """
428
 
        return self._parse_info(self.transport.get_bytes(path))
 
414
        return self._parse_info(self.transport.get(path))
429
415
 
430
416
    def peek(self):
431
417
        """Check if the lock is held by anyone.
432
418
 
433
 
        If it is held, this returns the lock info structure as a dict
 
419
        If it is held, this returns the lock info structure as a rio Stanza,
434
420
        which contains some information about the current lock holder.
435
421
        Otherwise returns None.
436
422
        """
458
444
                   )
459
445
        return s.to_string()
460
446
 
461
 
    def _parse_info(self, info_bytes):
462
 
        stanza = rio.read_stanza(osutils.split_lines(info_bytes))
463
 
        if stanza is None:
464
 
            # see bug 185013; we fairly often end up with the info file being
465
 
            # empty after an interruption; we could log a message here but
466
 
            # there may not be much we can say
467
 
            return {}
468
 
        else:
469
 
            return stanza.as_dict()
 
447
    def _parse_info(self, info_file):
 
448
        return rio.read_stanza(info_file.readlines()).as_dict()
470
449
 
471
450
    def attempt_lock(self):
472
451
        """Take the lock; fail if it's already held.
540
519
                    deadline_str = time.strftime('%H:%M:%S',
541
520
                                                 time.localtime(deadline))
542
521
                lock_url = self.transport.abspath(self.path)
543
 
                # See <https://bugs.edge.launchpad.net/bzr/+bug/250451>
544
 
                # the URL here is sometimes not one that is useful to the
545
 
                # user, perhaps being wrapped in a lp-%d or chroot decorator,
546
 
                # especially if this error is issued from the server.
547
522
                self._report_function('%s %s\n'
548
 
                    '%s\n' # held by
549
 
                    '%s\n' # locked ... ago
550
 
                    'Will continue to try until %s, unless '
551
 
                    'you press Ctrl-C.\n'
552
 
                    'See "bzr help break-lock" for more.',
553
 
                    start,
554
 
                    formatted_info[0],
555
 
                    formatted_info[1],
556
 
                    formatted_info[2],
557
 
                    deadline_str,
558
 
                    )
 
523
                                      '%s\n' # held by
 
524
                                      '%s\n' # locked ... ago
 
525
                                      'Will continue to try until %s, unless '
 
526
                                      'you press Ctrl-C\n'
 
527
                                      'If you\'re sure that it\'s not being '
 
528
                                      'modified, use bzr break-lock %s',
 
529
                                      start,
 
530
                                      formatted_info[0],
 
531
                                      formatted_info[1],
 
532
                                      formatted_info[2],
 
533
                                      deadline_str,
 
534
                                      lock_url)
559
535
 
560
536
            if (max_attempts is not None) and (attempt_count >= max_attempts):
561
537
                self._trace("exceeded %d attempts")
617
593
    def _format_lock_info(self, info):
618
594
        """Turn the contents of peek() into something for the user"""
619
595
        lock_url = self.transport.abspath(self.path)
620
 
        start_time = info.get('start_time')
621
 
        if start_time is None:
622
 
            time_ago = '(unknown)'
623
 
        else:
624
 
            time_ago = format_delta(time.time() - int(info['start_time']))
 
596
        delta = time.time() - int(info['start_time'])
625
597
        return [
626
598
            'lock %s' % (lock_url,),
627
 
            'held by %s on host %s [process #%s]' %
628
 
                tuple([info.get(x, '<unknown>') for x in ['user', 'hostname', 'pid']]),
629
 
            'locked %s' % (time_ago,),
 
599
            'held by %(user)s on host %(hostname)s [process #%(pid)s]' % info,
 
600
            'locked %s' % (format_delta(delta),),
630
601
            ]
631
602
 
632
603
    def validate_token(self, token):