From e1aec91911786b82d2eb39047153687574de37b2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 25 Jun 2009 16:09:25 -0700 Subject: [PATCH] Improve file locking on windows for config files. Should fix #2598 (Calibre 0.6b6 Crash While Converting) --- src/calibre/ebooks/oeb/reader.py | 2 +- src/calibre/ebooks/pdf/pdftohtml.py | 2 +- src/calibre/utils/lock.py | 109 ++++++++++++++++++++++++---- 3 files changed, 97 insertions(+), 16 deletions(-) diff --git a/src/calibre/ebooks/oeb/reader.py b/src/calibre/ebooks/oeb/reader.py index 9a637c1a24..ab8d9976fd 100644 --- a/src/calibre/ebooks/oeb/reader.py +++ b/src/calibre/ebooks/oeb/reader.py @@ -140,7 +140,7 @@ class OEBReader(object): mi.language = get_lang() meta_info_to_oeb_metadata(mi, self.oeb.metadata, self.logger) bookid = "urn:uuid:%s" % str(uuid.uuid4()) if mi.application_id is None \ - else mi.applicaion_id + else mi.application_id self.oeb.metadata.add('identifier', bookid, id='calibre-uuid') self.oeb.uid = self.oeb.metadata.identifier[0] diff --git a/src/calibre/ebooks/pdf/pdftohtml.py b/src/calibre/ebooks/pdf/pdftohtml.py index 11631c7fe0..9293d616ac 100644 --- a/src/calibre/ebooks/pdf/pdftohtml.py +++ b/src/calibre/ebooks/pdf/pdftohtml.py @@ -69,7 +69,7 @@ def pdftohtml(output_dir, pdf_path, no_images): if not os.path.exists(index) or os.stat(index).st_size < 100: raise DRMError() - with open(index, 'rb+wb') as i: + with open(index, 'r+b') as i: raw = i.read() raw = '\n' + raw i.seek(0) diff --git a/src/calibre/utils/lock.py b/src/calibre/utils/lock.py index b5d165dcb6..5098c78f90 100644 --- a/src/calibre/utils/lock.py +++ b/src/calibre/utils/lock.py @@ -13,6 +13,96 @@ import time, atexit, os class LockError(Exception): pass +class WindowsExclFile(object): + + def __init__(self, path, timeout=20): + self.name = path + import win32file as w + import pywintypes + + while timeout > 0: + timeout -= 1 + try: + self._handle = w.CreateFile(path, + w.GENERIC_READ|w.GENERIC_WRITE, # Open for reading and writing + 0, # Open exclusive + None, # No security attributes + w.OPEN_ALWAYS, # If file does not exist, create it + w.FILE_ATTRIBUTE_NORMAL, #Normal attributes + None, #No template file + ) + break + except pywintypes.error, err: + if getattr(err, 'args', [-1])[0] in (0x20, 0x21): + time.sleep(1) + continue + else: + raise + + def seek(self, amt, frm=0): + import win32file as w + if frm not in (0, 1, 2): + raise ValueError('Invalid from for seek: %s'%frm) + frm = {0:w.FILE_BEGIN, 1: w.FILE_CURRENT, 2:w.FILE_END}[frm] + if frm is w.FILE_END: + amt = 0 - amt + w.SetFilePointer(self._handle, amt, frm) + + def tell(self): + import win32file as w + return w.SetFilePointer(self._handle, 0, w.FILE_CURRENT) + + def flush(self): + import win32file as w + w.FlushFileBuffers(self._handle) + + def close(self): + if self._handle is not None: + import win32file as w + self.flush() + w.CloseHandle(self._handle) + self._handle = None + + def read(self, bytes=-1): + import win32file as w + sz = w.GetFileSize(self._handle) + max = sz - self.tell() + if bytes < 0: bytes = max + bytes = min(max, bytes) + if bytes < 1: + return '' + hr, ans = w.ReadFile(self._handle, bytes, None) + if hr != 0: + raise IOError('Error reading file: %s'%hr) + return ans + + def readlines(self, sizehint=-1): + return self.read().splitlines() + + def write(self, bytes): + if isinstance(bytes, unicode): + bytes = bytes.encode('utf-8') + import win32file as w + w.WriteFile(self._handle, bytes, None) + + def truncate(self, size=None): + import win32file as w + pos = self.tell() + if size is None: + size = pos + t = min(size, pos) + self.seek(t) + w.SetEndOfFile(self._handle) + self.seek(pos) + + def isatty(self): + return False + + @property + def closed(self): + return self._handle is None + + class ExclusiveFile(object): def __init__(self, path, timeout=15): @@ -20,17 +110,10 @@ class ExclusiveFile(object): self.timeout = timeout def __enter__(self): - self.file = open(self.path, 'a+b') + self.file = WindowsExclFile(self.path, self.timeout) if iswindows else open(self.path, 'a+b') self.file.seek(0) timeout = self.timeout - if iswindows: - name = ('Local\\'+(__appname__+self.file.name).replace('\\', '_'))[:201] - while self.timeout < 0 or timeout >= 0: - self.mutex = win32event.CreateMutex(None, False, name) - if win32api.GetLastError() != winerror.ERROR_ALREADY_EXISTS: break - time.sleep(1) - timeout -= 1 - else: + if not iswindows: while self.timeout < 0 or timeout >= 0: try: fcntl.lockf(self.file.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB) @@ -38,14 +121,12 @@ class ExclusiveFile(object): except IOError: time.sleep(1) timeout -= 1 - if timeout < 0 and self.timeout >= 0: - self.file.close() - raise LockError + if timeout < 0 and self.timeout >= 0: + self.file.close() + raise LockError('Failed to lock') return self.file def __exit__(self, type, value, traceback): - if iswindows: - win32api.CloseHandle(self.mutex) self.file.close() def _clean_lock_file(file):