[Zope-CMF] CMF FSObject cooking bug
Shane Hathaway
shane@zope.com
Mon, 23 Sep 2002 13:20:31 -0400
This is a multi-part message in MIME format.
--------------080805040409040308050608
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
Alan and Andy seem to say there's still a trace of a race condition in
parsing FSObjects on Zope startup. The bug exists in CMF 1.3, and it
has been fixed in CVS, but Alan and Andy say it's still around even on
CMF-1_3-branch. Here's a patch for CMFCore/FSObject.py that makes it
even more conservative. It serializes all FSObject parsing.
If this solves the problem (main symptom: random NameErrors on bound
variables like "context" and "container" in FSPythonScripts) then I'll
check it in. (I still can't reproduce the bug, though.)
Note that if you're unable to apply the patch, you probably need to
update your checkout.
Shane
--------------080805040409040308050608
Content-Type: text/plain;
name="CMF-deep-fried.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="CMF-deep-fried.patch"
Index: CMFCore/FSObject.py
===================================================================
RCS file: /cvs-repository/CMF/CMFCore/FSObject.py,v
retrieving revision 1.12
diff -u -r1.12 FSObject.py
--- CMFCore/FSObject.py 15 Aug 2002 20:03:09 -0000 1.12
+++ CMFCore/FSObject.py 23 Sep 2002 17:07:49 -0000
@@ -17,6 +17,7 @@
from string import split
from os import path, stat
+from threading import RLock
import Acquisition, Globals
from AccessControl import ClassSecurityInfo
@@ -28,6 +29,10 @@
from CMFCorePermissions import ViewManagementScreens
from CMFCorePermissions import ManagePortal
+# Note that _parse_lock must be reentrant to avoid deadlocks.
+_parse_lock = RLock()
+
+
class FSObject(Acquisition.Implicit, Item):
"""FSObject is a base class for all filesystem based look-alikes.
@@ -98,15 +103,22 @@
# Refresh our contents from the filesystem if that is newer and we are
# running in debug mode.
def _updateFromFS(self):
- parsed = self._parsed
- if not parsed or Globals.DevelopmentMode:
+ if not self._parsed or Globals.DevelopmentMode:
fp = expandpath(self._filepath)
try: mtime=stat(fp)[8]
except: mtime=0
- if not parsed or mtime != self._file_mod_time:
- self._readFile(1)
- self._file_mod_time = mtime
- self._parsed = 1
+ if not self._parsed or mtime != self._file_mod_time:
+ _parse_lock.acquire()
+ try:
+ # This object may have been parsed in another thread
+ # just before _parse_lock was acquired. Check the
+ # condition again.
+ if not self._parsed or mtime != self._file_mod_time:
+ self._readFile(1)
+ self._file_mod_time = mtime
+ self._parsed = 1
+ finally:
+ _parse_lock.release()
security.declareProtected(View, 'get_size')
def get_size(self):
--------------080805040409040308050608--