[Checkins] SVN: zc.lockfile/trunk/ Split locking code out of ZODB.

Jim Fulton jim at zope.com
Wed Jul 18 07:24:03 EDT 2007


Log message for revision 78102:
  Split locking code out of ZODB.
  

Changed:
  A   zc.lockfile/trunk/README.txt
  A   zc.lockfile/trunk/buildout.cfg
  A   zc.lockfile/trunk/setup.py
  A   zc.lockfile/trunk/src/
  A   zc.lockfile/trunk/src/zc/
  A   zc.lockfile/trunk/src/zc/__init__.py
  A   zc.lockfile/trunk/src/zc/lockfile/
  A   zc.lockfile/trunk/src/zc/lockfile/README.txt
  A   zc.lockfile/trunk/src/zc/lockfile/__init__.py
  A   zc.lockfile/trunk/src/zc/lockfile/tests.py

-=-
Added: zc.lockfile/trunk/README.txt
===================================================================
--- zc.lockfile/trunk/README.txt	                        (rev 0)
+++ zc.lockfile/trunk/README.txt	2007-07-18 11:24:02 UTC (rev 78102)
@@ -0,0 +1,14 @@
+*************************
+Basic inter-process locks
+*************************
+
+The zc.lockfile package provides a basic portable implementation of
+interprocess locks using lock files.  The purpose if not specifically
+yo lock files, but to simply provide locks with an implementation
+based on file-locking primitives.  Of course, these locks could be
+used to mediate access to *other* files.  For example, the ZODB file
+storage implementation uses file locks to mediate access to
+file-storage database files.  The database files and lock file files
+are separate files.
+
+.. contents::


Property changes on: zc.lockfile/trunk/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.lockfile/trunk/buildout.cfg
===================================================================
--- zc.lockfile/trunk/buildout.cfg	                        (rev 0)
+++ zc.lockfile/trunk/buildout.cfg	2007-07-18 11:24:02 UTC (rev 78102)
@@ -0,0 +1,7 @@
+[buildout]
+develop = .
+parts = test
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = zc.lockfile


Property changes on: zc.lockfile/trunk/buildout.cfg
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.lockfile/trunk/setup.py
===================================================================
--- zc.lockfile/trunk/setup.py	                        (rev 0)
+++ zc.lockfile/trunk/setup.py	2007-07-18 11:24:02 UTC (rev 78102)
@@ -0,0 +1,47 @@
+import os
+from setuptools import setup, find_packages
+
+def read(*rnames):
+    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+long_description=(
+        read('README.txt')
+        + '\n' +
+        'Detailed Documentation\n'
+        '**********************\n'
+        + '\n' +
+        read('src', 'zc', 'lockfile', 'README.txt')
+        + '\n' +
+        'Download\n'
+        '**********************\n'
+        )
+
+open('doc.txt', 'w').write(long_description)
+
+name = "zc.lockfile"
+setup(
+    name = name,
+    version = "1.0.0b1",
+    author = "Jim Fulton",
+    author_email = "jim at zope.com",
+    description = "Basic inter-process locks",
+    long_description=long_description,
+    license = "ZPL 2.1",
+    keywords = "lock",
+    url='http://www.python.org/pypi/'+name,
+
+    packages = find_packages('src'),
+    package_dir = {'': 'src'},
+    namespace_packages = ['zc'],
+    install_requires = 'setuptools',
+    include_package_data = True,
+    zip_safe=False,
+    classifiers = [
+        'Programming Language :: Python',
+        'Development Status :: 4 - Beta',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: Zope Public License',
+        'Operating System :: POSIX',
+        'Operating System :: Microsoft :: Windows',
+       ],
+    )


Property changes on: zc.lockfile/trunk/setup.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.lockfile/trunk/src/zc/__init__.py
===================================================================
--- zc.lockfile/trunk/src/zc/__init__.py	                        (rev 0)
+++ zc.lockfile/trunk/src/zc/__init__.py	2007-07-18 11:24:02 UTC (rev 78102)
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)


Property changes on: zc.lockfile/trunk/src/zc/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Copied: zc.lockfile/trunk/src/zc/lockfile/README.txt (from rev 78057, ZODB/trunk/src/ZODB/lock_file.txt)
===================================================================
--- zc.lockfile/trunk/src/zc/lockfile/README.txt	                        (rev 0)
+++ zc.lockfile/trunk/src/zc/lockfile/README.txt	2007-07-18 11:24:02 UTC (rev 78102)
@@ -0,0 +1,33 @@
+Lock file support
+=================
+
+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
+LockFile object with a file name:
+
+    >>> import zc.lockfile
+    >>> lock = zc.lockfile.LockFile('lock')
+
+If we try to lock the same name, we'll get a lock error:
+
+    >>> try:
+    ...     zc.lockfile.LockFile('lock')
+    ... except zc.lockfile.LockError:
+    ...     print "Can't lock"
+    Can't lock
+
+To release the lock, use it's close method:
+
+    >>> lock.close()
+
+The lock file is not removed.  It is left behind:
+
+    >>> import os
+    >>> os.path.exists('lock')
+    True
+
+Of course, now that we've released the lock, we can created it again:
+
+    >>> lock = zc.lockfile.LockFile('lock')
+    >>> lock.close()

Copied: zc.lockfile/trunk/src/zc/lockfile/__init__.py (from rev 78057, ZODB/trunk/src/ZODB/lock_file.py)
===================================================================
--- zc.lockfile/trunk/src/zc/lockfile/__init__.py	                        (rev 0)
+++ zc.lockfile/trunk/src/zc/lockfile/__init__.py	2007-07-18 11:24:02 UTC (rev 78102)
@@ -0,0 +1,93 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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
+#
+##############################################################################
+
+import os
+import errno
+import logging
+logger = logging.getLogger("ZODB.lock_file")
+
+class LockError(Exception):
+    """Couldn't get a lock
+    """
+
+try:
+    import fcntl
+except ImportError:
+    try:
+        import msvcrt
+    except ImportError:
+        def _lock_file(file):
+            raise TypeError('No file-locking support on this platform')
+        def _unlock_file(file):
+            raise TypeError('No file-locking support on this platform')
+
+    else:
+        # Windows
+        def _lock_file(file):
+            # Lock just the first byte
+            try:
+                msvcrt.locking(file.fileno(), msvcrt.LK_NBLCK, 1)
+            except IOError:
+                raise LockError("Couldn't lock %r" % file.name)
+
+        def _unlock_file(file):
+            try:
+                file.seek(0)
+                msvcrt.locking(file.fileno(), msvcrt.LK_UNLCK, 1)
+            except IOError:
+                raise LockError("Couldn't unlock %r" % file.name)
+                
+else:
+    # Unix
+    _flags = fcntl.LOCK_EX | fcntl.LOCK_NB
+
+    def _lock_file(file):
+        try:
+            fcntl.flock(file.fileno(), _flags)
+        except IOError:
+            raise LockError("Couldn't lock %r" % file.name)
+            
+
+    def _unlock_file(file):
+        # File is automatically unlocked on close
+        pass
+
+
+class LockFile:
+
+    _fp = None
+
+    def __init__(self, path):
+        self._path = path
+        fp = open(path, 'w+')
+
+        try:
+            _lock_file(fp)
+        except:
+            fp.seek(1)
+            pid = fp.read().strip()[:20]
+            fp.close()
+            logger.exception("Error locking file", path, pid)
+            raise
+
+        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()
+            self._fp = None

Copied: zc.lockfile/trunk/src/zc/lockfile/tests.py (from rev 78057, ZODB/trunk/src/ZODB/tests/test_lock_file.py)
===================================================================
--- zc.lockfile/trunk/src/zc/lockfile/tests.py	                        (rev 0)
+++ zc.lockfile/trunk/src/zc/lockfile/tests.py	2007-07-18 11:24:02 UTC (rev 78102)
@@ -0,0 +1,58 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+#
+##############################################################################
+import os, sys, unittest
+from zope.testing import doctest
+
+import zc.lockfile, time, threading
+    
+
+def inc():
+    while 1:
+        try:
+            lock = zc.lockfile.LockFile('f.lock')
+        except zc.lockfile.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)
+    f.close()
+    lock.close()
+
+def many_threads_read_and_write():
+    r"""
+    >>> open('f', 'w+b').write('0\n')
+    >>> open('f.lock', 'w+b').write('0\n')
+
+    >>> n = 50
+    >>> 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('README.txt'))
+    suite.addTest(doctest.DocTestSuite())
+    return suite



More information about the Checkins mailing list