changeset 4419:d85005213fd9

locking: workaround win32 problems (spurious access denied exceptions), improve logging
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sat, 08 Nov 2008 14:18:04 +0100
parents db692d99e4d1
children 5ccf04ad9941
files MoinMoin/util/filesys.py MoinMoin/util/lock.py
diffstat 2 files changed, 56 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/util/filesys.py	Sat Nov 08 14:36:38 2008 +0100
+++ b/MoinMoin/util/filesys.py	Sat Nov 08 14:18:04 2008 +0100
@@ -2,13 +2,17 @@
 """
     MoinMoin - File System Utilities
 
-    @copyright: 2002 Juergen Hermann <jh@web.de>
+    @copyright: 2002 Juergen Hermann <jh@web.de>,
+                2006-2008 MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
 
-import sys, os, shutil, time
+import sys, os, shutil, time, errno
 from stat import S_ISDIR, ST_MODE, S_IMODE
 
+from MoinMoin import log
+logging = log.getLogger(__name__)
+
 #############################################################################
 ### Misc Helpers
 #############################################################################
@@ -67,6 +71,37 @@
     else:
         os.utime(name, None)
 
+
+def access_denied_decorator(fn):
+    """ Due to unknown reasons, some os.* functions on Win32 sometimes fail
+        with Access Denied (although access should be possible).
+        Just retrying it a bit later works and this is what we do.
+    """
+    if sys.platform == 'win32':
+        def wrapper(*args, **kwargs):
+            max_retries = 10
+            retry = 0
+            while True:
+                try:
+                    return fn(*args, **kwargs)
+                except OSError, err:
+                    retry += 1
+                    if retry > max_retries:
+                        raise
+                    if err.errno == errno.EACCES:
+                        logging.warning('%s(%r, %r) -> access denied. retrying...' % (fn.__name__, args, kwargs))
+                        time.sleep(0.01)
+                        continue
+                    raise
+        return wrapper
+    else:
+        return fn
+
+stat = access_denied_decorator(os.stat)
+mkdir = access_denied_decorator(os.mkdir)
+rmdir = access_denied_decorator(os.rmdir)
+
+
 def fuid(filename, max_staleness=3600):
     """ return a unique id for a file
 
--- a/MoinMoin/util/lock.py	Sat Nov 08 14:36:38 2008 +0100
+++ b/MoinMoin/util/lock.py	Sat Nov 08 14:18:04 2008 +0100
@@ -2,12 +2,17 @@
 """
     MoinMoin - locking functions
 
-    @copyright: 2005 Florian Festi, Nir Soffer
+    @copyright: 2005 Florian Festi, Nir Soffer,
+                2008 MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
 
 import os, sys, tempfile, time, errno
 
+from MoinMoin import log
+logging = log.getLogger(__name__)
+
+from MoinMoin.util import filesys
 
 class Timer:
     """ Simple count down timer
@@ -105,9 +110,9 @@
         timer.start()
         while timer.haveTime():
             try:
-                os.mkdir(self.lockDir)
+                filesys.mkdir(self.lockDir)
                 self._locked = True
-                # log('acquired exclusive lock: %s\n' % (self.lockDir, ))
+                logging.debug('acquired exclusive lock: %s' % (self.lockDir, ))
                 return True
             except OSError, err:
                 if err.errno != errno.EEXIST:
@@ -115,15 +120,16 @@
                 if self.expire():
                     continue # Try immediately to acquire
                 timer.sleep()
+        logging.debug('failed to acquire exclusive lock: %s' % (self.lockDir, ))
         return False
 
     def release(self):
         """ Release the lock """
         if not self._locked:
-            raise RuntimeError("lock already released")
+            raise RuntimeError('lock already released: %s' % self.lockDir)
         self._removeLockDir()
         self._locked = False
-        # log('released lock: %s\n' % self.lockDir)
+        logging.debug('released lock: %s' % self.lockDir)
 
     def isLocked(self):
         return self._locked
@@ -141,7 +147,7 @@
         if self.timeout is None:
             return not self.exists()
         try:
-            lock_age = time.time() - os.stat(self.lockDir).st_mtime
+            lock_age = time.time() - filesys.stat(self.lockDir).st_mtime
             return lock_age > self.timeout
         except OSError, err:
             if err.errno == errno.ENOENT:
@@ -153,7 +159,7 @@
         """ Return True if the lock is expired or missing; False otherwise. """
         if self.isExpired():
             self._removeLockDir()
-            # log("expired lock: %s\n" % self.lockDir)
+            logging.debug("expired lock: %s" % self.lockDir)
             return True
         return False
 
@@ -162,8 +168,8 @@
     def _makeDir(self):
         """ Make sure directory exists """
         try:
-            os.mkdir(self.dir)
-            # log('created directory: %s\n' % self.dir)
+            filesys.mkdir(self.dir)
+            logging.debug('created directory: %s' % self.dir)
         except OSError, err:
             if err.errno != errno.EEXIST:
                 raise
@@ -171,7 +177,8 @@
     def _removeLockDir(self):
         """ Remove lockDir ignoring 'No such file or directory' errors """
         try:
-            os.rmdir(self.lockDir)
+            filesys.rmdir(self.lockDir)
+            logging.debug('removed directory: %s' % self.dir)
         except OSError, err:
             if err.errno != errno.ENOENT:
                 raise
@@ -230,7 +237,7 @@
                     timer.sleep()
             finally:
                 if result:
-                    # log('acquired write lock: %s\n' % (self.lockDir))
+                    logging.debug('acquired write lock: %s' % self.lockDir)
                     return True
                 else:
                     self.release()
@@ -291,7 +298,7 @@
             try:
                 self.lockDir = tempfile.mkdtemp('', self.fileName, self.dir)
                 self._locked = True
-                # log('acquired read lock: %s\n' % self.lockDir)
+                logging.debug('acquired read lock: %s' % self.lockDir)
                 return True
             finally:
                 self.writeLock.release()
@@ -393,5 +400,3 @@
             return WriteLock.expire(self)
         else: # POSIX
             return True
-
-