From 3871e811aa50d44fee03c71330e530c4a493e9a3 Mon Sep 17 00:00:00 2001 From: Yariv Livay Date: Mon, 27 Oct 2025 21:29:50 +0200 Subject: [PATCH 1/2] Avoid modifying _instances while iterating over it in get_rule by mutating it in a lock --- proto/marshal/marshal.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/proto/marshal/marshal.py b/proto/marshal/marshal.py index d278421a..dd98ef85 100644 --- a/proto/marshal/marshal.py +++ b/proto/marshal/marshal.py @@ -14,6 +14,7 @@ import abc import enum +import threading from google.protobuf import message from google.protobuf import duration_pb2 @@ -256,6 +257,7 @@ class Marshal(BaseMarshal): """ _instances = {} + _instances_lock = threading.Lock() def __new__(cls, *, name: str): """Create a marshal instance. @@ -265,9 +267,13 @@ def __new__(cls, *, name: str): marshals with the same ``name`` argument will provide the same marshal each time. """ - klass = cls._instances.get(name) - if klass is None: - klass = cls._instances[name] = super().__new__(cls) + with cls._instances_lock: + klass = cls._instances.get(name) + if klass is None: + klass = super().__new__(cls) + instances_copy = cls._instances.copy() + instances_copy[name] = klass + cls._instances = instances_copy return klass From 774fb89c005aa7b665636db6aeb23c3269ed3ba1 Mon Sep 17 00:00:00 2001 From: Yariv Livay Date: Mon, 27 Oct 2025 21:37:11 +0200 Subject: [PATCH 2/2] optimistic locking --- proto/marshal/marshal.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/proto/marshal/marshal.py b/proto/marshal/marshal.py index dd98ef85..37ae137e 100644 --- a/proto/marshal/marshal.py +++ b/proto/marshal/marshal.py @@ -267,13 +267,15 @@ def __new__(cls, *, name: str): marshals with the same ``name`` argument will provide the same marshal each time. """ - with cls._instances_lock: - klass = cls._instances.get(name) - if klass is None: - klass = super().__new__(cls) - instances_copy = cls._instances.copy() - instances_copy[name] = klass - cls._instances = instances_copy + klass = cls._instances.get(name) + if klass is None: + with cls._instances_lock: + klass = cls._instances.get(name) + if klass is None: + klass = super().__new__(cls) + instances_copy = cls._instances.copy() + instances_copy[name] = klass + cls._instances = instances_copy return klass