~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lock.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-06-25 17:36:29 UTC
  • mfrom: (4459.3.4 jam-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20090625173629-yw2y6x938qnr0hr4
(Martin <gzlist>) Use CreateFile rather than LockFileEx for OS locks.

Show diffs side-by-side

added added

removed removed

Lines of Context:
35
35
"""
36
36
 
37
37
import errno
 
38
import os
38
39
import sys
39
40
 
40
41
from bzrlib import (
96
97
if sys.platform == 'win32':
97
98
    import msvcrt
98
99
    try:
99
 
        import win32con, win32file, pywintypes, winerror
 
100
        import win32file, pywintypes, winerror
100
101
        have_pywin32 = True
101
102
    except ImportError:
102
103
        pass
151
152
 
152
153
 
153
154
if have_fcntl:
154
 
    LOCK_SH = fcntl.LOCK_SH
155
 
    LOCK_NB = fcntl.LOCK_NB
156
 
    lock_EX = fcntl.LOCK_EX
157
 
 
158
155
 
159
156
    class _fcntl_FileLock(_OSLock):
160
157
 
304
301
 
305
302
 
306
303
if have_pywin32 and sys.platform == 'win32':
307
 
    LOCK_SH = 0 # the default
308
 
    LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
309
 
    LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
310
 
 
311
304
 
312
305
    class _w32c_FileLock(_OSLock):
313
306
 
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()
 
307
        def _open(self, filename, access, share, cflags, pymode):
 
308
            self.filename = osutils.realpath(filename)
319
309
            try:
320
 
                win32file.LockFileEx(self.hfile, lockmode, 0, 0x7fff0000,
321
 
                                     overlapped)
 
310
                self._handle = win32file.CreateFile(filename, access, share,
 
311
                    None, win32file.OPEN_ALWAYS,
 
312
                    win32file.FILE_ATTRIBUTE_NORMAL, None)
322
313
            except pywintypes.error, 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()
 
314
                if e.args[0] == winerror.ERROR_ACCESS_DENIED:
 
315
                    raise errors.LockFailed(filename, e)
 
316
                if e.args[0] == winerror.ERROR_SHARING_VIOLATION:
 
317
                    raise errors.LockContention(filename, e)
327
318
                raise
328
 
            except Exception, e:
329
 
                self._clear_f()
330
 
                raise errors.LockContention(filename, e)
 
319
            fd = win32file._open_osfhandle(self._handle, cflags)
 
320
            self.f = os.fdopen(fd, pymode)
 
321
            return self.f
331
322
 
332
323
        def unlock(self):
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)
 
324
            self._clear_f()
 
325
            self._handle = None
339
326
 
340
327
 
341
328
    class _w32c_ReadLock(_w32c_FileLock):
342
329
        def __init__(self, filename):
343
330
            super(_w32c_ReadLock, self).__init__()
344
 
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
 
331
            self._open(filename, win32file.GENERIC_READ,
 
332
                win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
345
333
 
346
334
        def temporary_write_lock(self):
347
335
            """Try to grab a write lock on the file.
366
354
    class _w32c_WriteLock(_w32c_FileLock):
367
355
        def __init__(self, filename):
368
356
            super(_w32c_WriteLock, self).__init__()
369
 
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
 
357
            self._open(filename,
 
358
                win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
 
359
                os.O_RDWR, "rb+")
370
360
 
371
361
        def restore_read_lock(self):
372
362
            """Restore the original ReadLock."""
380
370
 
381
371
 
382
372
if have_ctypes_win32:
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
 
 
395
 
    ### Define the OVERLAPPED structure.
396
 
    #   http://msdn2.microsoft.com/en-us/library/ms684342.aspx
397
 
    # typedef struct _OVERLAPPED {
398
 
    #   ULONG_PTR Internal;
399
 
    #   ULONG_PTR InternalHigh;
400
 
    #   union {
401
 
    #     struct {
402
 
    #       DWORD Offset;
403
 
    #       DWORD OffsetHigh;
404
 
    #     };
405
 
    #     PVOID Pointer;
406
 
    #   };
407
 
    #   HANDLE hEvent;
408
 
    # } OVERLAPPED,
409
 
 
410
 
    class _inner_struct(ctypes.Structure):
411
 
        _fields_ = [('Offset', ctypes.c_uint), # DWORD
412
 
                    ('OffsetHigh', ctypes.c_uint), # DWORD
413
 
                   ]
414
 
 
415
 
    class _inner_union(ctypes.Union):
416
 
        _fields_  = [('anon_struct', _inner_struct), # struct
417
 
                     ('Pointer', ctypes.c_void_p), # PVOID
418
 
                    ]
419
 
 
420
 
    class OVERLAPPED(ctypes.Structure):
421
 
        _fields_ = [('Internal', ctypes.c_void_p), # ULONG_PTR
422
 
                    ('InternalHigh', ctypes.c_void_p), # ULONG_PTR
423
 
                    ('_inner_union', _inner_union),
424
 
                    ('hEvent', ctypes.c_void_p), # HANDLE
425
 
                   ]
 
373
    from ctypes.wintypes import DWORD, LPCSTR, LPCWSTR
 
374
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p # used as NULL no need to declare
 
375
    HANDLE = ctypes.c_int # rather than unsigned as in ctypes.wintypes
 
376
    if os.path.supports_unicode_filenames:
 
377
        _function_name = "CreateFileW"
 
378
        LPTSTR = LPCWSTR
 
379
    else:
 
380
        _function_name = "CreateFileA"
 
381
        class LPTSTR(LPCSTR):
 
382
            def __new__(cls, obj):
 
383
                return LPCSTR.__new__(cls, obj.encode("mbcs"))
 
384
 
 
385
    # CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
 
386
    _CreateFile = ctypes.WINFUNCTYPE(
 
387
            HANDLE,                # return value
 
388
            LPTSTR,                # lpFileName
 
389
            DWORD,                 # dwDesiredAccess
 
390
            DWORD,                 # dwShareMode
 
391
            LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
 
392
            DWORD,                 # dwCreationDisposition
 
393
            DWORD,                 # dwFlagsAndAttributes
 
394
            HANDLE                 # hTemplateFile
 
395
        )((_function_name, ctypes.windll.kernel32))
 
396
    
 
397
    INVALID_HANDLE_VALUE = -1
 
398
    
 
399
    GENERIC_READ = 0x80000000
 
400
    GENERIC_WRITE = 0x40000000
 
401
    FILE_SHARE_READ = 1
 
402
    OPEN_ALWAYS = 4
 
403
    FILE_ATTRIBUTE_NORMAL = 128
 
404
    
 
405
    ERROR_ACCESS_DENIED = 5
 
406
    ERROR_SHARING_VIOLATION = 32
426
407
 
427
408
    class _ctypes_FileLock(_OSLock):
428
409
 
429
 
        def _lock(self, filename, openmode, lockmode):
430
 
            self._open(filename, openmode)
431
 
 
432
 
            self.hfile = msvcrt.get_osfhandle(self.f.fileno())
433
 
            overlapped = OVERLAPPED()
434
 
            result = _LockFileEx(self.hfile, # HANDLE hFile
435
 
                                 lockmode,   # DWORD dwFlags
436
 
                                 0,          # DWORD dwReserved
437
 
                                 0x7fffffff, # DWORD nNumberOfBytesToLockLow
438
 
                                 0x00000000, # DWORD nNumberOfBytesToLockHigh
439
 
                                 ctypes.byref(overlapped), # lpOverlapped
440
 
                                )
441
 
            if result == 0:
442
 
                last_err = ctypes.GetLastError()
443
 
                self._clear_f()
444
 
                if last_err in (ERROR_LOCK_VIOLATION,):
445
 
                    raise errors.LockContention(filename)
446
 
                raise errors.LockContention(filename,
447
 
                    'Unknown locking error: %s' % (last_err,))
 
410
        def _open(self, filename, access, share, cflags, pymode):
 
411
            self.filename = osutils.realpath(filename)
 
412
            handle = _CreateFile(filename, access, share, None, OPEN_ALWAYS,
 
413
                FILE_ATTRIBUTE_NORMAL, 0)
 
414
            if handle in (INVALID_HANDLE_VALUE, 0):
 
415
                e = ctypes.WinError()
 
416
                if e.args[0] == ERROR_ACCESS_DENIED:
 
417
                    raise errors.LockFailed(filename, e)
 
418
                if e.args[0] == ERROR_SHARING_VIOLATION:
 
419
                    raise errors.LockContention(filename, e)
 
420
                raise e
 
421
            fd = msvcrt.open_osfhandle(handle, cflags)
 
422
            self.f = os.fdopen(fd, pymode)
 
423
            return self.f
448
424
 
449
425
        def unlock(self):
450
 
            overlapped = OVERLAPPED()
451
 
            result = _UnlockFileEx(self.hfile, # HANDLE hFile
452
 
                                   0,          # DWORD dwReserved
453
 
                                   0x7fffffff, # DWORD nNumberOfBytesToLockLow
454
 
                                   0x00000000, # DWORD nNumberOfBytesToLockHigh
455
 
                                   ctypes.byref(overlapped), # lpOverlapped
456
 
                                  )
457
 
            if result == 0:
458
 
                last_err = ctypes.GetLastError()
459
 
                self._clear_f()
460
 
                raise errors.LockContention(self.filename,
461
 
                    'Unknown unlocking error: %s' % (last_err,))
462
426
            self._clear_f()
463
427
 
464
428
 
465
429
    class _ctypes_ReadLock(_ctypes_FileLock):
466
430
        def __init__(self, filename):
467
431
            super(_ctypes_ReadLock, self).__init__()
468
 
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
 
432
            self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
 
433
                "rb")
469
434
 
470
435
        def temporary_write_lock(self):
471
436
            """Try to grab a write lock on the file.
489
454
    class _ctypes_WriteLock(_ctypes_FileLock):
490
455
        def __init__(self, filename):
491
456
            super(_ctypes_WriteLock, self).__init__()
492
 
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
 
457
            self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
 
458
                "rb+")
493
459
 
494
460
        def restore_read_lock(self):
495
461
            """Restore the original ReadLock."""