119
119
class _fcntl_WriteLock(_fcntl_FileLock):
123
123
def __init__(self, filename):
124
# standard IO errors get exposed directly.
125
124
super(_fcntl_WriteLock, self).__init__()
126
self._open(filename, 'rb+')
127
if self.filename in self.open_locks:
125
# Check we can grab a lock before we actually open the file.
126
self.filename = osutils.realpath(filename)
127
if self.filename in _fcntl_WriteLock._open_locks:
129
129
raise errors.LockContention(self.filename)
131
self._open(self.filename, 'rb+')
130
132
# reserve a slot for this lock - even if the lockf call fails,
131
133
# at thisi point unlock() will be called, because self.f is set.
132
134
# TODO: make this fully threadsafe, if we decide we care.
133
self.open_locks[self.filename] = self.filename
135
_fcntl_WriteLock._open_locks.add(self.filename)
135
137
# LOCK_NB will cause IOError to be raised if we can't grab a
136
138
# lock right away.
144
146
raise errors.LockError(e)
146
148
def unlock(self):
147
del self.open_locks[self.filename]
149
_fcntl_WriteLock._open_locks.remove(self.filename)
151
153
class _fcntl_ReadLock(_fcntl_FileLock):
155
157
def __init__(self, filename):
156
158
super(_fcntl_ReadLock, self).__init__()
159
self.filename = osutils.realpath(filename)
160
_fcntl_ReadLock._open_locks.setdefault(self.filename, 0)
161
_fcntl_ReadLock._open_locks[self.filename] += 1
157
162
self._open(filename, 'rb')
159
164
# LOCK_NB will cause IOError to be raised if we can't grab a
165
170
raise errors.LockError(e)
167
172
def unlock(self):
173
count = _fcntl_ReadLock._open_locks[self.filename]
175
del _fcntl_ReadLock._open_locks[self.filename]
177
_fcntl_ReadLock._open_locks[self.filename] = count - 1
180
def temporary_write_lock(self):
181
"""Try to grab a write lock on the file.
183
On platforms that support it, this will upgrade to a write lock
184
without unlocking the file.
185
Otherwise, this will release the read lock, and try to acquire a
188
:return: A token which can be used to switch back to a read lock.
190
assert self.filename not in _fcntl_WriteLock._open_locks
192
wlock = _fcntl_TemporaryWriteLock(self)
193
except errors.LockError:
194
# We didn't unlock, so we can just return 'self'
199
class _fcntl_TemporaryWriteLock(_base_Lock):
200
"""A token used when grabbing a temporary_write_lock.
202
Call restore_read_lock() when you are done with the write lock.
205
def __init__(self, read_lock):
206
super(_fcntl_TemporaryWriteLock, self).__init__()
207
self._read_lock = read_lock
208
self.filename = read_lock.filename
210
count = _fcntl_ReadLock._open_locks[self.filename]
212
# Something else also has a read-lock, so we cannot grab a
214
raise errors.LockContention(self.filename)
216
assert self.filename not in _fcntl_WriteLock._open_locks
218
# See if we can open the file for writing. Another process might
219
# have a read lock. We don't use self._open() because we don't want
220
# to create the file if it exists. That would have already been
221
# done by _fcntl_ReadLock
223
new_f = open(self.filename, 'rb+')
225
if e.errno in (errno.EACCES, errno.EPERM):
226
raise errors.ReadOnlyLockError(self.filename, str(e))
229
# LOCK_NB will cause IOError to be raised if we can't grab a
231
fcntl.lockf(new_f, fcntl.LOCK_SH | fcntl.LOCK_NB)
233
# TODO: Raise a more specific error based on the type of error
234
raise errors.LockError(e)
235
_fcntl_WriteLock._open_locks.add(self.filename)
239
def restore_read_lock(self):
240
"""Restore the original ReadLock."""
241
# For fcntl, since we never released the read lock, just release the
242
# write lock, and return the original lock.
243
fcntl.lockf(self.f, fcntl.LOCK_UN)
245
_fcntl_WriteLock._open_locks.remove(self.filename)
246
# Avoid reference cycles
247
read_lock = self._read_lock
248
self._read_lock = None
171
252
_lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
211
292
super(_w32c_ReadLock, self).__init__()
212
293
self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
295
def temporary_write_lock(self):
296
"""Try to grab a write lock on the file.
298
On platforms that support it, this will upgrade to a write lock
299
without unlocking the file.
300
Otherwise, this will release the read lock, and try to acquire a
303
:return: A token which can be used to switch back to a read lock.
305
# I can't find a way to upgrade a read lock to a write lock without
306
# unlocking first. So here, we do just that.
309
wlock = _w32c_WriteLock(self.filename)
310
except errors.LockError:
311
return False, _w32c_ReadLock(self.filename)
215
315
class _w32c_WriteLock(_w32c_FileLock):
216
316
def __init__(self, filename):
217
317
super(_w32c_WriteLock, self).__init__()
218
318
self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
320
def restore_read_lock(self):
321
"""Restore the original ReadLock."""
322
# For win32 we had to completely let go of the original lock, so we
323
# just unlock and create a new read lock.
325
return _w32c_ReadLock(self.filename)
221
328
_lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
310
417
super(_ctypes_ReadLock, self).__init__()
311
418
self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
420
def temporary_write_lock(self):
421
"""Try to grab a write lock on the file.
423
On platforms that support it, this will upgrade to a write lock
424
without unlocking the file.
425
Otherwise, this will release the read lock, and try to acquire a
428
:return: A token which can be used to switch back to a read lock.
430
# I can't find a way to upgrade a read lock to a write lock without
431
# unlocking first. So here, we do just that.
434
wlock = _ctypes_WriteLock(self.filename)
435
except errors.LockError:
436
return False, _ctypes_ReadLock(self.filename)
314
439
class _ctypes_WriteLock(_ctypes_FileLock):
315
440
def __init__(self, filename):
316
441
super(_ctypes_WriteLock, self).__init__()
317
442
self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
444
def restore_read_lock(self):
445
"""Restore the original ReadLock."""
446
# For win32 we had to completely let go of the original lock, so we
447
# just unlock and create a new read lock.
449
return _ctypes_ReadLock(self.filename)
320
452
_lock_classes.append(('ctypes', _ctypes_WriteLock, _ctypes_ReadLock))