/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: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

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
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,
55
58
        self.create_hook(HookPoint('lock_released',
56
59
            "Called with a bzrlib.lock.LockResult when a physical lock is "
57
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))
58
64
 
59
65
 
60
66
class Lock(object):
77
83
    def __eq__(self, other):
78
84
        return self.lock_url == other.lock_url and self.details == other.details
79
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
 
80
107
 
81
108
try:
82
109
    import fcntl
89
116
if sys.platform == 'win32':
90
117
    import msvcrt
91
118
    try:
92
 
        import win32con, win32file, pywintypes, winerror
 
119
        import win32file, pywintypes, winerror
93
120
        have_pywin32 = True
94
121
    except ImportError:
95
122
        pass
144
171
 
145
172
 
146
173
if have_fcntl:
147
 
    LOCK_SH = fcntl.LOCK_SH
148
 
    LOCK_NB = fcntl.LOCK_NB
149
 
    lock_EX = fcntl.LOCK_EX
150
 
 
151
174
 
152
175
    class _fcntl_FileLock(_OSLock):
153
176
 
167
190
            if self.filename in _fcntl_WriteLock._open_locks:
168
191
                self._clear_f()
169
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,))
170
200
 
171
201
            self._open(self.filename, 'rb+')
172
202
            # reserve a slot for this lock - even if the lockf call fails,
173
 
            # 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.
174
204
            # TODO: make this fully threadsafe, if we decide we care.
175
205
            _fcntl_WriteLock._open_locks.add(self.filename)
176
206
            try:
197
227
        def __init__(self, filename):
198
228
            super(_fcntl_ReadLock, self).__init__()
199
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,))
200
238
            _fcntl_ReadLock._open_locks.setdefault(self.filename, 0)
201
239
            _fcntl_ReadLock._open_locks[self.filename] += 1
202
240
            self._open(filename, 'rb')
297
335
 
298
336
 
299
337
if have_pywin32 and sys.platform == 'win32':
300
 
    LOCK_SH = 0 # the default
301
 
    LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
302
 
    LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
303
 
 
 
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
304
344
 
305
345
    class _w32c_FileLock(_OSLock):
306
346
 
307
 
        def _lock(self, filename, openmode, lockmode):
308
 
            self._open(filename, openmode)
309
 
 
310
 
            self.hfile = msvcrt.get_osfhandle(self.f.fileno())
311
 
            overlapped = pywintypes.OVERLAPPED()
 
347
        def _open(self, filename, access, share, cflags, pymode):
 
348
            self.filename = osutils.realpath(filename)
312
349
            try:
313
 
                win32file.LockFileEx(self.hfile, lockmode, 0, 0x7fff0000,
314
 
                                     overlapped)
 
350
                self._handle = win32file_CreateFile(filename, access, share,
 
351
                    None, win32file.OPEN_ALWAYS,
 
352
                    win32file.FILE_ATTRIBUTE_NORMAL, None)
315
353
            except pywintypes.error, e:
316
 
                self._clear_f()
317
 
                if e.args[0] in (winerror.ERROR_LOCK_VIOLATION,):
318
 
                    raise errors.LockContention(filename)
319
 
                ## 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)
320
358
                raise
321
 
            except Exception, e:
322
 
                self._clear_f()
323
 
                raise errors.LockContention(filename, e)
 
359
            fd = win32file._open_osfhandle(self._handle, cflags)
 
360
            self.f = os.fdopen(fd, pymode)
 
361
            return self.f
324
362
 
325
363
        def unlock(self):
326
 
            overlapped = pywintypes.OVERLAPPED()
327
 
            try:
328
 
                win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
329
 
                self._clear_f()
330
 
            except Exception, e:
331
 
                raise errors.LockContention(self.filename, e)
 
364
            self._clear_f()
 
365
            self._handle = None
332
366
 
333
367
 
334
368
    class _w32c_ReadLock(_w32c_FileLock):
335
369
        def __init__(self, filename):
336
370
            super(_w32c_ReadLock, self).__init__()
337
 
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
 
371
            self._open(filename, win32file.GENERIC_READ,
 
372
                win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
338
373
 
339
374
        def temporary_write_lock(self):
340
375
            """Try to grab a write lock on the file.
359
394
    class _w32c_WriteLock(_w32c_FileLock):
360
395
        def __init__(self, filename):
361
396
            super(_w32c_WriteLock, self).__init__()
362
 
            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+")
363
400
 
364
401
        def restore_read_lock(self):
365
402
            """Restore the original ReadLock."""
373
410
 
374
411
 
375
412
if have_ctypes_win32:
376
 
    # These constants were copied from the win32con.py module.
377
 
    LOCKFILE_FAIL_IMMEDIATELY = 1
378
 
    LOCKFILE_EXCLUSIVE_LOCK = 2
379
 
    # Constant taken from winerror.py module
380
 
    ERROR_LOCK_VIOLATION = 33
381
 
 
382
 
    LOCK_SH = 0
383
 
    LOCK_EX = LOCKFILE_EXCLUSIVE_LOCK
384
 
    LOCK_NB = LOCKFILE_FAIL_IMMEDIATELY
385
 
    _LockFileEx = ctypes.windll.kernel32.LockFileEx
386
 
    _UnlockFileEx = ctypes.windll.kernel32.UnlockFileEx
387
 
    _GetLastError = ctypes.windll.kernel32.GetLastError
388
 
 
389
 
    ### Define the OVERLAPPED structure.
390
 
    #   http://msdn2.microsoft.com/en-us/library/ms684342.aspx
391
 
    # typedef struct _OVERLAPPED {
392
 
    #   ULONG_PTR Internal;
393
 
    #   ULONG_PTR InternalHigh;
394
 
    #   union {
395
 
    #     struct {
396
 
    #       DWORD Offset;
397
 
    #       DWORD OffsetHigh;
398
 
    #     };
399
 
    #     PVOID Pointer;
400
 
    #   };
401
 
    #   HANDLE hEvent;
402
 
    # } OVERLAPPED,
403
 
 
404
 
    class _inner_struct(ctypes.Structure):
405
 
        _fields_ = [('Offset', ctypes.c_uint), # DWORD
406
 
                    ('OffsetHigh', ctypes.c_uint), # DWORD
407
 
                   ]
408
 
 
409
 
    class _inner_union(ctypes.Union):
410
 
        _fields_  = [('anon_struct', _inner_struct), # struct
411
 
                     ('Pointer', ctypes.c_void_p), # PVOID
412
 
                    ]
413
 
 
414
 
    class OVERLAPPED(ctypes.Structure):
415
 
        _fields_ = [('Internal', ctypes.c_void_p), # ULONG_PTR
416
 
                    ('InternalHigh', ctypes.c_void_p), # ULONG_PTR
417
 
                    ('_inner_union', _inner_union),
418
 
                    ('hEvent', ctypes.c_void_p), # HANDLE
419
 
                   ]
 
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
420
447
 
421
448
    class _ctypes_FileLock(_OSLock):
422
449
 
423
 
        def _lock(self, filename, openmode, lockmode):
424
 
            self._open(filename, openmode)
425
 
 
426
 
            self.hfile = msvcrt.get_osfhandle(self.f.fileno())
427
 
            overlapped = OVERLAPPED()
428
 
            result = _LockFileEx(self.hfile, # HANDLE hFile
429
 
                                 lockmode,   # DWORD dwFlags
430
 
                                 0,          # DWORD dwReserved
431
 
                                 0x7fffffff, # DWORD nNumberOfBytesToLockLow
432
 
                                 0x00000000, # DWORD nNumberOfBytesToLockHigh
433
 
                                 ctypes.byref(overlapped), # lpOverlapped
434
 
                                )
435
 
            if result == 0:
436
 
                self._clear_f()
437
 
                last_err = _GetLastError()
438
 
                if last_err in (ERROR_LOCK_VIOLATION,):
439
 
                    raise errors.LockContention(filename)
440
 
                raise errors.LockContention(filename,
441
 
                    'Unknown locking error: %s' % (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
442
464
 
443
465
        def unlock(self):
444
 
            overlapped = OVERLAPPED()
445
 
            result = _UnlockFileEx(self.hfile, # HANDLE hFile
446
 
                                   0,          # DWORD dwReserved
447
 
                                   0x7fffffff, # DWORD nNumberOfBytesToLockLow
448
 
                                   0x00000000, # DWORD nNumberOfBytesToLockHigh
449
 
                                   ctypes.byref(overlapped), # lpOverlapped
450
 
                                  )
451
466
            self._clear_f()
452
 
            if result == 0:
453
 
                self._clear_f()
454
 
                last_err = _GetLastError()
455
 
                raise errors.LockContention(self.filename,
456
 
                    'Unknown unlocking error: %s' % (last_err,))
457
467
 
458
468
 
459
469
    class _ctypes_ReadLock(_ctypes_FileLock):
460
470
        def __init__(self, filename):
461
471
            super(_ctypes_ReadLock, self).__init__()
462
 
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
 
472
            self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
 
473
                "rb")
463
474
 
464
475
        def temporary_write_lock(self):
465
476
            """Try to grab a write lock on the file.
483
494
    class _ctypes_WriteLock(_ctypes_FileLock):
484
495
        def __init__(self, filename):
485
496
            super(_ctypes_WriteLock, self).__init__()
486
 
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
 
497
            self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
 
498
                "rb+")
487
499
 
488
500
        def restore_read_lock(self):
489
501
            """Restore the original ReadLock."""
506
518
# We default to using the first available lock class.
507
519
_lock_type, WriteLock, ReadLock = _lock_classes[0]
508
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