[Zodb-checkins] SVN: ZODB/branches/jim-zeo-blob/s Refactored ZODB.lock_file to keep lock files around after writing a

Jim Fulton jim at zope.com
Thu May 17 15:11:36 EDT 2007


Log message for revision 75816:
  Refactored ZODB.lock_file to keep lock files around after writing a
  test to demonstrate a race condition.
  
  Retired the winlock module in favor of msvcrt from the standard
  library.
  

Changed:
  U   ZODB/branches/jim-zeo-blob/setup.py
  U   ZODB/branches/jim-zeo-blob/src/ZODB/lock_file.py
  U   ZODB/branches/jim-zeo-blob/src/ZODB/lock_file.txt
  U   ZODB/branches/jim-zeo-blob/src/ZODB/tests/test_lock_file.py
  D   ZODB/branches/jim-zeo-blob/src/ZODB/winlock.c
  D   ZODB/branches/jim-zeo-blob/src/ZODB/winlock.txt

-=-
Modified: ZODB/branches/jim-zeo-blob/setup.py
===================================================================
--- ZODB/branches/jim-zeo-blob/setup.py	2007-05-17 17:45:08 UTC (rev 75815)
+++ ZODB/branches/jim-zeo-blob/setup.py	2007-05-17 19:11:36 UTC (rev 75816)
@@ -168,13 +168,6 @@
          TimeStamp,
         ]
 
-if sys.platform == 'win32':
-    exts.append(Extension(name = 'ZODB.winlock',
-                          include_dirs = include,
-                          sources = ['src/ZODB/winlock.c']
-                          )
-                )
-
 # The ZODB.zodb4 code is not being packaged, because it is only
 # need to convert early versions of Zope3 databases to ZODB3.
 

Modified: ZODB/branches/jim-zeo-blob/src/ZODB/lock_file.py
===================================================================
--- ZODB/branches/jim-zeo-blob/src/ZODB/lock_file.py	2007-05-17 17:45:08 UTC (rev 75815)
+++ ZODB/branches/jim-zeo-blob/src/ZODB/lock_file.py	2007-05-17 19:11:36 UTC (rev 75816)
@@ -25,7 +25,7 @@
     import fcntl
 except ImportError:
     try:
-        import ZODB.winlock
+        import msvcrt
     except ImportError:
         def _lock_file(file):
             raise TypeError('No file-locking support on this platform')
@@ -37,15 +37,14 @@
         def _lock_file(file):
             # Lock just the first byte
             try:
-                ZODB.winlock.LockFile(file.fileno())
-            except ZODB.winlock.LockError:
+                msvcrt.locking(file.fileno(), msvcrt.LK_NBLCK, 1)
+            except IOError:
                 raise LockError("Couldn't lock %r" % file.name)
-            
 
         def _unlock_file(file):
             try:
-                ZODB.winlock.UnlockFile(file.fileno())
-            except ZODB.winlock.LockError:
+                msvcrt.locking(file.fileno(), msvcrt.LK_UNLCK, 1)
+            except IOError:
                 raise LockError("Couldn't unlock %r" % file.name)
                 
 else:
@@ -70,26 +69,28 @@
 # close both closes and unlocks the lock file.
 class LockFile:
 
+    _fp = None
+
     def __init__(self, path):
         self._path = path
+        fp = open(path, 'w+')
+
         try:
-            self._fp = open(path, 'r+')
-        except IOError, e:
-            if e.errno <> errno.ENOENT: raise
-            self._fp = open(path, 'w+')
-        # Acquire the lock and piss on the hydrant
-        try:
-            _lock_file(self._fp)
+            _lock_file(fp)
         except:
-            self._fp.close()
-            logger.exception("Error locking file %s", path)
+            fp.seek(1)
+            pid = fp.read().strip()[:20]
+            fp.close()
+            logger.exception("Error locking file", path, pid)
             raise
-        print >> self._fp, os.getpid()
-        self._fp.flush()
 
+        self._fp = fp
+        fp.write(" %s\n" % os.getpid())
+        fp.truncate()
+        fp.flush()
+
     def close(self):
         if self._fp is not None:
             _unlock_file(self._fp)
             self._fp.close()
-            os.unlink(self._path)
             self._fp = None

Modified: ZODB/branches/jim-zeo-blob/src/ZODB/lock_file.txt
===================================================================
--- ZODB/branches/jim-zeo-blob/src/ZODB/lock_file.txt	2007-05-17 17:45:08 UTC (rev 75815)
+++ ZODB/branches/jim-zeo-blob/src/ZODB/lock_file.txt	2007-05-17 19:11:36 UTC (rev 75816)
@@ -1,8 +1,6 @@
 Lock file support
 =================
 
-Obviously, this should move out of the ZODB package. :/
-
 The ZODB lock_file module provides support for creating file system
 locks.  These are locks that are implemented with lock files and
 OS-provided locking facilities.  To create a lock, instantiate a
@@ -23,27 +21,13 @@
 
     >>> lock.close()
 
-The lock file is automatically removed:
+The lock file is not removed.  It is left behind:
 
     >>> import os
     >>> os.path.exists('lock')
-    False
+    True
 
 Of course, now that we've released the lock, we can created it again:
 
     >>> lock = ZODB.lock_file.LockFile('lock')
     >>> lock.close()
-    >>> os.path.exists('lock')
-    False
-
-It's OK if the file exists, say because it wasn't cleaned up properly:
-
-    >>> open('lock', 'w').close()
-    >>> os.path.exists('lock')
-    True
-    >>> lock = ZODB.lock_file.LockFile('lock')
-    >>> lock.close()
-    >>> os.path.exists('lock')
-    False
-
-Of course, it's a bad idea to put precious data into a lock file.

Modified: ZODB/branches/jim-zeo-blob/src/ZODB/tests/test_lock_file.py
===================================================================
--- ZODB/branches/jim-zeo-blob/src/ZODB/tests/test_lock_file.py	2007-05-17 17:45:08 UTC (rev 75815)
+++ ZODB/branches/jim-zeo-blob/src/ZODB/tests/test_lock_file.py	2007-05-17 19:11:36 UTC (rev 75816)
@@ -14,9 +14,43 @@
 import os, sys, unittest
 from zope.testing import doctest
 
+import ZODB.lock_file, time, threading
+    
+
+def inc():
+    while 1:
+        try:
+            lock = ZODB.lock_file.LockFile('f.lock')
+        except ZODB.lock_file.LockError:
+            continue
+        else:
+            break
+    f = open('f', 'r+b')
+    v = int(f.readline().strip())
+    time.sleep(0.01)
+    v += 1
+    f.seek(0)
+    f.write('%d\n' % v)
+    lock.close()
+
+def many_threads_read_and_write():
+    r"""
+    >>> open('f', 'w+b').write('0\n')
+
+    >>> n = 10
+    >>> threads = [threading.Thread(target=inc) for i in range(n)]
+    >>> _ = [thread.start() for thread in threads]
+    >>> _ = [thread.join() for thread in threads]
+    >>> saved = int(open('f', 'rb').readline().strip())
+    >>> saved == n
+    True
+
+    >>> os.remove('f')
+    >>> os.remove('f.lock')
+    """
+
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest(doctest.DocFileSuite(os.path.join('..', 'lock_file.txt')))
-    if sys.platform == 'win32':
-        suite.addTest(doctest.DocFileSuite(os.path.join('..', 'winlock.txt')))
+    suite.addTest(doctest.DocTestSuite())
     return suite

Deleted: ZODB/branches/jim-zeo-blob/src/ZODB/winlock.c
===================================================================
--- ZODB/branches/jim-zeo-blob/src/ZODB/winlock.c	2007-05-17 17:45:08 UTC (rev 75815)
+++ ZODB/branches/jim-zeo-blob/src/ZODB/winlock.c	2007-05-17 19:11:36 UTC (rev 75816)
@@ -1,99 +0,0 @@
-/*****************************************************************************
-
-  Copyright (c) Zope Corporation and Contributors.
-  All Rights Reserved.
-
-  This software is subject to the provisions of the Zope Public License,
-  Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-  WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-  WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-  FOR A PARTICULAR PURPOSE
-
- ****************************************************************************/
-static char winlock_doc_string[] =
-"Lock files on Windows."
-"\n"
-"$Id$\n";
-
-#include "Python.h"
-
-static PyObject *Error, *LockError;
-
-#include <windows.h>
-#include <io.h>
-
-/* LOCK_FUNC is the shared type of Win32 LockFile and UnlockFile. */
-typedef WINBASEAPI BOOL WINAPI LOCK_FUNC(HANDLE, DWORD, DWORD, DWORD, DWORD);
-
-static PyObject *
-common(LOCK_FUNC func, PyObject *args)
-{
-  int fileno;
-  long h, ofslo=0, ofshi=0, lenlo=1, lenhi=0;
-
-  if (! PyArg_ParseTuple(args, "i|llll", &fileno,
-                         &ofslo, &ofshi,
-                         &lenlo, &lenhi))
-    return NULL;
-
-  h = _get_osfhandle(fileno);
-  if (h == -1) {
-    PyErr_SetString(Error, "_get_osfhandle failed");
-    return NULL;
-  }
-  if (func((HANDLE)h, ofslo, ofshi, lenlo, lenhi)) {
-    Py_INCREF(Py_None);
-    return Py_None;
-  }
-  PyErr_SetObject(LockError, PyInt_FromLong(GetLastError()));
-  return NULL;
-}
-
-static PyObject *
-winlock(PyObject *ignored, PyObject *args)
-{
-  return common(LockFile, args);
-}
-
-static PyObject *
-winunlock(PyObject *ignored, PyObject *args)
-{
-  return common(UnlockFile, args);
-}
-
-static struct PyMethodDef methods[] = {
-  {"LockFile",	(PyCFunction)winlock,	METH_VARARGS,
-   "LockFile(fileno, offsetLow, offsetHigh, lengthLow, lengthHigh) -- "
-   "Lock the file associated with fileno"},
-  
-  {"UnlockFile",	(PyCFunction)winunlock,	METH_VARARGS,
-   "UnlockFile(fileno, offsetLow, offsetHigh, lengthLow, lengthHigh) -- "
-   "Unlock the file associated with fileno"},
-  
-  {NULL,		NULL}		/* sentinel */
-};
-
-/* Initialization function for the module (*must* be called initwinlock) */
-
-#ifndef DL_EXPORT	/* declarations for DLL import/export */
-#define DL_EXPORT(RTYPE) RTYPE
-#endif
-DL_EXPORT(void)
-initwinlock(void)
-{
-  PyObject *m, *d;
-
-  if (!(Error=PyErr_NewException("ZODB.winlock.Error", NULL, NULL)))
-    return;
-  if (!(LockError=PyErr_NewException("ZODB.winlock.LockError", NULL, NULL)))
-    return;
-
-  /* Create the module and add the functions */
-  m = Py_InitModule4("winlock", methods, winlock_doc_string,
-                     (PyObject*)NULL, PYTHON_API_VERSION);
-
-  d = PyModule_GetDict(m);
-  PyDict_SetItemString(d, "Error", Error);
-  PyDict_SetItemString(d, "LockError", LockError);
-}

Deleted: ZODB/branches/jim-zeo-blob/src/ZODB/winlock.txt
===================================================================
--- ZODB/branches/jim-zeo-blob/src/ZODB/winlock.txt	2007-05-17 17:45:08 UTC (rev 75815)
+++ ZODB/branches/jim-zeo-blob/src/ZODB/winlock.txt	2007-05-17 19:11:36 UTC (rev 75816)
@@ -1,29 +0,0 @@
-Windows File Locks
-==================
-
-The winlock module provids support for locking existing files.
-
-    >>> f = open('lock', 'w')
-
-    >>> import ZODB.winlock
-    >>> ZODB.winlock.LockFile(f.fileno())
-
-If we try to lock a file more than once, we'll get a lock error:
-
-    >>> try:
-    ...     ZODB.winlock.LockFile(f.fileno())
-    ... except ZODB.winlock.LockError:
-    ...     print "Can't lock file"
-    Can't lock file
-
-We use UnlockFile to remove the lock:
-
-    >>> ZODB.winlock.UnlockFile(f.fileno())
-    >>> ZODB.winlock.LockFile(f.fileno())
-    >>> ZODB.winlock.UnlockFile(f.fileno())
-
-It is up to applications to create and remove the lock file:
-
-    >>> f.close()
-    >>> import os
-    >>> os.remove('lock')



More information about the Zodb-checkins mailing list