[ZODB-Dev] Exception while closing ZODB
Tim Peters
tim at zope.com
Tue Jul 6 15:55:41 EDT 2004
[Ulla Theiss]
> since Version Zope 2.7.0 we have a problem closing the ZODB under
> Windows.
>
> Sometime the class LockFile (in the file .../ZODB/lock_file.py) throws
> while unlinking the lockfile the Exception:
> OSError: [Errno 13] Permission denied '....\\var\\...fs.lock'
>
> We open and close ZODB-databases in different threads.
That's dangerous; more below.
> A look at the sourcecode in LockFile.close() shows, that first
> unlock_file(self._fp)
> self._fp.close()
>
> is called. Which means other threads and processes are able to use the
> locked file again. Afterwards the lock-file should be deleted:
>
> os.unlink(self._path)
Let's look at all the code:
class LockFile:
def __init__(self, path):
self._path = path
try:
self._fp = open(path, 'r+')
except IOError, e:
if e.errno <> errno.ENOENT: raise
self._fp = open(path, 'w+')
lock_file(self._fp)
print >> self._fp, os.getpid()
self._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
As best I understand you:
thread A created a LockFile, and later called its close() method
simultaneously, thread B calls LockFile.__init__ with the same path
thread A completes its unlock_file() and _fp_close() calls
thread B completes its open() call
thread A then barfs on its unlink() call (because you can't delete
an open file on Windows, and thread B has the file open now)
Is that right? If so, that's not the only way it can fail. For example,
before thread A does unlock_file(), thread B completes open() and
tries to do lock_file()
thread B will barf then (Windows file locks aren't "advisory" -- you
can't lock a file that's already locked on Windows)
Example:
>>> import ZODB
>>> from ZODB import lock_file
>>> f = open('whatever', 'w')
>>> lock_file.lock_file(f)
>>> g = open('whatever', 'r')
>>> lock_file.lock_file(g) # attempt to lock the same physical file twice
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "ZODB\lock_file.py", line 33, in lock_file
_LockFile(file.fileno(), 0, 0, 1, 0)
winlock.error: 33
>>>
> Therefore, if another thread already uses the lock-file, the first thread
> is unable to delete it and the 'Permission denied' exception is thrown.
If I'm understanding you, that's one failure mode, but not the only failure
mode.
> As a workaround we put the os.unlink - statement in a try-except-block.
>
> def close(self):
> if self._fp is not None:
> unlock_file(self._fp)
> self._fp.close()
> try:
> os.unlink(self._path)
> self._fp = None
> except:
> pass
That can't stop the other failure mode above.
> How to correct the error best?
Trying to open a file in one thread, while simultaneously closing it another
thread, is the deeper problem here, and seems inherently ill-defined. Do
you really need to do that? Since yours is the first report of this, it
can't be a popular vice <wink>.
More information about the ZODB-Dev
mailing list