245
162
except IOError, e:
246
163
# we should be more precise about whats a locking
247
164
# error and whats a random-other error
248
raise errors.LockContention(self.filename, e)
165
raise errors.LockError(e)
250
167
def unlock(self):
251
count = _fcntl_ReadLock._open_locks[self.filename]
253
del _fcntl_ReadLock._open_locks[self.filename]
255
_fcntl_ReadLock._open_locks[self.filename] = count - 1
258
def temporary_write_lock(self):
259
"""Try to grab a write lock on the file.
261
On platforms that support it, this will upgrade to a write lock
262
without unlocking the file.
263
Otherwise, this will release the read lock, and try to acquire a
266
:return: A token which can be used to switch back to a read lock.
268
if self.filename in _fcntl_WriteLock._open_locks:
269
raise AssertionError('file already locked: %r'
272
wlock = _fcntl_TemporaryWriteLock(self)
273
except errors.LockError:
274
# We didn't unlock, so we can just return 'self'
279
class _fcntl_TemporaryWriteLock(_OSLock):
280
"""A token used when grabbing a temporary_write_lock.
282
Call restore_read_lock() when you are done with the write lock.
285
def __init__(self, read_lock):
286
super(_fcntl_TemporaryWriteLock, self).__init__()
287
self._read_lock = read_lock
288
self.filename = read_lock.filename
290
count = _fcntl_ReadLock._open_locks[self.filename]
292
# Something else also has a read-lock, so we cannot grab a
294
raise errors.LockContention(self.filename)
296
if self.filename in _fcntl_WriteLock._open_locks:
297
raise AssertionError('file already locked: %r'
300
# See if we can open the file for writing. Another process might
301
# have a read lock. We don't use self._open() because we don't want
302
# to create the file if it exists. That would have already been
303
# done by _fcntl_ReadLock
305
new_f = open(self.filename, 'rb+')
307
if e.errno in (errno.EACCES, errno.EPERM):
308
raise errors.LockFailed(self.filename, str(e))
311
# LOCK_NB will cause IOError to be raised if we can't grab a
313
fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
315
# TODO: Raise a more specific error based on the type of error
316
raise errors.LockContention(self.filename, e)
317
_fcntl_WriteLock._open_locks.add(self.filename)
321
def restore_read_lock(self):
322
"""Restore the original ReadLock."""
323
# For fcntl, since we never released the read lock, just release the
324
# write lock, and return the original lock.
325
fcntl.lockf(self.f, fcntl.LOCK_UN)
327
_fcntl_WriteLock._open_locks.remove(self.filename)
328
# Avoid reference cycles
329
read_lock = self._read_lock
330
self._read_lock = None
334
171
_lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
337
174
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)
175
LOCK_SH = 0 # the default
176
LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
177
LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
180
class _w32c_FileLock(_base_Lock):
182
def _lock(self, filename, openmode, lockmode):
183
self._open(filename, openmode)
185
self.hfile = msvcrt.get_osfhandle(self.f.fileno())
186
overlapped = pywintypes.OVERLAPPED()
350
self._handle = win32file_CreateFile(filename, access, share,
351
None, win32file.OPEN_ALWAYS,
352
win32file.FILE_ATTRIBUTE_NORMAL, None)
188
win32file.LockFileEx(self.hfile, lockmode, 0, 0x7fff0000,
353
190
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)
192
if e.args[0] in (winerror.ERROR_LOCK_VIOLATION,):
193
raise errors.LockContention(filename)
194
## import pdb; pdb.set_trace()
359
fd = win32file._open_osfhandle(self._handle, cflags)
360
self.f = os.fdopen(fd, pymode)
198
raise errors.LockError(e)
363
200
def unlock(self):
201
overlapped = pywintypes.OVERLAPPED()
203
win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
206
raise errors.LockError(e)
368
209
class _w32c_ReadLock(_w32c_FileLock):
369
210
def __init__(self, filename):
370
211
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)
212
self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
394
215
class _w32c_WriteLock(_w32c_FileLock):
395
216
def __init__(self, filename):
396
217
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)
218
self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
409
221
_lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
412
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
448
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)
224
if have_ctypes and sys.platform == 'win32':
225
# These constants were copied from the win32con.py module.
226
LOCKFILE_FAIL_IMMEDIATELY = 1
227
LOCKFILE_EXCLUSIVE_LOCK = 2
228
# Constant taken from winerror.py module
229
ERROR_LOCK_VIOLATION = 33
232
LOCK_EX = LOCKFILE_EXCLUSIVE_LOCK
233
LOCK_NB = LOCKFILE_FAIL_IMMEDIATELY
234
_LockFileEx = ctypes.windll.kernel32.LockFileEx
235
_UnlockFileEx = ctypes.windll.kernel32.UnlockFileEx
236
_GetLastError = ctypes.windll.kernel32.GetLastError
238
### Define the OVERLAPPED structure.
239
# http://msdn2.microsoft.com/en-us/library/ms684342.aspx
240
# typedef struct _OVERLAPPED {
241
# ULONG_PTR Internal;
242
# ULONG_PTR InternalHigh;
253
class _inner_struct(ctypes.Structure):
254
_fields_ = [('Offset', ctypes.c_uint), # DWORD
255
('OffsetHigh', ctypes.c_uint), # DWORD
258
class _inner_union(ctypes.Union):
259
_fields_ = [('anon_struct', _inner_struct), # struct
260
('Pointer', ctypes.c_void_p), # PVOID
263
class OVERLAPPED(ctypes.Structure):
264
_fields_ = [('Internal', ctypes.c_void_p), # ULONG_PTR
265
('InternalHigh', ctypes.c_void_p), # ULONG_PTR
266
('_inner_union', _inner_union),
267
('hEvent', ctypes.c_void_p), # HANDLE
270
class _ctypes_FileLock(_base_Lock):
272
def _lock(self, filename, openmode, lockmode):
273
self._open(filename, openmode)
275
self.hfile = msvcrt.get_osfhandle(self.f.fileno())
276
overlapped = OVERLAPPED()
277
p_overlapped = ctypes.pointer(overlapped)
278
result = _LockFileEx(self.hfile, # HANDLE hFile
279
lockmode, # DWORD dwFlags
280
0, # DWORD dwReserved
281
0x7fffffff, # DWORD nNumberOfBytesToLockLow
282
0x00000000, # DWORD nNumberOfBytesToLockHigh
283
p_overlapped, # lpOverlapped
287
last_err = _GetLastError()
288
if last_err in (ERROR_LOCK_VIOLATION,):
289
raise errors.LockContention(filename)
290
raise errors.LockError('Unknown locking error: %s'
465
293
def unlock(self):
294
overlapped = OVERLAPPED()
295
p_overlapped = ctypes.pointer(overlapped)
296
result = _UnlockFileEx(self.hfile, # HANDLE hFile
297
0, # DWORD dwReserved
298
0x7fffffff, # DWORD nNumberOfBytesToLockLow
299
0x00000000, # DWORD nNumberOfBytesToLockHigh
300
p_overlapped, # lpOverlapped
305
last_err = _GetLastError()
306
raise errors.LockError('Unknown unlocking error: %s'
469
310
class _ctypes_ReadLock(_ctypes_FileLock):
470
311
def __init__(self, filename):
471
312
super(_ctypes_ReadLock, self).__init__()
472
self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
475
def temporary_write_lock(self):
476
"""Try to grab a write lock on the file.
478
On platforms that support it, this will upgrade to a write lock
479
without unlocking the file.
480
Otherwise, this will release the read lock, and try to acquire a
483
:return: A token which can be used to switch back to a read lock.
485
# I can't find a way to upgrade a read lock to a write lock without
486
# unlocking first. So here, we do just that.
489
wlock = _ctypes_WriteLock(self.filename)
490
except errors.LockError:
491
return False, _ctypes_ReadLock(self.filename)
313
self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
494
316
class _ctypes_WriteLock(_ctypes_FileLock):
495
317
def __init__(self, filename):
496
318
super(_ctypes_WriteLock, self).__init__()
497
self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
500
def restore_read_lock(self):
501
"""Restore the original ReadLock."""
502
# For win32 we had to completely let go of the original lock, so we
503
# just unlock and create a new read lock.
505
return _ctypes_ReadLock(self.filename)
319
self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
508
322
_lock_classes.append(('ctypes', _ctypes_WriteLock, _ctypes_ReadLock))