Further simplify windows exclusive file by using open_osfhandle

This commit is contained in:
Kovid Goyal 2017-05-03 16:24:13 +05:30
parent 55844a8b8e
commit 58840260cf
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -13,13 +13,13 @@ import time
from calibre.constants import (
__appname__, fcntl, filesystem_encoding, ishaiku, islinux, iswindows, win32api,
win32event, winerror
win32event
)
from calibre.utils.monotonic import monotonic
if iswindows:
excl_file_mode = stat.S_IREAD | stat.S_IWRITE
import msvcrt
import msvcrt, win32file, pywintypes, winerror
else:
excl_file_mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH
@ -44,28 +44,40 @@ def unix_open(path):
return os.fdopen(fd, 'r+b')
def unix_retry(err):
return err.errno in (errno.EACCES, errno.EAGAIN, errno.ENOLCK, errno.EINTR)
def windows_open(path):
flags = os.O_RDWR | os.O_CREAT | os.O_NOINHERIT | os.O_BINARY
fd = os.open(path, flags, excl_file_mode)
return os.fdopen(fd, 'r+bN')
try:
h = win32file.CreateFile(
path,
win32file.GENERIC_READ | win32file.GENERIC_WRITE, # Open for reading and writing
0, # Open exclusive
None, # No security attributes, ensures handle is not inherited by children
win32file.OPEN_ALWAYS, # If file does not exist, create it
win32file.FILE_ATTRIBUTE_NORMAL, # Normal attributes
None, # No template file
)
except pywintypes.error as err:
raise WindowsError(err[0], err[2], path)
fd = msvcrt.open_osfhandle(h.Detach(), 0)
return os.fdopen(fd, 'r+b')
class TimeoutError(Exception):
pass
def windows_retry(err):
return err.winerror in (winerror.ERROR_SHARING_VIOLATION, winerror.ERROR_LOCK_VIOLATION)
def retry_for_a_time(timeout, sleep_time, func, *args):
def retry_for_a_time(timeout, sleep_time, func, error_retry, *args):
limit = monotonic() + timeout
last_error = None
while monotonic() <= limit:
while True:
try:
return func(*args)
except EnvironmentError as err:
last_error = err.args
if monotonic() > limit:
break
if not error_retry(err) or monotonic() > limit:
raise
time.sleep(sleep_time)
raise TimeoutError(*last_error)
class ExclusiveFile(object):
@ -79,30 +91,20 @@ class ExclusiveFile(object):
self.sleep_time = sleep_time
def __enter__(self):
try:
if iswindows:
f = windows_open(self.path)
retry_for_a_time(
self.timeout, self.sleep_time, msvcrt.locking,
f.fileno(), msvcrt.LK_NBLCK, 1
)
else:
f = unix_open(self.path)
retry_for_a_time(
self.timeout, self.sleep_time, fcntl.flock,
f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB
)
if iswindows:
self.file = retry_for_a_time(
self.timeout, self.sleep_time, windows_open, windows_retry, self.path
)
else:
f = unix_open(self.path)
retry_for_a_time(
self.timeout, self.sleep_time, fcntl.flock, unix_retry,
f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB
)
self.file = f
except TimeoutError as err:
raise OSError(*(list(err.args)[:2] + [self.path]))
return self.file
def __exit__(self, type, value, traceback):
if iswindows:
try:
msvcrt.locking(self.file.fileno(), msvcrt.LK_UNLCK, 1)
except EnvironmentError:
pass
self.file.close()