Przeglądaj źródła

Only one lock file per process (lockf() works now)
lockf() is more portable than flock()

Unrud 9 lat temu
rodzic
commit
49bc0728e3
1 zmienionych plików z 29 dodań i 30 usunięć
  1. 29 30
      radicale/storage.py

+ 29 - 30
radicale/storage.py

@@ -518,6 +518,7 @@ class Collection(BaseCollection):
         return ""
 
     _lock = threading.Condition()
+    _lock_file = None
     _readers = 0
     _writer = False
 
@@ -542,40 +543,38 @@ class Collection(BaseCollection):
                 cls._lock.notify()
             else:
                 cls._writer = True
-        folder = os.path.expanduser(
-            cls.configuration.get("storage", "filesystem_folder"))
-        if not os.path.exists(folder):
-            os.makedirs(folder, exist_ok=True)
-        lock_path = os.path.join(folder, "Radicale.lock")
-        lock_file = open(lock_path, "w+")
-        # set access rights to a necessary minimum to prevent locking by
-        # arbitrary users
-        try:
-            os.chmod(lock_path, stat.S_IWUSR | stat.S_IRUSR)
-        except OSError:
-            cls.logger.debug("Failed to set permissions on lock file")
-        if os.name == "nt":
-            handle = msvcrt.get_osfhandle(lock_file.fileno())
-            flags = LOCKFILE_EXCLUSIVE_LOCK if mode == "w" else 0
-            overlapped = Overlapped()
-            if not lock_file_ex(handle, flags, 0, 1, 0, overlapped):
-                cls.logger.debug("Locking not supported")
-        elif os.name == "posix":
-            operation = fcntl.LOCK_EX if mode == "w" else fcntl.LOCK_SH
-            # According to documentation flock() is emulated with fcntl() on
-            # some platforms. fcntl() locks are not associated with an open
-            # file descriptor. The same file can be locked multiple times
-            # within the same process and if any fd of the file is closed,
-            # all locks are released.
-            # flock() does not work on NFS shares.
-            try:
-                fcntl.flock(lock_file.fileno(), operation)
-            except OSError:
-                cls.logger.debug("Locking not supported")
+            if not cls._lock_file:
+                folder = os.path.expanduser(
+                    cls.configuration.get("storage", "filesystem_folder"))
+                if not os.path.exists(folder):
+                    os.makedirs(folder, exist_ok=True)
+                lock_path = os.path.join(folder, "Radicale.lock")
+                cls._lock_file = open(lock_path, "w+")
+                # set access rights to a necessary minimum to prevent locking
+                # by arbitrary users
+                try:
+                    os.chmod(lock_path, stat.S_IWUSR | stat.S_IRUSR)
+                except OSError:
+                    cls.logger.debug("Failed to set permissions on lock file")
+                if os.name == "nt":
+                    handle = msvcrt.get_osfhandle(cls._lock_file.fileno())
+                    flags = LOCKFILE_EXCLUSIVE_LOCK if mode == "w" else 0
+                    overlapped = Overlapped()
+                    if not lock_file_ex(handle, flags, 0, 1, 0, overlapped):
+                        cls.logger.debug("Locking not supported")
+                elif os.name == "posix":
+                    _cmd = fcntl.LOCK_EX if mode == "w" else fcntl.LOCK_SH
+                    try:
+                        fcntl.lockf(cls._lock_file.fileno(), _cmd)
+                    except OSError:
+                        cls.logger.debug("Locking not supported")
         yield
         with cls._lock:
             if mode == "r":
                 cls._readers -= 1
             else:
                 cls._writer = False
+            if cls._readers == 0:
+                cls._lock_file.close()
+                cls._lock_file = None
             cls._lock.notify()