[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--