42
40
from bzrlib import (
48
from bzrlib.hooks import Hooks
51
class LockHooks(Hooks):
54
Hooks.__init__(self, "bzrlib.lock", "Lock.hooks")
55
self.add_hook('lock_acquired',
56
"Called with a bzrlib.lock.LockResult when a physical lock is "
58
self.add_hook('lock_released',
59
"Called with a bzrlib.lock.LockResult when a physical lock is "
61
self.add_hook('lock_broken',
62
"Called with a bzrlib.lock.LockResult when a physical lock is "
67
"""Base class for locks.
69
:cvar hooks: Hook dictionary for operations on locks.
75
class LockResult(object):
76
"""Result of an operation on a lock; passed to a hook"""
78
def __init__(self, lock_url, details=None):
79
"""Create a lock result for lock with optional details about the lock."""
80
self.lock_url = lock_url
81
self.details = details
83
def __eq__(self, other):
84
return self.lock_url == other.lock_url and self.details == other.details
87
return '%s(%s, %s)' % (self.__class__.__name__,
88
self.lock_url, self.details)
91
class LogicalLockResult(object):
92
"""The result of a lock_read/lock_write/lock_tree_write call on lockables.
94
:ivar unlock: A callable which will unlock the lock.
97
def __init__(self, unlock):
101
return "LogicalLockResult(%s)" % (self.unlock)
105
def cant_unlock_not_held(locked_object):
106
"""An attempt to unlock failed because the object was not locked.
108
This provides a policy point from which we can generate either a warning
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
115
if 'unlock' in debug.debug_flags:
116
warnings.warn("%r is already unlocked" % (locked_object,),
119
raise errors.LockNotHeld(locked_object)
129
have_ctypes_win32 = False
130
if sys.platform == 'win32':
133
import win32file, pywintypes, winerror
140
have_ctypes_win32 = True
145
47
class _OSLock(object):
345
259
if have_pywin32 and sys.platform == 'win32':
346
if os.path.supports_unicode_filenames:
347
# for Windows NT/2K/XP/etc
348
win32file_CreateFile = win32file.CreateFileW
351
win32file_CreateFile = win32file.CreateFile
260
LOCK_SH = 0 # the default
261
LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
262
LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
353
265
class _w32c_FileLock(_OSLock):
355
def _open(self, filename, access, share, cflags, pymode):
356
self.filename = osutils.realpath(filename)
267
def _lock(self, filename, openmode, lockmode):
268
self._open(filename, openmode)
270
self.hfile = msvcrt.get_osfhandle(self.f.fileno())
271
overlapped = pywintypes.OVERLAPPED()
358
self._handle = win32file_CreateFile(filename, access, share,
359
None, win32file.OPEN_ALWAYS,
360
win32file.FILE_ATTRIBUTE_NORMAL, None)
273
win32file.LockFileEx(self.hfile, lockmode, 0, 0x7fff0000,
361
275
except pywintypes.error, e:
362
if e.args[0] == winerror.ERROR_ACCESS_DENIED:
363
raise errors.LockFailed(filename, e)
364
if e.args[0] == winerror.ERROR_SHARING_VIOLATION:
365
raise errors.LockContention(filename, e)
277
if e.args[0] in (winerror.ERROR_LOCK_VIOLATION,):
278
raise errors.LockContention(filename)
279
## import pdb; pdb.set_trace()
367
fd = win32file._open_osfhandle(self._handle, cflags)
368
self.f = os.fdopen(fd, pymode)
283
raise errors.LockContention(e)
371
285
def unlock(self):
286
overlapped = pywintypes.OVERLAPPED()
288
win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
291
raise errors.LockContention(e)
376
294
class _w32c_ReadLock(_w32c_FileLock):
377
295
def __init__(self, filename):
378
296
super(_w32c_ReadLock, self).__init__()
379
self._open(filename, win32file.GENERIC_READ,
380
win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
297
self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
382
299
def temporary_write_lock(self):
383
300
"""Try to grab a write lock on the file.
417
332
_lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
420
if have_ctypes_win32:
421
from ctypes.wintypes import DWORD, LPCSTR, LPCWSTR
422
LPSECURITY_ATTRIBUTES = ctypes.c_void_p # used as NULL no need to declare
423
HANDLE = ctypes.c_int # rather than unsigned as in ctypes.wintypes
424
if os.path.supports_unicode_filenames:
425
_function_name = "CreateFileW"
428
_function_name = "CreateFileA"
429
class LPTSTR(LPCSTR):
430
def __new__(cls, obj):
431
return LPCSTR.__new__(cls, obj.encode("mbcs"))
433
# CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
434
_CreateFile = ctypes.WINFUNCTYPE(
435
HANDLE, # return value
437
DWORD, # dwDesiredAccess
439
LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
440
DWORD, # dwCreationDisposition
441
DWORD, # dwFlagsAndAttributes
442
HANDLE # hTemplateFile
443
)((_function_name, ctypes.windll.kernel32))
445
INVALID_HANDLE_VALUE = -1
447
GENERIC_READ = 0x80000000
448
GENERIC_WRITE = 0x40000000
451
FILE_ATTRIBUTE_NORMAL = 128
453
ERROR_ACCESS_DENIED = 5
454
ERROR_SHARING_VIOLATION = 32
335
if have_ctypes and sys.platform == 'win32':
336
# These constants were copied from the win32con.py module.
337
LOCKFILE_FAIL_IMMEDIATELY = 1
338
LOCKFILE_EXCLUSIVE_LOCK = 2
339
# Constant taken from winerror.py module
340
ERROR_LOCK_VIOLATION = 33
343
LOCK_EX = LOCKFILE_EXCLUSIVE_LOCK
344
LOCK_NB = LOCKFILE_FAIL_IMMEDIATELY
345
_LockFileEx = ctypes.windll.kernel32.LockFileEx
346
_UnlockFileEx = ctypes.windll.kernel32.UnlockFileEx
347
_GetLastError = ctypes.windll.kernel32.GetLastError
349
### Define the OVERLAPPED structure.
350
# http://msdn2.microsoft.com/en-us/library/ms684342.aspx
351
# typedef struct _OVERLAPPED {
352
# ULONG_PTR Internal;
353
# ULONG_PTR InternalHigh;
364
class _inner_struct(ctypes.Structure):
365
_fields_ = [('Offset', ctypes.c_uint), # DWORD
366
('OffsetHigh', ctypes.c_uint), # DWORD
369
class _inner_union(ctypes.Union):
370
_fields_ = [('anon_struct', _inner_struct), # struct
371
('Pointer', ctypes.c_void_p), # PVOID
374
class OVERLAPPED(ctypes.Structure):
375
_fields_ = [('Internal', ctypes.c_void_p), # ULONG_PTR
376
('InternalHigh', ctypes.c_void_p), # ULONG_PTR
377
('_inner_union', _inner_union),
378
('hEvent', ctypes.c_void_p), # HANDLE
456
381
class _ctypes_FileLock(_OSLock):
458
def _open(self, filename, access, share, cflags, pymode):
459
self.filename = osutils.realpath(filename)
460
handle = _CreateFile(filename, access, share, None, OPEN_ALWAYS,
461
FILE_ATTRIBUTE_NORMAL, 0)
462
if handle in (INVALID_HANDLE_VALUE, 0):
463
e = ctypes.WinError()
464
if e.args[0] == ERROR_ACCESS_DENIED:
465
raise errors.LockFailed(filename, e)
466
if e.args[0] == ERROR_SHARING_VIOLATION:
467
raise errors.LockContention(filename, e)
469
fd = msvcrt.open_osfhandle(handle, cflags)
470
self.f = os.fdopen(fd, pymode)
383
def _lock(self, filename, openmode, lockmode):
384
self._open(filename, openmode)
386
self.hfile = msvcrt.get_osfhandle(self.f.fileno())
387
overlapped = OVERLAPPED()
388
result = _LockFileEx(self.hfile, # HANDLE hFile
389
lockmode, # DWORD dwFlags
390
0, # DWORD dwReserved
391
0x7fffffff, # DWORD nNumberOfBytesToLockLow
392
0x00000000, # DWORD nNumberOfBytesToLockHigh
393
ctypes.byref(overlapped), # lpOverlapped
397
last_err = _GetLastError()
398
if last_err in (ERROR_LOCK_VIOLATION,):
399
raise errors.LockContention(filename)
400
raise errors.LockContention('Unknown locking error: %s'
473
403
def unlock(self):
404
overlapped = OVERLAPPED()
405
result = _UnlockFileEx(self.hfile, # HANDLE hFile
406
0, # DWORD dwReserved
407
0x7fffffff, # DWORD nNumberOfBytesToLockLow
408
0x00000000, # DWORD nNumberOfBytesToLockHigh
409
ctypes.byref(overlapped), # lpOverlapped
414
last_err = _GetLastError()
415
raise errors.LockContention('Unknown unlocking error: %s'
477
419
class _ctypes_ReadLock(_ctypes_FileLock):
478
420
def __init__(self, filename):
479
421
super(_ctypes_ReadLock, self).__init__()
480
self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
422
self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
483
424
def temporary_write_lock(self):
484
425
"""Try to grab a write lock on the file.