~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lock.py

  • Committer: Matt Nordhoff
  • Date: 2009-06-23 05:12:07 UTC
  • mto: This revision was merged to the branch mainline in revision 4474.
  • Revision ID: mnordhoff@mattnordhoff.com-20090623051207-fksdtbzkwtnrw9dd
Update _add_text docstrings that still referred to add_text.

Show diffs side-by-side

added added

removed removed

Lines of Context:
35
35
"""
36
36
 
37
37
import errno
38
 
import os
39
38
import sys
40
 
import warnings
41
39
 
42
40
from bzrlib import (
43
 
    debug,
44
41
    errors,
45
42
    osutils,
46
43
    trace,
88
85
                             self.lock_url, self.details)
89
86
 
90
87
 
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
88
try:
109
89
    import fcntl
110
90
    have_fcntl = True
116
96
if sys.platform == 'win32':
117
97
    import msvcrt
118
98
    try:
119
 
        import win32file, pywintypes, winerror
 
99
        import win32con, win32file, pywintypes, winerror
120
100
        have_pywin32 = True
121
101
    except ImportError:
122
102
        pass
171
151
 
172
152
 
173
153
if have_fcntl:
 
154
    LOCK_SH = fcntl.LOCK_SH
 
155
    LOCK_NB = fcntl.LOCK_NB
 
156
    lock_EX = fcntl.LOCK_EX
 
157
 
174
158
 
175
159
    class _fcntl_FileLock(_OSLock):
176
160
 
190
174
            if self.filename in _fcntl_WriteLock._open_locks:
191
175
                self._clear_f()
192
176
                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,))
200
177
 
201
178
            self._open(self.filename, 'rb+')
202
179
            # reserve a slot for this lock - even if the lockf call fails,
227
204
        def __init__(self, filename):
228
205
            super(_fcntl_ReadLock, self).__init__()
229
206
            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,))
238
207
            _fcntl_ReadLock._open_locks.setdefault(self.filename, 0)
239
208
            _fcntl_ReadLock._open_locks[self.filename] += 1
240
209
            self._open(filename, 'rb')
335
304
 
336
305
 
337
306
if have_pywin32 and sys.platform == 'win32':
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
 
307
    LOCK_SH = 0 # the default
 
308
    LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
 
309
    LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
 
310
 
344
311
 
345
312
    class _w32c_FileLock(_OSLock):
346
313
 
347
 
        def _open(self, filename, access, share, cflags, pymode):
348
 
            self.filename = osutils.realpath(filename)
 
314
        def _lock(self, filename, openmode, lockmode):
 
315
            self._open(filename, openmode)
 
316
 
 
317
            self.hfile = msvcrt.get_osfhandle(self.f.fileno())
 
318
            overlapped = pywintypes.OVERLAPPED()
349
319
            try:
350
 
                self._handle = win32file_CreateFile(filename, access, share,
351
 
                    None, win32file.OPEN_ALWAYS,
352
 
                    win32file.FILE_ATTRIBUTE_NORMAL, None)
 
320
                win32file.LockFileEx(self.hfile, lockmode, 0, 0x7fff0000,
 
321
                                     overlapped)
353
322
            except pywintypes.error, e:
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)
 
323
                self._clear_f()
 
324
                if e.args[0] in (winerror.ERROR_LOCK_VIOLATION,):
 
325
                    raise errors.LockContention(filename)
 
326
                ## import pdb; pdb.set_trace()
358
327
                raise
359
 
            fd = win32file._open_osfhandle(self._handle, cflags)
360
 
            self.f = os.fdopen(fd, pymode)
361
 
            return self.f
 
328
            except Exception, e:
 
329
                self._clear_f()
 
330
                raise errors.LockContention(filename, e)
362
331
 
363
332
        def unlock(self):
364
 
            self._clear_f()
365
 
            self._handle = None
 
333
            overlapped = pywintypes.OVERLAPPED()
 
334
            try:
 
335
                win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
 
336
                self._clear_f()
 
337
            except Exception, e:
 
338
                raise errors.LockContention(self.filename, e)
366
339
 
367
340
 
368
341
    class _w32c_ReadLock(_w32c_FileLock):
369
342
        def __init__(self, filename):
370
343
            super(_w32c_ReadLock, self).__init__()
371
 
            self._open(filename, win32file.GENERIC_READ,
372
 
                win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
 
344
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
373
345
 
374
346
        def temporary_write_lock(self):
375
347
            """Try to grab a write lock on the file.
394
366
    class _w32c_WriteLock(_w32c_FileLock):
395
367
        def __init__(self, filename):
396
368
            super(_w32c_WriteLock, self).__init__()
397
 
            self._open(filename,
398
 
                win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
399
 
                os.O_RDWR, "rb+")
 
369
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
400
370
 
401
371
        def restore_read_lock(self):
402
372
            """Restore the original ReadLock."""
410
380
 
411
381
 
412
382
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
 
383
    # These constants were copied from the win32con.py module.
 
384
    LOCKFILE_FAIL_IMMEDIATELY = 1
 
385
    LOCKFILE_EXCLUSIVE_LOCK = 2
 
386
    # Constant taken from winerror.py module
 
387
    ERROR_LOCK_VIOLATION = 33
 
388
 
 
389
    LOCK_SH = 0
 
390
    LOCK_EX = LOCKFILE_EXCLUSIVE_LOCK
 
391
    LOCK_NB = LOCKFILE_FAIL_IMMEDIATELY
 
392
    _LockFileEx = ctypes.windll.kernel32.LockFileEx
 
393
    _UnlockFileEx = ctypes.windll.kernel32.UnlockFileEx
 
394
    _GetLastError = ctypes.windll.kernel32.GetLastError
 
395
 
 
396
    ### Define the OVERLAPPED structure.
 
397
    #   http://msdn2.microsoft.com/en-us/library/ms684342.aspx
 
398
    # typedef struct _OVERLAPPED {
 
399
    #   ULONG_PTR Internal;
 
400
    #   ULONG_PTR InternalHigh;
 
401
    #   union {
 
402
    #     struct {
 
403
    #       DWORD Offset;
 
404
    #       DWORD OffsetHigh;
 
405
    #     };
 
406
    #     PVOID Pointer;
 
407
    #   };
 
408
    #   HANDLE hEvent;
 
409
    # } OVERLAPPED,
 
410
 
 
411
    class _inner_struct(ctypes.Structure):
 
412
        _fields_ = [('Offset', ctypes.c_uint), # DWORD
 
413
                    ('OffsetHigh', ctypes.c_uint), # DWORD
 
414
                   ]
 
415
 
 
416
    class _inner_union(ctypes.Union):
 
417
        _fields_  = [('anon_struct', _inner_struct), # struct
 
418
                     ('Pointer', ctypes.c_void_p), # PVOID
 
419
                    ]
 
420
 
 
421
    class OVERLAPPED(ctypes.Structure):
 
422
        _fields_ = [('Internal', ctypes.c_void_p), # ULONG_PTR
 
423
                    ('InternalHigh', ctypes.c_void_p), # ULONG_PTR
 
424
                    ('_inner_union', _inner_union),
 
425
                    ('hEvent', ctypes.c_void_p), # HANDLE
 
426
                   ]
447
427
 
448
428
    class _ctypes_FileLock(_OSLock):
449
429
 
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
 
430
        def _lock(self, filename, openmode, lockmode):
 
431
            self._open(filename, openmode)
 
432
 
 
433
            self.hfile = msvcrt.get_osfhandle(self.f.fileno())
 
434
            overlapped = OVERLAPPED()
 
435
            result = _LockFileEx(self.hfile, # HANDLE hFile
 
436
                                 lockmode,   # DWORD dwFlags
 
437
                                 0,          # DWORD dwReserved
 
438
                                 0x7fffffff, # DWORD nNumberOfBytesToLockLow
 
439
                                 0x00000000, # DWORD nNumberOfBytesToLockHigh
 
440
                                 ctypes.byref(overlapped), # lpOverlapped
 
441
                                )
 
442
            if result == 0:
 
443
                self._clear_f()
 
444
                last_err = _GetLastError()
 
445
                if last_err in (ERROR_LOCK_VIOLATION,):
 
446
                    raise errors.LockContention(filename)
 
447
                raise errors.LockContention(filename,
 
448
                    'Unknown locking error: %s' % (last_err,))
464
449
 
465
450
        def unlock(self):
 
451
            overlapped = OVERLAPPED()
 
452
            result = _UnlockFileEx(self.hfile, # HANDLE hFile
 
453
                                   0,          # DWORD dwReserved
 
454
                                   0x7fffffff, # DWORD nNumberOfBytesToLockLow
 
455
                                   0x00000000, # DWORD nNumberOfBytesToLockHigh
 
456
                                   ctypes.byref(overlapped), # lpOverlapped
 
457
                                  )
466
458
            self._clear_f()
 
459
            if result == 0:
 
460
                self._clear_f()
 
461
                last_err = _GetLastError()
 
462
                raise errors.LockContention(self.filename,
 
463
                    'Unknown unlocking error: %s' % (last_err,))
467
464
 
468
465
 
469
466
    class _ctypes_ReadLock(_ctypes_FileLock):
470
467
        def __init__(self, filename):
471
468
            super(_ctypes_ReadLock, self).__init__()
472
 
            self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
473
 
                "rb")
 
469
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
474
470
 
475
471
        def temporary_write_lock(self):
476
472
            """Try to grab a write lock on the file.
494
490
    class _ctypes_WriteLock(_ctypes_FileLock):
495
491
        def __init__(self, filename):
496
492
            super(_ctypes_WriteLock, self).__init__()
497
 
            self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
498
 
                "rb+")
 
493
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
499
494
 
500
495
        def restore_read_lock(self):
501
496
            """Restore the original ReadLock."""