Commit dee9727e authored by Jim Fulton's avatar Jim Fulton

Removed the ZODB.lock_file module. Now depend on zc.lockfile instead.

parent e6b642a6
...@@ -267,6 +267,7 @@ setup(name="ZODB3", ...@@ -267,6 +267,7 @@ setup(name="ZODB3",
'ZConfig', 'ZConfig',
'zdaemon', 'zdaemon',
'transaction', 'transaction',
'zc.lockfile',
], ],
zip_safe = False, zip_safe = False,
entry_points = """ entry_points = """
......
...@@ -71,6 +71,8 @@ New Features ...@@ -71,6 +71,8 @@ New Features
- Removed the ThreadedAsync module. - Removed the ThreadedAsync module.
- Now depend on zc.lockfile
Bugs Fixed Bugs Fixed
---------- ----------
......
...@@ -40,7 +40,7 @@ from ZEO.auth import get_module ...@@ -40,7 +40,7 @@ from ZEO.auth import get_module
from ZEO.zrpc.client import ConnectionManager from ZEO.zrpc.client import ConnectionManager
import ZODB.interfaces import ZODB.interfaces
import ZODB.lock_file import zc.lockfile
import ZODB.BaseStorage import ZODB.BaseStorage
from ZODB import POSException from ZODB import POSException
from ZODB import utils from ZODB import utils
...@@ -931,8 +931,8 @@ class ClientStorage(object): ...@@ -931,8 +931,8 @@ class ClientStorage(object):
lockfilename = blob_filename+'.lock' lockfilename = blob_filename+'.lock'
try: try:
lock = ZODB.lock_file.LockFile(lockfilename) lock = zc.lockfile.LockFile(lockfilename)
except ZODB.lock_file.LockError: except zc.lockfile.LockError:
# Someone is already downloading the Blob. Wait for the # Someone is already downloading the Blob. Wait for the
# lock to be freed. How long should we be willing to wait? # lock to be freed. How long should we be willing to wait?
...@@ -941,8 +941,8 @@ class ClientStorage(object): ...@@ -941,8 +941,8 @@ class ClientStorage(object):
while 1: while 1:
time.sleep(0.1) time.sleep(0.1)
try: try:
lock = ZODB.lock_file.LockFile(lockfilename) lock = zc.lockfile.LockFile(lockfilename)
except ZODB.lock_file.LockError: except zc.lockfile.LockError:
pass pass
else: else:
# We have the lock. We should be able to get the file now. # We have the lock. We should be able to get the file now.
......
...@@ -34,7 +34,7 @@ import threading ...@@ -34,7 +34,7 @@ import threading
import time import time
import ZODB.fsIndex import ZODB.fsIndex
import ZODB.lock_file import zc.lockfile
from ZODB.utils import p64, u64, z64 from ZODB.utils import p64, u64, z64
logger = logging.getLogger("ZEO.cache") logger = logging.getLogger("ZEO.cache")
...@@ -181,7 +181,7 @@ class ClientCache(object): ...@@ -181,7 +181,7 @@ class ClientCache(object):
# (and it sets self.f). # (and it sets self.f).
if path: if path:
self._lock_file = ZODB.lock_file.LockFile(path + '.lock') self._lock_file = zc.lockfile.LockFile(path + '.lock')
if path and os.path.exists(path): if path and os.path.exists(path):
# Reuse an existing file. scan() will open & read it. # Reuse an existing file. scan() will open & read it.
......
...@@ -34,7 +34,7 @@ import ZODB.interfaces ...@@ -34,7 +34,7 @@ import ZODB.interfaces
from ZODB import BaseStorage, ConflictResolution, POSException from ZODB import BaseStorage, ConflictResolution, POSException
from ZODB.POSException import UndoError, POSKeyError, MultipleUndoErrors from ZODB.POSException import UndoError, POSKeyError, MultipleUndoErrors
from persistent.TimeStamp import TimeStamp from persistent.TimeStamp import TimeStamp
from ZODB.lock_file import LockFile from zc.lockfile import LockFile
from ZODB.utils import p64, u64, cp, z64 from ZODB.utils import p64, u64, cp, z64
from ZODB.FileStorage.fspack import FileStoragePacker from ZODB.FileStorage.fspack import FileStoragePacker
from ZODB.FileStorage.format import FileStorageFormatter, DataHeader from ZODB.FileStorage.format import FileStorageFormatter, DataHeader
......
##############################################################################
#
# 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 lock a file
"""
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
# This is a better interface to use than the lockfile.lock_file() interface.
# Creating the instance acquires the lock. The file remains open. Calling
# close both closes and unlocks the lock file.
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()
if not pid:
pid = 'UNKNOWN'
logger.exception("Error locking file %s; pid=%s", 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
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 ZODB.lock_file
>>> lock = ZODB.lock_file.LockFile('lock')
If we try to lock the same name, we'll get a lock error and it will be logged:
>>> import ZODB.tests.loggingsupport
>>> handler = ZODB.tests.loggingsupport.InstalledHandler('ZODB.lock_file')
>>> try:
... ZODB.lock_file.LockFile('lock')
... except ZODB.lock_file.LockError:
... print "Can't lock file"
Can't lock file
>>> for record in handler.records:
... print record.levelname, record.getMessage()
ERROR Error locking file lock; pid=UNKNOWN
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 = ZODB.lock_file.LockFile('lock')
>>> lock.close()
##############################################################################
#
# 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 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)
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(os.path.join('..', 'lock_file.txt')))
suite.addTest(doctest.DocTestSuite())
return suite
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment