42
40
from bzrlib import (
48
from bzrlib.hooks import HookPoint, Hooks
51
class LockHooks(Hooks):
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))
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
def cant_unlock_not_held(locked_object):
92
"""An attempt to unlock failed because the object was not locked.
94
This provides a policy point from which we can generate either a warning
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
101
if 'unlock' in debug.debug_flags:
102
warnings.warn("%r is already unlocked" % (locked_object,),
105
raise errors.LockNotHeld(locked_object)
115
have_ctypes_win32 = False
116
if sys.platform == 'win32':
119
import win32file, pywintypes, winerror
126
have_ctypes_win32 = True
131
47
class _OSLock(object):
337
253
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
343
win32file_CreateFile = win32file.CreateFile
254
LOCK_SH = 0 # the default
255
LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
256
LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
345
259
class _w32c_FileLock(_OSLock):
347
def _open(self, filename, access, share, cflags, pymode):
348
self.filename = osutils.realpath(filename)
261
def _lock(self, filename, openmode, lockmode):
262
self._open(filename, openmode)
264
self.hfile = msvcrt.get_osfhandle(self.f.fileno())
265
overlapped = pywintypes.OVERLAPPED()
350
self._handle = win32file_CreateFile(filename, access, share,
351
None, win32file.OPEN_ALWAYS,
352
win32file.FILE_ATTRIBUTE_NORMAL, None)
267
win32file.LockFileEx(self.hfile, lockmode, 0, 0x7fff0000,
353
269
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)
271
if e.args[0] in (winerror.ERROR_LOCK_VIOLATION,):
272
raise errors.LockContention(filename)
273
## import pdb; pdb.set_trace()
359
fd = win32file._open_osfhandle(self._handle, cflags)
360
self.f = os.fdopen(fd, pymode)
277
raise errors.LockContention(e)
363
279
def unlock(self):
280
overlapped = pywintypes.OVERLAPPED()
282
win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
285
raise errors.LockContention(e)
368
288
class _w32c_ReadLock(_w32c_FileLock):
369
289
def __init__(self, filename):
370
290
super(_w32c_ReadLock, self).__init__()
371
self._open(filename, win32file.GENERIC_READ,
372
win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
291
self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
374
293
def temporary_write_lock(self):
375
294
"""Try to grab a write lock on the file.
409
326
_lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
329
have_ctypes_win32 = False
330
if sys.platform == 'win32':
332
import ctypes, msvcrt
333
have_ctypes_win32 = True
412
337
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"
420
_function_name = "CreateFileA"
421
class LPTSTR(LPCSTR):
422
def __new__(cls, obj):
423
return LPCSTR.__new__(cls, obj.encode("mbcs"))
425
# CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
426
_CreateFile = ctypes.WINFUNCTYPE(
427
HANDLE, # return value
429
DWORD, # dwDesiredAccess
431
LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
432
DWORD, # dwCreationDisposition
433
DWORD, # dwFlagsAndAttributes
434
HANDLE # hTemplateFile
435
)((_function_name, ctypes.windll.kernel32))
437
INVALID_HANDLE_VALUE = -1
439
GENERIC_READ = 0x80000000
440
GENERIC_WRITE = 0x40000000
443
FILE_ATTRIBUTE_NORMAL = 128
445
ERROR_ACCESS_DENIED = 5
446
ERROR_SHARING_VIOLATION = 32
338
# These constants were copied from the win32con.py module.
339
LOCKFILE_FAIL_IMMEDIATELY = 1
340
LOCKFILE_EXCLUSIVE_LOCK = 2
341
# Constant taken from winerror.py module
342
ERROR_LOCK_VIOLATION = 33
345
LOCK_EX = LOCKFILE_EXCLUSIVE_LOCK
346
LOCK_NB = LOCKFILE_FAIL_IMMEDIATELY
347
_LockFileEx = ctypes.windll.kernel32.LockFileEx
348
_UnlockFileEx = ctypes.windll.kernel32.UnlockFileEx
349
_GetLastError = ctypes.windll.kernel32.GetLastError
351
### Define the OVERLAPPED structure.
352
# http://msdn2.microsoft.com/en-us/library/ms684342.aspx
353
# typedef struct _OVERLAPPED {
354
# ULONG_PTR Internal;
355
# ULONG_PTR InternalHigh;
366
class _inner_struct(ctypes.Structure):
367
_fields_ = [('Offset', ctypes.c_uint), # DWORD
368
('OffsetHigh', ctypes.c_uint), # DWORD
371
class _inner_union(ctypes.Union):
372
_fields_ = [('anon_struct', _inner_struct), # struct
373
('Pointer', ctypes.c_void_p), # PVOID
376
class OVERLAPPED(ctypes.Structure):
377
_fields_ = [('Internal', ctypes.c_void_p), # ULONG_PTR
378
('InternalHigh', ctypes.c_void_p), # ULONG_PTR
379
('_inner_union', _inner_union),
380
('hEvent', ctypes.c_void_p), # HANDLE
448
383
class _ctypes_FileLock(_OSLock):
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)
461
fd = msvcrt.open_osfhandle(handle, cflags)
462
self.f = os.fdopen(fd, pymode)
385
def _lock(self, filename, openmode, lockmode):
386
self._open(filename, openmode)
388
self.hfile = msvcrt.get_osfhandle(self.f.fileno())
389
overlapped = OVERLAPPED()
390
result = _LockFileEx(self.hfile, # HANDLE hFile
391
lockmode, # DWORD dwFlags
392
0, # DWORD dwReserved
393
0x7fffffff, # DWORD nNumberOfBytesToLockLow
394
0x00000000, # DWORD nNumberOfBytesToLockHigh
395
ctypes.byref(overlapped), # lpOverlapped
399
last_err = _GetLastError()
400
if last_err in (ERROR_LOCK_VIOLATION,):
401
raise errors.LockContention(filename)
402
raise errors.LockContention('Unknown locking error: %s'
465
405
def unlock(self):
406
overlapped = OVERLAPPED()
407
result = _UnlockFileEx(self.hfile, # HANDLE hFile
408
0, # DWORD dwReserved
409
0x7fffffff, # DWORD nNumberOfBytesToLockLow
410
0x00000000, # DWORD nNumberOfBytesToLockHigh
411
ctypes.byref(overlapped), # lpOverlapped
416
last_err = _GetLastError()
417
raise errors.LockContention('Unknown unlocking error: %s'
469
421
class _ctypes_ReadLock(_ctypes_FileLock):
470
422
def __init__(self, filename):
471
423
super(_ctypes_ReadLock, self).__init__()
472
self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
424
self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
475
426
def temporary_write_lock(self):
476
427
"""Try to grab a write lock on the file.