~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lock.py

Merge previous attempt into current trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2005-2010 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
37
37
import errno
38
38
import os
39
39
import sys
 
40
import warnings
40
41
 
41
42
from bzrlib import (
 
43
    debug,
42
44
    errors,
43
45
    osutils,
44
46
    trace,
82
84
        return self.lock_url == other.lock_url and self.details == other.details
83
85
 
84
86
    def __repr__(self):
85
 
        return '%s(%s%s)' % (self.__class__.__name__,
 
87
        return '%s(%s, %s)' % (self.__class__.__name__,
86
88
                             self.lock_url, self.details)
87
89
 
88
90
 
 
91
class LogicalLockResult(object):
 
92
    """The result of a lock_read/lock_write/lock_tree_write call on lockables.
 
93
 
 
94
    :ivar unlock: A callable which will unlock the lock.
 
95
    """
 
96
 
 
97
    def __init__(self, unlock):
 
98
        self.unlock = unlock
 
99
 
 
100
    def __repr__(self):
 
101
        return "LogicalLockResult(%s)" % (self.unlock)
 
102
 
 
103
 
 
104
 
 
105
def cant_unlock_not_held(locked_object):
 
106
    """An attempt to unlock failed because the object was not locked.
 
107
 
 
108
    This provides a policy point from which we can generate either a warning 
 
109
    or an exception.
 
110
    """
 
111
    # This is typically masking some other error and called from a finally
 
112
    # block, so it's useful to have the option not to generate a new error
 
113
    # here.  You can use -Werror to make it fatal.  It should possibly also
 
114
    # raise LockNotHeld.
 
115
    if 'unlock' in debug.debug_flags:
 
116
        warnings.warn("%r is already unlocked" % (locked_object,),
 
117
            stacklevel=3)
 
118
    else:
 
119
        raise errors.LockNotHeld(locked_object)
 
120
 
 
121
 
89
122
try:
90
123
    import fcntl
91
124
    have_fcntl = True
171
204
            if self.filename in _fcntl_WriteLock._open_locks:
172
205
                self._clear_f()
173
206
                raise errors.LockContention(self.filename)
 
207
            if self.filename in _fcntl_ReadLock._open_locks:
 
208
                if 'strict_locks' in debug.debug_flags:
 
209
                    self._clear_f()
 
210
                    raise errors.LockContention(self.filename)
 
211
                else:
 
212
                    trace.mutter('Write lock taken w/ an open read lock on: %s'
 
213
                                 % (self.filename,))
174
214
 
175
215
            self._open(self.filename, 'rb+')
176
216
            # reserve a slot for this lock - even if the lockf call fails,
177
 
            # at thisi point unlock() will be called, because self.f is set.
 
217
            # at this point unlock() will be called, because self.f is set.
178
218
            # TODO: make this fully threadsafe, if we decide we care.
179
219
            _fcntl_WriteLock._open_locks.add(self.filename)
180
220
            try:
201
241
        def __init__(self, filename):
202
242
            super(_fcntl_ReadLock, self).__init__()
203
243
            self.filename = osutils.realpath(filename)
 
244
            if self.filename in _fcntl_WriteLock._open_locks:
 
245
                if 'strict_locks' in debug.debug_flags:
 
246
                    # We raise before calling _open so we don't need to
 
247
                    # _clear_f
 
248
                    raise errors.LockContention(self.filename)
 
249
                else:
 
250
                    trace.mutter('Read lock taken w/ an open write lock on: %s'
 
251
                                 % (self.filename,))
204
252
            _fcntl_ReadLock._open_locks.setdefault(self.filename, 0)
205
253
            _fcntl_ReadLock._open_locks[self.filename] += 1
206
254
            self._open(filename, 'rb')
399
447
            DWORD,                 # dwFlagsAndAttributes
400
448
            HANDLE                 # hTemplateFile
401
449
        )((_function_name, ctypes.windll.kernel32))
402
 
    
 
450
 
403
451
    INVALID_HANDLE_VALUE = -1
404
 
    
 
452
 
405
453
    GENERIC_READ = 0x80000000
406
454
    GENERIC_WRITE = 0x40000000
407
455
    FILE_SHARE_READ = 1
408
456
    OPEN_ALWAYS = 4
409
457
    FILE_ATTRIBUTE_NORMAL = 128
410
 
    
 
458
 
411
459
    ERROR_ACCESS_DENIED = 5
412
460
    ERROR_SHARING_VIOLATION = 32
413
461
 
484
532
# We default to using the first available lock class.
485
533
_lock_type, WriteLock, ReadLock = _lock_classes[0]
486
534
 
 
535
 
 
536
class _RelockDebugMixin(object):
 
537
    """Mixin support for -Drelock flag.
 
538
 
 
539
    Add this as a base class then call self._note_lock with 'r' or 'w' when
 
540
    acquiring a read- or write-lock.  If this object was previously locked (and
 
541
    locked the same way), and -Drelock is set, then this will trace.note a
 
542
    message about it.
 
543
    """
 
544
    
 
545
    _prev_lock = None
 
546
 
 
547
    def _note_lock(self, lock_type):
 
548
        if 'relock' in debug.debug_flags and self._prev_lock == lock_type:
 
549
            if lock_type == 'r':
 
550
                type_name = 'read'
 
551
            else:
 
552
                type_name = 'write'
 
553
            trace.note('%r was %s locked again', self, type_name)
 
554
        self._prev_lock = lock_type
 
555