/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/lock.py

  • Committer: Martin von Gagern
  • Date: 2010-04-20 08:47:38 UTC
  • mfrom: (5167 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5195.
  • Revision ID: martin.vgagern@gmx.net-20100420084738-ygymnqmdllzrhpfn
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 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
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
 
18
18
"""Locking using OS file locks or file existence.
35
35
"""
36
36
 
37
37
import errno
 
38
import os
38
39
import sys
 
40
import warnings
39
41
 
40
42
from bzrlib import (
 
43
    debug,
41
44
    errors,
42
45
    osutils,
43
46
    trace,
44
47
    )
45
 
from bzrlib.hooks import Hooks
 
48
from bzrlib.hooks import HookPoint, Hooks
46
49
 
47
50
 
48
51
class LockHooks(Hooks):
49
52
 
50
53
    def __init__(self):
51
54
        Hooks.__init__(self)
52
 
 
53
 
        # added in 1.8; called with a LockResult when a physical lock is
54
 
        # acquired
55
 
        self['lock_acquired'] = []
56
 
 
57
 
        # added in 1.8; called with a LockResult when a physical lock is
58
 
        # acquired
59
 
        self['lock_released'] = []
 
55
        self.create_hook(HookPoint('lock_acquired',
 
56
            "Called with a bzrlib.lock.LockResult when a physical lock is "
 
57
            "acquired.", (1, 8), None))
 
58
        self.create_hook(HookPoint('lock_released',
 
59
            "Called with a bzrlib.lock.LockResult when a physical lock is "
 
60
            "released.", (1, 8), None))
 
61
        self.create_hook(HookPoint('lock_broken',
 
62
            "Called with a bzrlib.lock.LockResult when a physical lock is "
 
63
            "broken.", (1, 15), None))
60
64
 
61
65
 
62
66
class Lock(object):
79
83
    def __eq__(self, other):
80
84
        return self.lock_url == other.lock_url and self.details == other.details
81
85
 
 
86
    def __repr__(self):
 
87
        return '%s(%s, %s)' % (self.__class__.__name__,
 
88
                             self.lock_url, self.details)
 
89
 
 
90
 
 
91
def cant_unlock_not_held(locked_object):
 
92
    """An attempt to unlock failed because the object was not locked.
 
93
 
 
94
    This provides a policy point from which we can generate either a warning 
 
95
    or an exception.
 
96
    """
 
97
    # This is typically masking some other error and called from a finally
 
98
    # block, so it's useful to have the option not to generate a new error
 
99
    # here.  You can use -Werror to make it fatal.  It should possibly also
 
100
    # raise LockNotHeld.
 
101
    if 'unlock' in debug.debug_flags:
 
102
        warnings.warn("%r is already unlocked" % (locked_object,),
 
103
            stacklevel=3)
 
104
    else:
 
105
        raise errors.LockNotHeld(locked_object)
 
106
 
 
107
 
 
108
try:
 
109
    import fcntl
 
110
    have_fcntl = True
 
111
except ImportError:
 
112
    have_fcntl = False
 
113
 
 
114
have_pywin32 = False
 
115
have_ctypes_win32 = False
 
116
if sys.platform == 'win32':
 
117
    import msvcrt
 
118
    try:
 
119
        import win32file, pywintypes, winerror
 
120
        have_pywin32 = True
 
121
    except ImportError:
 
122
        pass
 
123
 
 
124
    try:
 
125
        import ctypes
 
126
        have_ctypes_win32 = True
 
127
    except ImportError:
 
128
        pass
 
129
 
82
130
 
83
131
class _OSLock(object):
84
132
 
119
167
        raise NotImplementedError()
120
168
 
121
169
 
122
 
try:
123
 
    import fcntl
124
 
    have_fcntl = True
125
 
except ImportError:
126
 
    have_fcntl = False
127
 
try:
128
 
    import win32con, win32file, pywintypes, winerror, msvcrt
129
 
    have_pywin32 = True
130
 
except ImportError:
131
 
    have_pywin32 = False
132
 
try:
133
 
    import ctypes, msvcrt
134
 
    have_ctypes = True
135
 
except ImportError:
136
 
    have_ctypes = False
137
 
 
138
 
 
139
170
_lock_classes = []
140
171
 
141
172
 
142
173
if have_fcntl:
143
 
    LOCK_SH = fcntl.LOCK_SH
144
 
    LOCK_NB = fcntl.LOCK_NB
145
 
    lock_EX = fcntl.LOCK_EX
146
 
 
147
174
 
148
175
    class _fcntl_FileLock(_OSLock):
149
176
 
163
190
            if self.filename in _fcntl_WriteLock._open_locks:
164
191
                self._clear_f()
165
192
                raise errors.LockContention(self.filename)
 
193
            if self.filename in _fcntl_ReadLock._open_locks:
 
194
                if 'strict_locks' in debug.debug_flags:
 
195
                    self._clear_f()
 
196
                    raise errors.LockContention(self.filename)
 
197
                else:
 
198
                    trace.mutter('Write lock taken w/ an open read lock on: %s'
 
199
                                 % (self.filename,))
166
200
 
167
201
            self._open(self.filename, 'rb+')
168
202
            # reserve a slot for this lock - even if the lockf call fails,
169
 
            # at thisi point unlock() will be called, because self.f is set.
 
203
            # at this point unlock() will be called, because self.f is set.
170
204
            # TODO: make this fully threadsafe, if we decide we care.
171
205
            _fcntl_WriteLock._open_locks.add(self.filename)
172
206
            try:
179
213
                    self.unlock()
180
214
                # we should be more precise about whats a locking
181
215
                # error and whats a random-other error
182
 
                raise errors.LockContention(e)
 
216
                raise errors.LockContention(self.filename, e)
183
217
 
184
218
        def unlock(self):
185
219
            _fcntl_WriteLock._open_locks.remove(self.filename)
193
227
        def __init__(self, filename):
194
228
            super(_fcntl_ReadLock, self).__init__()
195
229
            self.filename = osutils.realpath(filename)
 
230
            if self.filename in _fcntl_WriteLock._open_locks:
 
231
                if 'strict_locks' in debug.debug_flags:
 
232
                    # We raise before calling _open so we don't need to
 
233
                    # _clear_f
 
234
                    raise errors.LockContention(self.filename)
 
235
                else:
 
236
                    trace.mutter('Read lock taken w/ an open write lock on: %s'
 
237
                                 % (self.filename,))
196
238
            _fcntl_ReadLock._open_locks.setdefault(self.filename, 0)
197
239
            _fcntl_ReadLock._open_locks[self.filename] += 1
198
240
            self._open(filename, 'rb')
203
245
            except IOError, e:
204
246
                # we should be more precise about whats a locking
205
247
                # error and whats a random-other error
206
 
                raise errors.LockContention(e)
 
248
                raise errors.LockContention(self.filename, e)
207
249
 
208
250
        def unlock(self):
209
251
            count = _fcntl_ReadLock._open_locks[self.filename]
271
313
                fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
272
314
            except IOError, e:
273
315
                # TODO: Raise a more specific error based on the type of error
274
 
                raise errors.LockContention(e)
 
316
                raise errors.LockContention(self.filename, e)
275
317
            _fcntl_WriteLock._open_locks.add(self.filename)
276
318
 
277
319
            self.f = new_f
293
335
 
294
336
 
295
337
if have_pywin32 and sys.platform == 'win32':
296
 
    LOCK_SH = 0 # the default
297
 
    LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
298
 
    LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
299
 
 
 
338
    if os.path.supports_unicode_filenames:
 
339
        # for Windows NT/2K/XP/etc
 
340
        win32file_CreateFile = win32file.CreateFileW
 
341
    else:
 
342
        # for Windows 98
 
343
        win32file_CreateFile = win32file.CreateFile
300
344
 
301
345
    class _w32c_FileLock(_OSLock):
302
346
 
303
 
        def _lock(self, filename, openmode, lockmode):
304
 
            self._open(filename, openmode)
305
 
 
306
 
            self.hfile = msvcrt.get_osfhandle(self.f.fileno())
307
 
            overlapped = pywintypes.OVERLAPPED()
 
347
        def _open(self, filename, access, share, cflags, pymode):
 
348
            self.filename = osutils.realpath(filename)
308
349
            try:
309
 
                win32file.LockFileEx(self.hfile, lockmode, 0, 0x7fff0000,
310
 
                                     overlapped)
 
350
                self._handle = win32file_CreateFile(filename, access, share,
 
351
                    None, win32file.OPEN_ALWAYS,
 
352
                    win32file.FILE_ATTRIBUTE_NORMAL, None)
311
353
            except pywintypes.error, e:
312
 
                self._clear_f()
313
 
                if e.args[0] in (winerror.ERROR_LOCK_VIOLATION,):
314
 
                    raise errors.LockContention(filename)
315
 
                ## import pdb; pdb.set_trace()
 
354
                if e.args[0] == winerror.ERROR_ACCESS_DENIED:
 
355
                    raise errors.LockFailed(filename, e)
 
356
                if e.args[0] == winerror.ERROR_SHARING_VIOLATION:
 
357
                    raise errors.LockContention(filename, e)
316
358
                raise
317
 
            except Exception, e:
318
 
                self._clear_f()
319
 
                raise errors.LockContention(e)
 
359
            fd = win32file._open_osfhandle(self._handle, cflags)
 
360
            self.f = os.fdopen(fd, pymode)
 
361
            return self.f
320
362
 
321
363
        def unlock(self):
322
 
            overlapped = pywintypes.OVERLAPPED()
323
 
            try:
324
 
                win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
325
 
                self._clear_f()
326
 
            except Exception, e:
327
 
                raise errors.LockContention(e)
 
364
            self._clear_f()
 
365
            self._handle = None
328
366
 
329
367
 
330
368
    class _w32c_ReadLock(_w32c_FileLock):
331
369
        def __init__(self, filename):
332
370
            super(_w32c_ReadLock, self).__init__()
333
 
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
 
371
            self._open(filename, win32file.GENERIC_READ,
 
372
                win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
334
373
 
335
374
        def temporary_write_lock(self):
336
375
            """Try to grab a write lock on the file.
355
394
    class _w32c_WriteLock(_w32c_FileLock):
356
395
        def __init__(self, filename):
357
396
            super(_w32c_WriteLock, self).__init__()
358
 
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
 
397
            self._open(filename,
 
398
                win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
 
399
                os.O_RDWR, "rb+")
359
400
 
360
401
        def restore_read_lock(self):
361
402
            """Restore the original ReadLock."""
368
409
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
369
410
 
370
411
 
371
 
if have_ctypes and sys.platform == 'win32':
372
 
    # These constants were copied from the win32con.py module.
373
 
    LOCKFILE_FAIL_IMMEDIATELY = 1
374
 
    LOCKFILE_EXCLUSIVE_LOCK = 2
375
 
    # Constant taken from winerror.py module
376
 
    ERROR_LOCK_VIOLATION = 33
377
 
 
378
 
    LOCK_SH = 0
379
 
    LOCK_EX = LOCKFILE_EXCLUSIVE_LOCK
380
 
    LOCK_NB = LOCKFILE_FAIL_IMMEDIATELY
381
 
    _LockFileEx = ctypes.windll.kernel32.LockFileEx
382
 
    _UnlockFileEx = ctypes.windll.kernel32.UnlockFileEx
383
 
    _GetLastError = ctypes.windll.kernel32.GetLastError
384
 
 
385
 
    ### Define the OVERLAPPED structure.
386
 
    #   http://msdn2.microsoft.com/en-us/library/ms684342.aspx
387
 
    # typedef struct _OVERLAPPED {
388
 
    #   ULONG_PTR Internal;
389
 
    #   ULONG_PTR InternalHigh;
390
 
    #   union {
391
 
    #     struct {
392
 
    #       DWORD Offset;
393
 
    #       DWORD OffsetHigh;
394
 
    #     };
395
 
    #     PVOID Pointer;
396
 
    #   };
397
 
    #   HANDLE hEvent;
398
 
    # } OVERLAPPED,
399
 
 
400
 
    class _inner_struct(ctypes.Structure):
401
 
        _fields_ = [('Offset', ctypes.c_uint), # DWORD
402
 
                    ('OffsetHigh', ctypes.c_uint), # DWORD
403
 
                   ]
404
 
 
405
 
    class _inner_union(ctypes.Union):
406
 
        _fields_  = [('anon_struct', _inner_struct), # struct
407
 
                     ('Pointer', ctypes.c_void_p), # PVOID
408
 
                    ]
409
 
 
410
 
    class OVERLAPPED(ctypes.Structure):
411
 
        _fields_ = [('Internal', ctypes.c_void_p), # ULONG_PTR
412
 
                    ('InternalHigh', ctypes.c_void_p), # ULONG_PTR
413
 
                    ('_inner_union', _inner_union),
414
 
                    ('hEvent', ctypes.c_void_p), # HANDLE
415
 
                   ]
 
412
if have_ctypes_win32:
 
413
    from ctypes.wintypes import DWORD, LPCSTR, LPCWSTR
 
414
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p # used as NULL no need to declare
 
415
    HANDLE = ctypes.c_int # rather than unsigned as in ctypes.wintypes
 
416
    if os.path.supports_unicode_filenames:
 
417
        _function_name = "CreateFileW"
 
418
        LPTSTR = LPCWSTR
 
419
    else:
 
420
        _function_name = "CreateFileA"
 
421
        class LPTSTR(LPCSTR):
 
422
            def __new__(cls, obj):
 
423
                return LPCSTR.__new__(cls, obj.encode("mbcs"))
 
424
 
 
425
    # CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
 
426
    _CreateFile = ctypes.WINFUNCTYPE(
 
427
            HANDLE,                # return value
 
428
            LPTSTR,                # lpFileName
 
429
            DWORD,                 # dwDesiredAccess
 
430
            DWORD,                 # dwShareMode
 
431
            LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
 
432
            DWORD,                 # dwCreationDisposition
 
433
            DWORD,                 # dwFlagsAndAttributes
 
434
            HANDLE                 # hTemplateFile
 
435
        )((_function_name, ctypes.windll.kernel32))
 
436
 
 
437
    INVALID_HANDLE_VALUE = -1
 
438
 
 
439
    GENERIC_READ = 0x80000000
 
440
    GENERIC_WRITE = 0x40000000
 
441
    FILE_SHARE_READ = 1
 
442
    OPEN_ALWAYS = 4
 
443
    FILE_ATTRIBUTE_NORMAL = 128
 
444
 
 
445
    ERROR_ACCESS_DENIED = 5
 
446
    ERROR_SHARING_VIOLATION = 32
416
447
 
417
448
    class _ctypes_FileLock(_OSLock):
418
449
 
419
 
        def _lock(self, filename, openmode, lockmode):
420
 
            self._open(filename, openmode)
421
 
 
422
 
            self.hfile = msvcrt.get_osfhandle(self.f.fileno())
423
 
            overlapped = OVERLAPPED()
424
 
            result = _LockFileEx(self.hfile, # HANDLE hFile
425
 
                                 lockmode,   # DWORD dwFlags
426
 
                                 0,          # DWORD dwReserved
427
 
                                 0x7fffffff, # DWORD nNumberOfBytesToLockLow
428
 
                                 0x00000000, # DWORD nNumberOfBytesToLockHigh
429
 
                                 ctypes.byref(overlapped), # lpOverlapped
430
 
                                )
431
 
            if result == 0:
432
 
                self._clear_f()
433
 
                last_err = _GetLastError()
434
 
                if last_err in (ERROR_LOCK_VIOLATION,):
435
 
                    raise errors.LockContention(filename)
436
 
                raise errors.LockContention('Unknown locking error: %s'
437
 
                                            % (last_err,))
 
450
        def _open(self, filename, access, share, cflags, pymode):
 
451
            self.filename = osutils.realpath(filename)
 
452
            handle = _CreateFile(filename, access, share, None, OPEN_ALWAYS,
 
453
                FILE_ATTRIBUTE_NORMAL, 0)
 
454
            if handle in (INVALID_HANDLE_VALUE, 0):
 
455
                e = ctypes.WinError()
 
456
                if e.args[0] == ERROR_ACCESS_DENIED:
 
457
                    raise errors.LockFailed(filename, e)
 
458
                if e.args[0] == ERROR_SHARING_VIOLATION:
 
459
                    raise errors.LockContention(filename, e)
 
460
                raise e
 
461
            fd = msvcrt.open_osfhandle(handle, cflags)
 
462
            self.f = os.fdopen(fd, pymode)
 
463
            return self.f
438
464
 
439
465
        def unlock(self):
440
 
            overlapped = OVERLAPPED()
441
 
            result = _UnlockFileEx(self.hfile, # HANDLE hFile
442
 
                                   0,          # DWORD dwReserved
443
 
                                   0x7fffffff, # DWORD nNumberOfBytesToLockLow
444
 
                                   0x00000000, # DWORD nNumberOfBytesToLockHigh
445
 
                                   ctypes.byref(overlapped), # lpOverlapped
446
 
                                  )
447
466
            self._clear_f()
448
 
            if result == 0:
449
 
                self._clear_f()
450
 
                last_err = _GetLastError()
451
 
                raise errors.LockContention('Unknown unlocking error: %s'
452
 
                                            % (last_err,))
453
467
 
454
468
 
455
469
    class _ctypes_ReadLock(_ctypes_FileLock):
456
470
        def __init__(self, filename):
457
471
            super(_ctypes_ReadLock, self).__init__()
458
 
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
 
472
            self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
 
473
                "rb")
459
474
 
460
475
        def temporary_write_lock(self):
461
476
            """Try to grab a write lock on the file.
479
494
    class _ctypes_WriteLock(_ctypes_FileLock):
480
495
        def __init__(self, filename):
481
496
            super(_ctypes_WriteLock, self).__init__()
482
 
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
 
497
            self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
 
498
                "rb+")
483
499
 
484
500
        def restore_read_lock(self):
485
501
            """Restore the original ReadLock."""
502
518
# We default to using the first available lock class.
503
519
_lock_type, WriteLock, ReadLock = _lock_classes[0]
504
520
 
 
521
 
 
522
class _RelockDebugMixin(object):
 
523
    """Mixin support for -Drelock flag.
 
524
 
 
525
    Add this as a base class then call self._note_lock with 'r' or 'w' when
 
526
    acquiring a read- or write-lock.  If this object was previously locked (and
 
527
    locked the same way), and -Drelock is set, then this will trace.note a
 
528
    message about it.
 
529
    """
 
530
    
 
531
    _prev_lock = None
 
532
 
 
533
    def _note_lock(self, lock_type):
 
534
        if 'relock' in debug.debug_flags and self._prev_lock == lock_type:
 
535
            if lock_type == 'r':
 
536
                type_name = 'read'
 
537
            else:
 
538
                type_name = 'write'
 
539
            trace.note('%r was %s locked again', self, type_name)
 
540
        self._prev_lock = lock_type
 
541