details:   https://code.tryton.org/tryton/commit/120754f528d6
branch:    7.8
user:      Cédric Krier <[email protected]>
date:      Wed Nov 04 16:07:16 2026 +0100
description:
        Lock the modification and iteration of the cache dictionary of instances

        Closes #15172
        (grafted from c6520525a35a337adb4b8f5fc1ab449d9310f84a)
diffstat:

 trytond/trytond/cache.py |  36 +++++++++++++++++++++---------------
 1 files changed, 21 insertions(+), 15 deletions(-)

diffs (79 lines):

diff -r 5bcbf72945af -r 120754f528d6 trytond/trytond/cache.py
--- a/trytond/trytond/cache.py  Fri Nov 06 14:39:54 2026 +0100
+++ b/trytond/trytond/cache.py  Wed Nov 04 16:07:16 2026 +0100
@@ -87,12 +87,14 @@
     def __call__(cls, *args, **kwargs):
         inst = super().__call__(*args, **kwargs)
         assert inst._name not in BaseCache._instances, inst._name
-        BaseCache._instances[inst._name] = inst
+        with BaseCache._instances_lock:
+            BaseCache._instances[inst._name] = inst
         return inst
 
 
 class BaseCache(metaclass=_CacheMeta):
     _instances = {}
+    _instances_lock = threading.Lock()
     context_ignored_keys = {
         'client', '_request', '_check_access', '_skip_warnings',
         }
@@ -123,12 +125,13 @@
 
     @classmethod
     def stats(cls):
-        for name, inst in cls._instances.items():
-            yield {
-                'name': name,
-                'hit': inst.hit,
-                'miss': inst.miss,
-                }
+        with cls._instances_lock:
+            for name, inst in cls._instances.items():
+                yield {
+                    'name': name,
+                    'hit': inst.hit,
+                    'miss': inst.miss,
+                    }
 
     def _key(self, key):
         if self.context:
@@ -150,8 +153,9 @@
 
     @classmethod
     def clear_all(cls):
-        for inst in cls._instances.values():
-            inst.clear()
+        with cls._instances_lock:
+            for inst in cls._instances.values():
+                inst.clear()
 
     @classmethod
     def sync(cls, transaction):
@@ -250,8 +254,9 @@
 
     @classmethod
     def _clear_all(cls, dbname):
-        for inst in cls._instances.values():
-            inst._clear(dbname)
+        with cls._instances_lock:
+            for inst in cls._instances.values():
+                inst._clear(dbname)
 
     @classmethod
     def sync(cls, transaction):
@@ -388,10 +393,11 @@
             finally:
                 database.put_connection(conn)
             listener.join()
-        for inst in cls._instances.values():
-            inst._timestamp.pop(dbname, None)
-            inst._database_cache.pop(dbname, None)
-            inst._transaction_lower.pop(dbname, None)
+        with cls._instances_lock:
+            for inst in cls._instances.values():
+                inst._timestamp.pop(dbname, None)
+                inst._database_cache.pop(dbname, None)
+                inst._transaction_lower.pop(dbname, None)
 
     @classmethod
     def refresh_pool(cls, transaction):

Reply via email to