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 ( from calibre.constants import (
__appname__, fcntl, filesystem_encoding, ishaiku, islinux, iswindows, win32api, __appname__, fcntl, filesystem_encoding, ishaiku, islinux, iswindows, win32api,
win32event, winerror win32event
) )
from calibre.utils.monotonic import monotonic from calibre.utils.monotonic import monotonic
if iswindows: if iswindows:
excl_file_mode = stat.S_IREAD | stat.S_IWRITE excl_file_mode = stat.S_IREAD | stat.S_IWRITE
import msvcrt import msvcrt, win32file, pywintypes, winerror
else: else:
excl_file_mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH 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') 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): def windows_open(path):
flags = os.O_RDWR | os.O_CREAT | os.O_NOINHERIT | os.O_BINARY try:
fd = os.open(path, flags, excl_file_mode) h = win32file.CreateFile(
return os.fdopen(fd, 'r+bN') 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): def windows_retry(err):
pass 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 limit = monotonic() + timeout
last_error = None while True:
while monotonic() <= limit:
try: try:
return func(*args) return func(*args)
except EnvironmentError as err: except EnvironmentError as err:
last_error = err.args if not error_retry(err) or monotonic() > limit:
if monotonic() > limit: raise
break
time.sleep(sleep_time) time.sleep(sleep_time)
raise TimeoutError(*last_error)
class ExclusiveFile(object): class ExclusiveFile(object):
@ -79,30 +91,20 @@ class ExclusiveFile(object):
self.sleep_time = sleep_time self.sleep_time = sleep_time
def __enter__(self): def __enter__(self):
try: if iswindows:
if iswindows: self.file = retry_for_a_time(
f = windows_open(self.path) self.timeout, self.sleep_time, windows_open, windows_retry, self.path
retry_for_a_time( )
self.timeout, self.sleep_time, msvcrt.locking, else:
f.fileno(), msvcrt.LK_NBLCK, 1 f = unix_open(self.path)
) retry_for_a_time(
else: self.timeout, self.sleep_time, fcntl.flock, unix_retry,
f = unix_open(self.path) f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB
retry_for_a_time( )
self.timeout, self.sleep_time, fcntl.flock,
f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB
)
self.file = f self.file = f
except TimeoutError as err:
raise OSError(*(list(err.args)[:2] + [self.path]))
return self.file return self.file
def __exit__(self, type, value, traceback): def __exit__(self, type, value, traceback):
if iswindows:
try:
msvcrt.locking(self.file.fileno(), msvcrt.LK_UNLCK, 1)
except EnvironmentError:
pass
self.file.close() self.file.close()