Vo Minh Thu (OpenERP) has proposed merging 
lp:~openerp-dev/openobject-server/trunk-registry-lock-vmt into 
lp:openobject-server.

Requested reviews:
  OpenERP Core Team (openerp)

For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-server/trunk-registry-lock-vmt/+merge/83602
-- 
https://code.launchpad.net/~openerp-dev/openobject-server/trunk-registry-lock-vmt/+merge/83602
Your team OpenERP R&D Team is subscribed to branch 
lp:~openerp-dev/openobject-server/trunk-registry-lock-vmt.
=== modified file 'openerp/cron.py'
--- openerp/cron.py	2011-09-28 23:54:09 +0000
+++ openerp/cron.py	2011-11-28 13:15:27 +0000
@@ -177,8 +177,11 @@
             if canceled:
                 continue
             del _wakeup_by_db[db_name]
-            registry = openerp.pooler.get_pool(db_name)
-            if not registry._init:
+            # The master cron thread never initilizes itself a new registry,
+            # so we use blocking=False and don't need to test registry._init.
+            registry = openerp.modules.registry.RegistryManager.get(
+                db_name, blocking=False)
+            if registry:
                 _logger.debug("Database '%s' wake-up! Firing multi-threaded cron job processing", db_name)
                 registry['ir.cron']._run_jobs_multithread()
     amount = MAX_SLEEP

=== modified file 'openerp/modules/registry.py'
--- openerp/modules/registry.py	2011-09-30 15:00:26 +0000
+++ openerp/modules/registry.py	2011-11-28 13:15:27 +0000
@@ -42,6 +42,7 @@
     """
 
     def __init__(self, db_name):
+        self.lock = threading.RLock()
         self.models = {} # model name/model instance mapping
         self._sql_error = {}
         self._store_function = {}
@@ -121,6 +122,17 @@
         The manager is responsible for creation and deletion of model
         registries (essentially database connection/model registry pairs).
 
+        As the OpenERP server is multi-threaded and implements a lazy-loading
+        of registries, the interaction between get() and new() has to ensure a
+        few points:
+          - One RegistryManager.new() execution at a time: all the module
+            loading code is not thread-safe so it is protected from here.
+          - It must be possible to get() an already loaded registry while
+            another registre is being loaded.
+          - get() must return a completely initialized registry (or None if
+            it must not block). The thread doing the initialization can get
+            a partially initialized registry. It can then check its _init
+            attribute.
     """
     # Mapping between db name and model registry.
     # Accessed through the methods below.
@@ -129,13 +141,21 @@
 
     @classmethod
     def get(cls, db_name, force_demo=False, status=None, update_module=False,
-            pooljobs=True):
+            pooljobs=True, blocking=True):
         """ Return a registry for a given database name."""
         try:
-            return cls.registries[db_name]
-        except KeyError:
-            return cls.new(db_name, force_demo, status,
-                           update_module, pooljobs)
+            registry = cls.registries[db_name]
+            if registry.lock.acquire(blocking):
+                registry.lock.release()
+            else:
+                registry = None
+        except KeyError, e:
+            if blocking:
+                registry = cls.new(db_name, force_demo, status,
+                               update_module, pooljobs)
+            else:
+                registry = None
+        return registry
 
     @classmethod
     def new(cls, db_name, force_demo=False, status=None,
@@ -146,29 +166,31 @@
 
         """
         import openerp.modules
+
         with cls.registries_lock:
             registry = Registry(db_name)
-
-            # Initializing a registry will call general code which will in turn
-            # call registries.get (this object) to obtain the registry being
-            # initialized. Make it available in the registries dictionary then
-            # remove it if an exception is raised.
-            cls.delete(db_name)
-            cls.registries[db_name] = registry
-            try:
-                # This should be a method on Registry
-                openerp.modules.load_modules(registry.db, force_demo, status, update_module)
-            except Exception:
-                del cls.registries[db_name]
-                raise
-
-            cr = registry.db.cursor()
-            try:
-                registry.do_parent_store(cr)
-                registry.get('ir.actions.report.xml').register_all(cr)
-                cr.commit()
-            finally:
-                cr.close()
+            with registry.lock:
+
+                # Initializing a registry will call general code which will in turn
+                # call registries.get (this object) to obtain the registry being
+                # initialized. Make it available in the registries dictionary then
+                # remove it if an exception is raised.
+                cls.delete(db_name)
+                cls.registries[db_name] = registry
+                try:
+                    # This should be a method on Registry
+                    openerp.modules.load_modules(registry.db, force_demo, status, update_module)
+                except Exception:
+                    del cls.registries[db_name]
+                    raise
+
+                cr = registry.db.cursor()
+                try:
+                    registry.do_parent_store(cr)
+                    registry.get('ir.actions.report.xml').register_all(cr)
+                    cr.commit()
+                finally:
+                    cr.close()
 
         if pooljobs:
             registry.schedule_cron_jobs()

_______________________________________________
Mailing list: https://launchpad.net/~openerp-dev-gtk
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~openerp-dev-gtk
More help   : https://help.launchpad.net/ListHelp

Reply via email to