[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