Commit c66caa6b authored by Julien Muchembled's avatar Julien Muchembled

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.

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@38852 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 48ba5da4
...@@ -31,7 +31,7 @@ from struct import unpack ...@@ -31,7 +31,7 @@ from struct import unpack
from copy import copy from copy import copy
import warnings import warnings
import types import types
from threading import local import threading
from Products.ERP5Type.Globals import InitializeClass, DTMLFile from Products.ERP5Type.Globals import InitializeClass, DTMLFile
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
...@@ -760,8 +760,9 @@ class Base( CopyContainer, ...@@ -760,8 +760,9 @@ class Base( CopyContainer,
isTempDocument = ConstantGetter('isTempDocument', value=False) isTempDocument = ConstantGetter('isTempDocument', value=False)
# Dynamic method acquisition system (code generation) # Dynamic method acquisition system (code generation)
aq_method_lock = threading.RLock()
aq_method_generated = {} aq_method_generated = {}
aq_method_generating = local() aq_method_generating = []
aq_portal_type = {} aq_portal_type = {}
aq_related_generated = 0 aq_related_generated = 0
...@@ -833,24 +834,12 @@ class Base( CopyContainer, ...@@ -833,24 +834,12 @@ class Base( CopyContainer,
def _propertyMap(self): def _propertyMap(self):
""" Method overload - properties are now defined on the ptype """ """ 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 self._aq_dynamic('id') # Make sure aq_dynamic has been called once
if Base.aq_portal_type.has_key(aq_key): property_holder = Base.aq_portal_type.get(self._aq_key())
return tuple(list(getattr(Base.aq_portal_type[aq_key], '_properties', ())) + if property_holder is None:
list(getattr(self, '_local_properties', ())))
return ERP5PropertyManager._propertyMap(self) return ERP5PropertyManager._propertyMap(self)
return (tuple(getattr(property_holder, '_properties', ())) +
def _aq_dynamic_pmethod(self, id): tuple(getattr(self, '_local_properties', ())))
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
def manage_historyCompare(self, rev1, rev2, REQUEST, def manage_historyCompare(self, rev1, rev2, REQUEST,
historyComparisonResults=''): historyComparisonResults=''):
...@@ -884,24 +873,29 @@ class Base( CopyContainer, ...@@ -884,24 +873,29 @@ class Base( CopyContainer,
# for that portal_type, try to return a value ASAP # for that portal_type, try to return a value ASAP
try: try:
property_holder = Base.aq_portal_type[aq_key] property_holder = Base.aq_portal_type[aq_key]
except KeyError:
pass
else:
accessor = getattr(property_holder, id, None) accessor = getattr(property_holder, id, None)
if type(accessor) is tuple and id not in RESERVED_TUPLE_PROPERTY: if type(accessor) is tuple and id not in RESERVED_TUPLE_PROPERTY:
accessor = property_holder.createAccessor(id) accessor = property_holder.createAccessor(id)
return accessor 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 Base.aq_method_lock.acquire()
# in _aq_dynamic during generation for this aq_key, we'll return to prevent try:
# infinite loops. While generating for one aq_key, we will probably have to if aq_key in Base.aq_portal_type:
# generate for another aq_key, a typical example is that to generate # Another thread generated accessors just before we acquired the lock
# methods for a document, we'll have to generate methods for Types Tool and # 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 Category portal.
Base.aq_method_generating.aq_key = aq_key Base.aq_method_generating.append(aq_key)
try: try:
# Proceed with property generation # Proceed with property generation
if self.isTempObject(): if self.isTempObject():
...@@ -912,12 +906,9 @@ class Base( CopyContainer, ...@@ -912,12 +906,9 @@ class Base( CopyContainer,
# the temporary document class. # the temporary document class.
klass = klass.__bases__[0] klass = klass.__bases__[0]
generated = False # Prevent infinite loops
# Generate class methods # Generate class methods
if klass not in Base.aq_method_generated: if klass not in Base.aq_method_generated:
initializeClassDynamicProperties(self, klass) initializeClassDynamicProperties(self, klass)
generated = True
# Iterate until an ERP5 Site is obtained. # Iterate until an ERP5 Site is obtained.
portal = self.getPortalObject() portal = self.getPortalObject()
...@@ -925,14 +916,13 @@ class Base( CopyContainer, ...@@ -925,14 +916,13 @@ class Base( CopyContainer,
portal = portal.aq_parent.aq_inner.getPortalObject() portal = portal.aq_parent.aq_inner.getPortalObject()
# Generate portal_type methods # Generate portal_type methods
if aq_key not in Base.aq_portal_type:
initializePortalTypeDynamicProperties(self, klass, ptype, aq_key, portal) initializePortalTypeDynamicProperties(self, klass, ptype, aq_key, portal)
generated = True finally:
del Base.aq_method_generating[-1]
# Generate Related Accessors # Generate Related Accessors
if not Base.aq_related_generated: if not Base.aq_related_generated:
from Utils import createRelatedValueAccessors from Utils import createRelatedValueAccessors
generated = True
portal_types = getToolByName(portal, 'portal_types', None) portal_types = getToolByName(portal, 'portal_types', None)
generated_bid = {} generated_bid = {}
econtext = createExpressionContext(object=self, portal=portal) econtext = createExpressionContext(object=self, portal=portal)
...@@ -950,18 +940,16 @@ class Base( CopyContainer, ...@@ -950,18 +940,16 @@ class Base( CopyContainer,
base_category_list.append(cat) base_category_list.append(cat)
for bid in base_category_list: for bid in base_category_list:
if bid not in generated_bid: if bid not in generated_bid:
createRelatedValueAccessors(property_holder, bid) createRelatedValueAccessors(None, bid)
generated_bid[bid] = 1 generated_bid[bid] = 1
for ptype in portal_types.listTypeInfo(): for ptype in portal_types.listTypeInfo():
for bid in ptype.getTypeBaseCategoryList(): for bid in ptype.getTypeBaseCategoryList():
if bid not in generated_bid : if bid not in generated_bid :
createRelatedValueAccessors(property_holder, bid) createRelatedValueAccessors(None, bid)
generated_bid[bid] = 1 generated_bid[bid] = 1
Base.aq_related_generated = 1 Base.aq_related_generated = 1
# Always try to return something after generation
if generated:
# We suppose that if we reach this point # We suppose that if we reach this point
# then it means that all code generation has succeeded # then it means that all code generation has succeeded
# (no except should hide that). We can safely return None # (no except should hide that). We can safely return None
...@@ -970,7 +958,7 @@ class Base( CopyContainer, ...@@ -970,7 +958,7 @@ class Base( CopyContainer,
# raise an exception up to the user # raise an exception up to the user
return getattr(self, id, None) return getattr(self, id, None)
finally: finally:
Base.aq_method_generating.aq_key = None Base.aq_method_lock.release()
# Proceed with standard acquisition # Proceed with standard acquisition
return None return None
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment