48
from .hooks import Hooks
49
from .i18n import gettext
48
from bzrlib.hooks import HookPoint, Hooks
52
51
class LockHooks(Hooks):
54
53
def __init__(self):
55
Hooks.__init__(self, "breezy.lock", "Lock.hooks")
58
"Called with a breezy.lock.LockResult when a physical lock is "
62
"Called with a breezy.lock.LockResult when a physical lock is "
66
"Called with a breezy.lock.LockResult when a physical lock is "
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))
70
66
class Lock(object):
88
84
return self.lock_url == other.lock_url and self.details == other.details
90
86
def __repr__(self):
91
return '%s(%s, %s)' % (self.__class__.__name__,
92
self.lock_url, self.details)
95
class LogicalLockResult(object):
96
"""The result of a lock_read/lock_write/lock_tree_write call on lockables.
98
:ivar unlock: A callable which will unlock the lock.
101
def __init__(self, unlock, token=None):
106
return "LogicalLockResult(%s)" % (self.unlock)
111
def __exit__(self, exc_type, exc_val, exc_tb):
112
# If there was an error raised, prefer the original one
115
except BaseException:
87
return '%s(%s%s)' % (self.__class__.__name__,
88
self.lock_url, self.details)
121
91
def cant_unlock_not_held(locked_object):
122
92
"""An attempt to unlock failed because the object was not locked.
124
This provides a policy point from which we can generate either a warning or
94
This provides a policy point from which we can generate either a warning
127
97
# This is typically masking some other error and called from a finally
128
98
# block, so it's useful to have the option not to generate a new error
344
330
self._read_lock = None
347
334
_lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
337
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
345
class _w32c_FileLock(_OSLock):
347
def _open(self, filename, access, share, cflags, pymode):
348
self.filename = osutils.realpath(filename)
350
self._handle = win32file_CreateFile(filename, access, share,
351
None, win32file.OPEN_ALWAYS,
352
win32file.FILE_ATTRIBUTE_NORMAL, None)
353
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)
359
fd = win32file._open_osfhandle(self._handle, cflags)
360
self.f = os.fdopen(fd, pymode)
368
class _w32c_ReadLock(_w32c_FileLock):
369
def __init__(self, filename):
370
super(_w32c_ReadLock, self).__init__()
371
self._open(filename, win32file.GENERIC_READ,
372
win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
374
def temporary_write_lock(self):
375
"""Try to grab a write lock on the file.
377
On platforms that support it, this will upgrade to a write lock
378
without unlocking the file.
379
Otherwise, this will release the read lock, and try to acquire a
382
:return: A token which can be used to switch back to a read lock.
384
# I can't find a way to upgrade a read lock to a write lock without
385
# unlocking first. So here, we do just that.
388
wlock = _w32c_WriteLock(self.filename)
389
except errors.LockError:
390
return False, _w32c_ReadLock(self.filename)
394
class _w32c_WriteLock(_w32c_FileLock):
395
def __init__(self, filename):
396
super(_w32c_WriteLock, self).__init__()
398
win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
401
def restore_read_lock(self):
402
"""Restore the original ReadLock."""
403
# For win32 we had to completely let go of the original lock, so we
404
# just unlock and create a new read lock.
406
return _w32c_ReadLock(self.filename)
409
_lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
350
412
if have_ctypes_win32:
351
from ctypes.wintypes import DWORD, LPWSTR
352
LPSECURITY_ATTRIBUTES = ctypes.c_void_p # used as NULL no need to declare
353
HANDLE = ctypes.c_int # rather than unsigned as in ctypes.wintypes
354
_function_name = "CreateFileW"
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"))
356
425
# CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
357
426
_CreateFile = ctypes.WINFUNCTYPE(
358
HANDLE, # return value
360
DWORD, # dwDesiredAccess
362
LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
363
DWORD, # dwCreationDisposition
364
DWORD, # dwFlagsAndAttributes
365
HANDLE # hTemplateFile
427
HANDLE, # return value
429
DWORD, # dwDesiredAccess
431
LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
432
DWORD, # dwCreationDisposition
433
DWORD, # dwFlagsAndAttributes
434
HANDLE # hTemplateFile
366
435
)((_function_name, ctypes.windll.kernel32))
368
437
INVALID_HANDLE_VALUE = -1
447
518
# We default to using the first available lock class.
448
519
_lock_type, WriteLock, ReadLock = _lock_classes[0]
451
class _RelockDebugMixin(object):
452
"""Mixin support for -Drelock flag.
454
Add this as a base class then call self._note_lock with 'r' or 'w' when
455
acquiring a read- or write-lock. If this object was previously locked (and
456
locked the same way), and -Drelock is set, then this will trace.note a
462
def _note_lock(self, lock_type):
463
if 'relock' in debug.debug_flags and self._prev_lock == lock_type:
468
trace.note(gettext('{0!r} was {1} locked again'), self, type_name)
469
self._prev_lock = lock_type
472
@contextlib.contextmanager
473
def write_locked(lockable):
474
lockable.lock_write()