14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
17
"""Locking using OS file locks or file existence.
20
19
Note: This method of locking is generally deprecated in favour of LockDir, but
36
from __future__ import absolute_import
48
from bzrlib.hooks import HookPoint, Hooks
50
from .hooks import Hooks
51
from .i18n import gettext
51
54
class LockHooks(Hooks):
53
56
def __init__(self):
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))
57
Hooks.__init__(self, "breezy.lock", "Lock.hooks")
60
"Called with a breezy.lock.LockResult when a physical lock is "
64
"Called with a breezy.lock.LockResult when a physical lock is "
68
"Called with a breezy.lock.LockResult when a physical lock is "
66
72
class Lock(object):
86
92
def __repr__(self):
87
93
return '%s(%s, %s)' % (self.__class__.__name__,
88
self.lock_url, self.details)
94
self.lock_url, self.details)
91
97
class LogicalLockResult(object):
94
100
:ivar unlock: A callable which will unlock the lock.
97
def __init__(self, unlock):
103
def __init__(self, unlock, token=None):
98
104
self.unlock = unlock
100
107
def __repr__(self):
101
108
return "LogicalLockResult(%s)" % (self.unlock)
113
def __exit__(self, exc_type, exc_val, exc_tb):
114
# If there was an error raised, prefer the original one
117
except BaseException:
105
123
def cant_unlock_not_held(locked_object):
106
124
"""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
126
This provides a policy point from which we can generate either a warning or
111
129
# This is typically masking some other error and called from a finally
112
130
# block, so it's useful to have the option not to generate a new error
154
174
self.f = open(self.filename, filemode)
157
177
if e.errno in (errno.EACCES, errno.EPERM):
158
178
raise errors.LockFailed(self.filename, str(e))
159
179
if e.errno != errno.ENOENT:
221
234
# LOCK_NB will cause IOError to be raised if we can't grab a
222
235
# lock right away.
223
236
fcntl.lockf(self.f, fcntl.LOCK_EX | fcntl.LOCK_NB)
225
238
if e.errno in (errno.EAGAIN, errno.EACCES):
226
239
# We couldn't grab the lock
256
268
# LOCK_NB will cause IOError to be raised if we can't grab a
257
269
# lock right away.
258
270
fcntl.lockf(self.f, fcntl.LOCK_SH | fcntl.LOCK_NB)
260
272
# we should be more precise about whats a locking
261
273
# error and whats a random-other error
262
274
raise errors.LockContention(self.filename, e)
310
321
if self.filename in _fcntl_WriteLock._open_locks:
311
322
raise AssertionError('file already locked: %r'
314
325
# See if we can open the file for writing. Another process might
315
326
# have a read lock. We don't use self._open() because we don't want
317
328
# done by _fcntl_ReadLock
319
330
new_f = open(self.filename, 'rb+')
321
332
if e.errno in (errno.EACCES, errno.EPERM):
322
333
raise errors.LockFailed(self.filename, str(e))
325
336
# LOCK_NB will cause IOError to be raised if we can't grab a
326
337
# lock right away.
327
338
fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
329
340
# TODO: Raise a more specific error based on the type of error
330
341
raise errors.LockContention(self.filename, e)
331
342
_fcntl_WriteLock._open_locks.add(self.filename)
335
346
def restore_read_lock(self):
336
347
"""Restore the original ReadLock."""
337
# For fcntl, since we never released the read lock, just release the
338
# write lock, and return the original lock.
348
# For fcntl, since we never released the read lock, just release
349
# the write lock, and return the original lock.
339
350
fcntl.lockf(self.f, fcntl.LOCK_UN)
341
352
_fcntl_WriteLock._open_locks.remove(self.filename)
344
355
self._read_lock = None
348
358
_lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
351
361
if have_pywin32 and sys.platform == 'win32':
352
if os.path.supports_unicode_filenames:
353
# for Windows NT/2K/XP/etc
354
win32file_CreateFile = win32file.CreateFileW
357
win32file_CreateFile = win32file.CreateFile
362
win32file_CreateFile = win32file.CreateFileW
359
364
class _w32c_FileLock(_OSLock):
361
366
def _open(self, filename, access, share, cflags, pymode):
362
367
self.filename = osutils.realpath(filename)
364
self._handle = win32file_CreateFile(filename, access, share,
365
None, win32file.OPEN_ALWAYS,
369
self._handle = win32file_CreateFile(
370
filename, access, share, None, win32file.OPEN_ALWAYS,
366
371
win32file.FILE_ATTRIBUTE_NORMAL, None)
367
except pywintypes.error, e:
372
except pywintypes.error as e:
368
373
if e.args[0] == winerror.ERROR_ACCESS_DENIED:
369
374
raise errors.LockFailed(filename, e)
370
375
if e.args[0] == winerror.ERROR_SHARING_VIOLATION:
379
384
self._handle = None
382
386
class _w32c_ReadLock(_w32c_FileLock):
383
387
def __init__(self, filename):
384
388
super(_w32c_ReadLock, self).__init__()
385
389
self._open(filename, win32file.GENERIC_READ,
386
win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
390
win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
388
392
def temporary_write_lock(self):
389
393
"""Try to grab a write lock on the file.
404
408
return False, _w32c_ReadLock(self.filename)
405
409
return True, wlock
408
411
class _w32c_WriteLock(_w32c_FileLock):
409
412
def __init__(self, filename):
410
413
super(_w32c_WriteLock, self).__init__()
411
414
self._open(filename,
412
win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
415
win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
415
418
def restore_read_lock(self):
416
419
"""Restore the original ReadLock."""
420
423
return _w32c_ReadLock(self.filename)
423
425
_lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
426
428
if have_ctypes_win32:
427
from ctypes.wintypes import DWORD, LPCSTR, LPCWSTR
428
LPSECURITY_ATTRIBUTES = ctypes.c_void_p # used as NULL no need to declare
429
HANDLE = ctypes.c_int # rather than unsigned as in ctypes.wintypes
430
if os.path.supports_unicode_filenames:
431
_function_name = "CreateFileW"
434
_function_name = "CreateFileA"
435
class LPTSTR(LPCSTR):
436
def __new__(cls, obj):
437
return LPCSTR.__new__(cls, obj.encode("mbcs"))
429
from ctypes.wintypes import DWORD, LPWSTR
430
LPSECURITY_ATTRIBUTES = ctypes.c_void_p # used as NULL no need to declare
431
HANDLE = ctypes.c_int # rather than unsigned as in ctypes.wintypes
432
_function_name = "CreateFileW"
439
434
# CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
440
435
_CreateFile = ctypes.WINFUNCTYPE(
441
HANDLE, # return value
443
DWORD, # dwDesiredAccess
445
LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
446
DWORD, # dwCreationDisposition
447
DWORD, # dwFlagsAndAttributes
448
HANDLE # hTemplateFile
436
HANDLE, # return value
438
DWORD, # dwDesiredAccess
440
LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
441
DWORD, # dwCreationDisposition
442
DWORD, # dwFlagsAndAttributes
443
HANDLE # hTemplateFile
449
444
)((_function_name, ctypes.windll.kernel32))
451
446
INVALID_HANDLE_VALUE = -1
464
459
def _open(self, filename, access, share, cflags, pymode):
465
460
self.filename = osutils.realpath(filename)
466
461
handle = _CreateFile(filename, access, share, None, OPEN_ALWAYS,
467
FILE_ATTRIBUTE_NORMAL, 0)
462
FILE_ATTRIBUTE_NORMAL, 0)
468
463
if handle in (INVALID_HANDLE_VALUE, 0):
469
464
e = ctypes.WinError()
470
465
if e.args[0] == ERROR_ACCESS_DENIED:
479
474
def unlock(self):
483
477
class _ctypes_ReadLock(_ctypes_FileLock):
484
478
def __init__(self, filename):
485
479
super(_ctypes_ReadLock, self).__init__()
486
480
self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
489
483
def temporary_write_lock(self):
490
484
"""Try to grab a write lock on the file.
509
503
def __init__(self, filename):
510
504
super(_ctypes_WriteLock, self).__init__()
511
505
self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
514
508
def restore_read_lock(self):
515
509
"""Restore the original ReadLock."""
550
543
type_name = 'read'
552
545
type_name = 'write'
553
trace.note('%r was %s locked again', self, type_name)
546
trace.note(gettext('{0!r} was {1} locked again'), self, type_name)
554
547
self._prev_lock = lock_type
550
@contextlib.contextmanager
551
def write_locked(lockable):
552
lockable.lock_write()