[Erp5-report] r38852 jm - /erp5/trunk/products/ERP5Type/Base.py
nobody at svn.erp5.org
nobody at svn.erp5.org
Sat Oct 2 21:43:57 CEST 2010
Author: jm
Date: Sat Oct 2 21:43:54 2010
New Revision: 38852
URL: http://svn.erp5.org?rev=38852&view=rev
Log:
Replace thread-local variable by a lock (for real thread safety) and a list (for reentrancy)
Ideally, a lock should be used everywhere we modify (or sometimes access) a
global variable.
Here, _aq_dynamic is written in an optimistic way to avoid acquiring the lock
every time.
Modified:
erp5/trunk/products/ERP5Type/Base.py
Modified: erp5/trunk/products/ERP5Type/Base.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Base.py?rev=38852&r1=38851&r2=38852&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Base.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/Base.py [utf8] Sat Oct 2 21:43:54 2010
@@ -31,7 +31,7 @@ from struct import unpack
from copy import copy
import warnings
import types
-from threading import local
+import threading
from Products.ERP5Type.Globals import InitializeClass, DTMLFile
from AccessControl import ClassSecurityInfo
@@ -760,8 +760,9 @@ class Base( CopyContainer,
isTempDocument = ConstantGetter('isTempDocument', value=False)
# Dynamic method acquisition system (code generation)
+ aq_method_lock = threading.RLock()
aq_method_generated = {}
- aq_method_generating = local()
+ aq_method_generating = []
aq_portal_type = {}
aq_related_generated = 0
@@ -833,24 +834,12 @@ class Base( CopyContainer,
def _propertyMap(self):
""" Method overload - properties are now defined on the ptype """
- aq_key = self._aq_key()
self._aq_dynamic('id') # Make sure aq_dynamic has been called once
- if Base.aq_portal_type.has_key(aq_key):
- return tuple(list(getattr(Base.aq_portal_type[aq_key], '_properties', ())) +
- list(getattr(self, '_local_properties', ())))
- return ERP5PropertyManager._propertyMap(self)
-
- def _aq_dynamic_pmethod(self, id):
- ptype = self.portal_type
- klass = self.__class__
- aq_key = (ptype, klass) # We do not use _aq_key() here for speed
-
- #LOG("In _aq_dynamic_pmethod", 0, str((id, ptype, self)))
-
- if Base.aq_portal_type.has_key(aq_key):
- return getattr(Base.aq_portal_type[aq_key], id, None).__of__(self)
-
- return None
+ property_holder = Base.aq_portal_type.get(self._aq_key())
+ if property_holder is None:
+ return ERP5PropertyManager._propertyMap(self)
+ return (tuple(getattr(property_holder, '_properties', ())) +
+ tuple(getattr(self, '_local_properties', ())))
def manage_historyCompare(self, rev1, rev2, REQUEST,
historyComparisonResults=''):
@@ -884,55 +873,56 @@ class Base( CopyContainer,
# for that portal_type, try to return a value ASAP
try:
property_holder = Base.aq_portal_type[aq_key]
+ except KeyError:
+ pass
+ else:
accessor = getattr(property_holder, id, None)
if type(accessor) is tuple and id not in RESERVED_TUPLE_PROPERTY:
accessor = property_holder.createAccessor(id)
return accessor
- except KeyError:
- property_holder = None
- if getattr(Base.aq_method_generating, 'aq_key', None) == aq_key:
- # We are already generating for this aq_key, return not to generate
- # again.
- return None
- # Store that we are generating for this aq_key, then when we will recurse
- # in _aq_dynamic during generation for this aq_key, we'll return to prevent
- # infinite loops. While generating for one aq_key, we will probably have to
- # generate for another aq_key, a typical example is that to generate
- # methods for a document, we'll have to generate methods for Types Tool and
- # Base Category portal.
- Base.aq_method_generating.aq_key = aq_key
+ Base.aq_method_lock.acquire()
try:
- # Proceed with property generation
- if self.isTempObject():
- # If self is a temporary object, generate methods for the base
- # document class rather than for the temporary document class.
- # Otherwise, instances of the base document class would fail
- # in calling such methods, because they are not instances of
- # the temporary document class.
- klass = klass.__bases__[0]
-
- generated = False # Prevent infinite loops
-
- # Generate class methods
- if klass not in Base.aq_method_generated:
- initializeClassDynamicProperties(self, klass)
- generated = True
-
- # Iterate until an ERP5 Site is obtained.
- portal = self.getPortalObject()
- while portal.portal_type != 'ERP5 Site':
- portal = portal.aq_parent.aq_inner.getPortalObject()
+ if aq_key in Base.aq_portal_type:
+ # Another thread generated accessors just before we acquired the lock
+ # so we must simply retry.
+ return getattr(self, id, None)
+ if aq_key in Base.aq_method_generating:
+ # We are already generating accessors for this aq_key.
+ # Return immediately to prevent infinite loops.
+ return
+ # Store that we are generating for this aq_key, because _aq_dynamic may
+ # be called recursively. A typical example is that to generate methods
+ # for a document, we'll have to generate methods for Types Tool and
+ # Base Category portal.
+ Base.aq_method_generating.append(aq_key)
+ try:
+ # Proceed with property generation
+ if self.isTempObject():
+ # If self is a temporary object, generate methods for the base
+ # document class rather than for the temporary document class.
+ # Otherwise, instances of the base document class would fail
+ # in calling such methods, because they are not instances of
+ # the temporary document class.
+ klass = klass.__bases__[0]
+
+ # Generate class methods
+ if klass not in Base.aq_method_generated:
+ initializeClassDynamicProperties(self, klass)
+
+ # Iterate until an ERP5 Site is obtained.
+ portal = self.getPortalObject()
+ while portal.portal_type != 'ERP5 Site':
+ portal = portal.aq_parent.aq_inner.getPortalObject()
- # Generate portal_type methods
- if aq_key not in Base.aq_portal_type:
+ # Generate portal_type methods
initializePortalTypeDynamicProperties(self, klass, ptype, aq_key, portal)
- generated = True
+ finally:
+ del Base.aq_method_generating[-1]
# Generate Related Accessors
if not Base.aq_related_generated:
from Utils import createRelatedValueAccessors
- generated = True
portal_types = getToolByName(portal, 'portal_types', None)
generated_bid = {}
econtext = createExpressionContext(object=self, portal=portal)
@@ -950,27 +940,25 @@ class Base( CopyContainer,
base_category_list.append(cat)
for bid in base_category_list:
if bid not in generated_bid:
- createRelatedValueAccessors(property_holder, bid)
+ createRelatedValueAccessors(None, bid)
generated_bid[bid] = 1
for ptype in portal_types.listTypeInfo():
for bid in ptype.getTypeBaseCategoryList():
if bid not in generated_bid :
- createRelatedValueAccessors(property_holder, bid)
+ createRelatedValueAccessors(None, bid)
generated_bid[bid] = 1
Base.aq_related_generated = 1
- # Always try to return something after generation
- if generated:
- # We suppose that if we reach this point
- # then it means that all code generation has succeeded
- # (no except should hide that). We can safely return None
- # if id does not exist as a dynamic property
- # Baseline: accessor generation failures should always
- # raise an exception up to the user
- return getattr(self, id, None)
+ # We suppose that if we reach this point
+ # then it means that all code generation has succeeded
+ # (no except should hide that). We can safely return None
+ # if id does not exist as a dynamic property
+ # Baseline: accessor generation failures should always
+ # raise an exception up to the user
+ return getattr(self, id, None)
finally:
- Base.aq_method_generating.aq_key = None
+ Base.aq_method_lock.release()
# Proceed with standard acquisition
return None
More information about the Erp5-report
mailing list