backend.py: Introduce protocol for alternative locking schemes.
authorMark Wooding <mdw@distorted.org.uk>
Mon, 22 Dec 2014 20:32:58 +0000 (20:32 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Tue, 7 Apr 2015 18:54:17 +0000 (19:54 +0100)
It sounds fancier than it is.  There's now a method for FlatFileBackend
subclasses to override if they want to apply different locking
semantics.

backend.py

index 576486c..fda32e0 100644 (file)
@@ -25,6 +25,8 @@
 
 from __future__ import with_statement
 
+from auto import HOME
+import errno as E
 import itertools as I
 import os as OS; ENV = OS.environ
 
@@ -206,10 +208,13 @@ class FlatFileBackend (object):
   specified by the DELIM constructor argument.
 
   The file is updated by writing a new version alongside, as `FILE.new', and
-  renaming it over the old version.  If a LOCK file is named then an
-  exclusive fcntl(2)-style lock is taken out on `LOCKDIR/LOCK' (creating the
-  file if necessary) during the update operation.  Use of a lockfile is
-  strongly recommended.
+  renaming it over the old version.  If a LOCK is provided then this is done
+  while holding a lock.  By default, an exclusive fcntl(2)-style lock is
+  taken out on `LOCKDIR/LOCK' (creating the file if necessary) during the
+  update operation, but subclasses can override the `dolocked' method to
+  provide alternative locking behaviour; the LOCK parameter is not
+  interpreted by any other methods.  Use of a lockfile is strongly
+  recommended.
 
   The DELIM constructor argument specifies the delimiter character used when
   splitting lines into fields.  The USER and PASSWD arguments give the field
@@ -329,11 +334,21 @@ class FlatFileBackend (object):
 
     ## If there's a lockfile, then acquire it around the meat of this
     ## function; otherwise just do the job.
-    if me._lock is None:
-      doit()
-    else:
-      with U.lockfile(OS.path.join(CFG.LOCKDIR, me._lock), 5):
-        doit()
+    if me._lock is None: doit()
+    else: me.dolocked(me._lock, doit)
+
+  def dolocked(me, lock, func):
+    """
+    Call FUNC with the LOCK held.
+
+    Subclasses can override this method in order to provide alternative
+    locking functionality.
+    """
+    try: OS.mkdir(CFG.LOCKDIR)
+    except OSError, e:
+      if e.errno != E.EEXIST: raise
+    with U.lockfile(OS.path.join(CFG.LOCKDIR, lock), 5):
+      func()
 
   def _parse(me, line):
     """Convenience function for constructing a record."""