1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
"""Locking using OS file locks or file existence.
45
from bzrlib.hooks import HookPoint, Hooks
48
class LockHooks(Hooks):
52
self.create_hook(HookPoint('lock_acquired',
53
"Called with a bzrlib.lock.LockResult when a physical lock is "
54
"acquired.", (1, 8), None))
55
self.create_hook(HookPoint('lock_released',
56
"Called with a bzrlib.lock.LockResult when a physical lock is "
57
"released.", (1, 8), None))
58
self.create_hook(HookPoint('lock_broken',
59
"Called with a bzrlib.lock.LockResult when a physical lock is "
60
"broken.", (1, 15), None))
64
"""Base class for locks.
66
:cvar hooks: Hook dictionary for operations on locks.
72
class LockResult(object):
73
"""Result of an operation on a lock; passed to a hook"""
75
def __init__(self, lock_url, details=None):
76
"""Create a lock result for lock with optional details about the lock."""
77
self.lock_url = lock_url
78
self.details = details
80
def __eq__(self, other):
81
return self.lock_url == other.lock_url and self.details == other.details
84
return '%s(%s%s)' % (self.__class__.__name__,
85
self.lock_url, self.details)
95
have_ctypes_win32 = False
96
if sys.platform == 'win32':
99
import win32con, win32file, pywintypes, winerror
106
have_ctypes_win32 = True
47
111
class _OSLock(object):
58
122
except IOError, e:
59
123
if e.errno in (errno.EACCES, errno.EPERM):
60
raise errors.ReadOnlyLockError(self.filename, str(e))
124
raise errors.LockFailed(self.filename, str(e))
61
125
if e.errno != errno.ENOENT:
144
191
# we should be more precise about whats a locking
145
192
# error and whats a random-other error
146
raise errors.LockContention(e)
193
raise errors.LockContention(self.filename, e)
148
195
def unlock(self):
149
196
_fcntl_WriteLock._open_locks.remove(self.filename)
167
214
except IOError, e:
168
215
# we should be more precise about whats a locking
169
216
# error and whats a random-other error
170
raise errors.LockContention(e)
217
raise errors.LockContention(self.filename, e)
172
219
def unlock(self):
173
220
count = _fcntl_ReadLock._open_locks[self.filename]
188
235
:return: A token which can be used to switch back to a read lock.
190
assert self.filename not in _fcntl_WriteLock._open_locks
237
if self.filename in _fcntl_WriteLock._open_locks:
238
raise AssertionError('file already locked: %r'
192
241
wlock = _fcntl_TemporaryWriteLock(self)
193
242
except errors.LockError:
214
263
raise errors.LockContention(self.filename)
216
assert self.filename not in _fcntl_WriteLock._open_locks
265
if self.filename in _fcntl_WriteLock._open_locks:
266
raise AssertionError('file already locked: %r'
218
269
# See if we can open the file for writing. Another process might
219
270
# have a read lock. We don't use self._open() because we don't want
223
274
new_f = open(self.filename, 'rb+')
224
275
except IOError, e:
225
276
if e.errno in (errno.EACCES, errno.EPERM):
226
raise errors.ReadOnlyLockError(self.filename, str(e))
277
raise errors.LockFailed(self.filename, str(e))
229
280
# LOCK_NB will cause IOError to be raised if we can't grab a
231
282
fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
232
283
except IOError, e:
233
284
# TODO: Raise a more specific error based on the type of error
234
raise errors.LockContention(e)
285
raise errors.LockContention(self.filename, e)
235
286
_fcntl_WriteLock._open_locks.add(self.filename)
284
335
win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
286
337
except Exception, e:
287
raise errors.LockContention(e)
338
raise errors.LockContention(self.filename, e)
290
341
class _w32c_ReadLock(_w32c_FileLock):
328
379
_lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
331
if have_ctypes and sys.platform == 'win32':
382
if have_ctypes_win32:
332
383
# These constants were copied from the win32con.py module.
333
384
LOCKFILE_FAIL_IMMEDIATELY = 1
334
385
LOCKFILE_EXCLUSIVE_LOCK = 2
393
444
last_err = _GetLastError()
394
445
if last_err in (ERROR_LOCK_VIOLATION,):
395
446
raise errors.LockContention(filename)
396
raise errors.LockContention('Unknown locking error: %s'
447
raise errors.LockContention(filename,
448
'Unknown locking error: %s' % (last_err,))
399
450
def unlock(self):
400
451
overlapped = OVERLAPPED()
410
461
last_err = _GetLastError()
411
raise errors.LockContention('Unknown unlocking error: %s'
462
raise errors.LockContention(self.filename,
463
'Unknown unlocking error: %s' % (last_err,))
415
466
class _ctypes_ReadLock(_ctypes_FileLock):