<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1">
  <title></title>
</head>
<body text="#000000" bgcolor="#ffffff">
Hello Tim,<br>
<br>
thank you for the answer!<br>
<br>
1. I think, you have understood the problem correctly.<br>
<br>
2. The second problem you mentioned, we have solved by using semaphores
in the calling class.<br>
<br>
3. We really need this functionality. Our application is for much more
than a year in production. Until Zope 2.6.x (ZODB used the class
LockFile from winlock) we didn't have this problem. But we had a
problem with big&nbsp; memory leaks. At the moment we<br>
need reboot Zope 2.6.x about 5 times a day (!). The memory problem is
solved in Zope 2.7.0. Therefore we really need Zope 2.7.0. <br>
<br>
4. Because most of the time the application works fine and the problem
only comes up with very particular temporarily behaviour, it needs a
lot of time to find out the problem.<br>
<br>
Many thanks again,<br>
Ulla Theiss.<br>
<br>
Tim Peters wrote:<br>
<blockquote type="cite"
 cite="mid20040706195544.9C6F13B8038@smtp.zope.com">
  <pre wrap="">[Ulla Theiss]
  </pre>
  <blockquote type="cite">
    <pre wrap="">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.
    </pre>
  </blockquote>
  <pre wrap=""><!---->
That's dangerous; more below.

  </pre>
  <blockquote type="cite">
    <pre wrap="">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)
    </pre>
  </blockquote>
  <pre wrap=""><!---->
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 &lt;&gt; errno.ENOENT: raise
            self._fp = open(path, 'w+')
        lock_file(self._fp)
        print &gt;&gt; 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:

  </pre>
  <blockquote type="cite">
    <blockquote type="cite">
      <blockquote type="cite">
        <pre wrap="">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
        </pre>
      </blockquote>
    </blockquote>
  </blockquote>
  <pre wrap=""><!---->Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in ?
  File "ZODB\lock_file.py", line 33, in lock_file
    _LockFile(file.fileno(), 0, 0, 1, 0)
winlock.error: 33
  </pre>
  <pre wrap=""><!---->
  </pre>
  <blockquote type="cite">
    <pre wrap="">Therefore, if another thread already uses the lock-file, the first thread
is unable to delete it and the 'Permission denied' exception is thrown.
    </pre>
  </blockquote>
  <pre wrap=""><!---->
If I'm understanding you, that's one failure mode, but not the only failure
mode.

  </pre>
  <blockquote type="cite">
    <pre wrap="">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
    </pre>
  </blockquote>
  <pre wrap=""><!---->
That can't stop the other failure mode above.
 
  </pre>
  <blockquote type="cite">
    <pre wrap="">How to correct the error best?
    </pre>
  </blockquote>
  <pre wrap=""><!---->
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 &lt;wink&gt;.


  </pre>
</blockquote>
</body>
</html>