Commit 3181d5ad authored by Arnaud Fontaine's avatar Arnaud Fontaine

ZODB Components: Fix deadlock on import lock.

Since a29456bc, only import lock is used instead of aq_method_lock, dynamic
modules locks and import lock. However, it was creating another deadlock:

when the import lock is held in one thread (for instance when trying to
perform migration to Portal Type as Classes and ZODB Property Sheets), and an
object is loaded from ZODB, a request is sent to ZEO, and this blocks until
another thread (asyncore) gets the ZEO reply and sends it back to the first
thread.

However, if the asyncore thread receives an Exception, it will tries to import
its module and thus create a deadlock as the import lock is still held by the
first thread.
parent e8d1f578
......@@ -86,7 +86,6 @@ from Products.ERP5Type.Accessor.TypeDefinition import asDate
from Products.ERP5Type.Message import Message
from Products.ERP5Type.ConsistencyMessage import ConsistencyMessage
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
from Products.ERP5Type.dynamic.import_lock import ImportLock
from zope.interface import classImplementsOnly, implementedBy
......@@ -720,7 +719,6 @@ class Base( CopyContainer,
isTempDocument = ConstantGetter('isTempDocument', value=False)
# Dynamic method acquisition system (code generation)
aq_method_lock = ImportLock()
aq_method_generated = set()
aq_method_generating = []
aq_portal_type = {}
......
......@@ -37,7 +37,7 @@ from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
from AccessControl.Permission import Permission
from Products.ERP5Type.Tool.BaseTool import BaseTool
from Products.ERP5Type.Base import Base
from Products.ERP5Type.dynamic import aq_method_lock
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from zLOG import LOG, INFO, WARNING
......@@ -147,7 +147,7 @@ class ComponentTool(BaseTool):
# class when Components are reset through aq_method_lock
import erp5.component
from Products.ERP5Type.dynamic.component_package import ComponentDynamicPackage
with Base.aq_method_lock:
with aq_method_lock:
for package in erp5.component.__dict__.itervalues():
if isinstance(package, ComponentDynamicPackage):
package.reset()
......
import threading
aq_method_lock = threading.RLock()
......@@ -28,8 +28,8 @@
##############################################################################
from types import ModuleType
from . import aq_method_lock
import sys
from Products.ERP5Type.dynamic.import_lock import ImportLock
class DynamicModule(ModuleType):
"""This module may generate new objects at runtime."""
......@@ -41,13 +41,13 @@ class DynamicModule(ModuleType):
def __init__(self, name, factory, doc=None):
super(DynamicModule, self).__init__(name, doc=doc)
self._factory = factory
self._lock = ImportLock()
def __getattr__(self, name):
if name[:2] == '__':
raise AttributeError('%r module has no attribute %r'
% (self.__name__, name))
with self._lock:
with aq_method_lock:
try:
return super(DynamicModule, self).__getattribute__(name)
except AttributeError:
......
# -*- coding: utf-8 -*-
##############################################################################
# Copyright (c) 2012 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
import imp
class ImportLock(object):
"""
This class provides the interpreter's import lock.
It is intended to use in ERP5Type.dynamic to avoid possible dead lock.
It can be used in two ways :
1) 'with' statement
lock = ImportLock()
with lock:
...
2) traditional 'try' and 'finally'
lock = ImportLock()
lock.acquire()
try:
...
finally:
lock.release()
"""
def __enter__(self):
imp.acquire_lock()
def __exit__(self, type, value, tb):
imp.release_lock()
def acquire(self):
imp.acquire_lock()
def release(self):
imp.release_lock()
......@@ -6,6 +6,7 @@ from Products.ERP5Type import Permissions
from Products.ERP5Type.Accessor.Constant import Getter as ConstantGetter
from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type.Base import Base as ERP5Base
from . import aq_method_lock
from Products.ERP5Type.Base import PropertyHolder, initializePortalTypeDynamicWorkflowMethods
from Products.ERP5Type.Utils import UpperCase
from Products.ERP5Type.Core.CategoryProperty import CategoryProperty
......@@ -319,7 +320,7 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
portal_type = klass.__name__
from Products.ERP5.ERP5Site import getSite
site = getSite()
ERP5Base.aq_method_lock.acquire()
aq_method_lock.acquire()
try:
try:
class_definition = generatePortalTypeClass(site, portal_type)
......@@ -363,7 +364,7 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
import traceback; traceback.print_exc()
raise
finally:
ERP5Base.aq_method_lock.release()
aq_method_lock.release()
def generateLazyPortalTypeClass(portal_type_name):
return PortalTypeMetaClass(portal_type_name,
......
......@@ -33,7 +33,8 @@ import inspect
import transaction
from Products.ERP5Type.mixin.temporary import TemporaryDocumentMixin
from Products.ERP5Type.Base import Base, resetRegisteredWorkflowMethod
from Products.ERP5Type.Base import resetRegisteredWorkflowMethod
from . import aq_method_lock
from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type.Utils import setDefaultClassProperties
from Products.ERP5Type import document_class_registry, mixin_class_registry
......@@ -318,7 +319,7 @@ def synchronizeDynamicModules(context, force=False):
last_sync = cookie
import erp5
with Base.aq_method_lock:
with aq_method_lock:
# Thanks to TransactionalResource, the '_bootstrapped' global variable
# is updated in a transactional way. Without it, it would be required to
# restart the instance if anything went wrong.
......
......@@ -310,6 +310,9 @@ class ComponentMixin(PropertyRecordableMixin, Base):
Initially, namespace_dict default parameter value was an empty dict to
allow checking the source code before validate, but this is completely
wrong as the object reference is kept accross each call
TODO-arnau: Not used anymore in component_package, so this could be
removed as soon as pyflakes is used instead
"""
if text_content is None:
text_content = self.getTextContent(validated_only=validated_only)
......
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