summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRay Strode <rstrode@redhat.com>2020-06-10 18:04:07 -0400
committerRay Strode <halfline@gmail.com>2022-10-15 18:38:32 +0000
commitc57bd80c07a4f846c4ebcc55dcd10f629319c0be (patch)
tree0cabd3ddfe287247e10779a58e1c48ff4dd72aff
parent4b421597d1885c725cbcdd44c4d04277454bee85 (diff)
downloadpygobject-c57bd80c07a4f846c4ebcc55dcd10f629319c0be.tar.gz
IntrospectionModule: handle two threads loading type at same time
If two threads are trying to load a type at exactly the same time, it's possible for two wrappers to get generated for the type. One thread will end up with the wrapper that's not blessed as the "real" one and future calls will fail. The blessed wrapper will be incomplete, and so future calls from it will fail as well. This commit adds a lock to ensure the two threads don't stomp on each others toes.
-rw-r--r--gi/module.py110
1 files changed, 58 insertions, 52 deletions
diff --git a/gi/module.py b/gi/module.py
index f9e26bc2..93b8e6a3 100644
--- a/gi/module.py
+++ b/gi/module.py
@@ -21,6 +21,7 @@
# USA
import importlib
+from threading import Lock
import gi
@@ -117,6 +118,8 @@ class IntrospectionModule(object):
if self._version is None:
self._version = repository.get_version(self._namespace)
+ self._lock = Lock()
+
def __getattr__(self, name):
info = repository.find_by_name(self._namespace, name)
if not info:
@@ -125,39 +128,41 @@ class IntrospectionModule(object):
if isinstance(info, EnumInfo):
g_type = info.get_g_type()
- wrapper = g_type.pytype
- if wrapper is None:
- if info.is_flags():
- if g_type.is_a(TYPE_FLAGS):
- wrapper = flags_add(g_type)
- else:
- assert g_type == TYPE_NONE
- wrapper = flags_register_new_gtype_and_add(info)
- else:
- if g_type.is_a(TYPE_ENUM):
- wrapper = enum_add(g_type)
+ with self._lock:
+ wrapper = g_type.pytype
+
+ if wrapper is None:
+ if info.is_flags():
+ if g_type.is_a(TYPE_FLAGS):
+ wrapper = flags_add(g_type)
+ else:
+ assert g_type == TYPE_NONE
+ wrapper = flags_register_new_gtype_and_add(info)
else:
- assert g_type == TYPE_NONE
- wrapper = enum_register_new_gtype_and_add(info)
-
- wrapper.__info__ = info
- wrapper.__module__ = 'gi.repository.' + info.get_namespace()
-
- # Don't use upper() here to avoid locale specific
- # identifier conversion (e. g. in Turkish 'i'.upper() == 'i')
- # see https://bugzilla.gnome.org/show_bug.cgi?id=649165
- ascii_upper_trans = ''.maketrans(
- 'abcdefgjhijklmnopqrstuvwxyz',
- 'ABCDEFGJHIJKLMNOPQRSTUVWXYZ')
- for value_info in info.get_values():
- value_name = value_info.get_name_unescaped().translate(ascii_upper_trans)
- setattr(wrapper, value_name, wrapper(value_info.get_value()))
- for method_info in info.get_methods():
- setattr(wrapper, method_info.__name__, method_info)
-
- if g_type != TYPE_NONE:
- g_type.pytype = wrapper
+ if g_type.is_a(TYPE_ENUM):
+ wrapper = enum_add(g_type)
+ else:
+ assert g_type == TYPE_NONE
+ wrapper = enum_register_new_gtype_and_add(info)
+
+ wrapper.__info__ = info
+ wrapper.__module__ = 'gi.repository.' + info.get_namespace()
+
+ # Don't use upper() here to avoid locale specific
+ # identifier conversion (e. g. in Turkish 'i'.upper() == 'i')
+ # see https://bugzilla.gnome.org/show_bug.cgi?id=649165
+ ascii_upper_trans = ''.maketrans(
+ 'abcdefgjhijklmnopqrstuvwxyz',
+ 'ABCDEFGJHIJKLMNOPQRSTUVWXYZ')
+ for value_info in info.get_values():
+ value_name = value_info.get_name_unescaped().translate(ascii_upper_trans)
+ setattr(wrapper, value_name, wrapper(value_info.get_value()))
+ for method_info in info.get_methods():
+ setattr(wrapper, method_info.__name__, method_info)
+
+ if g_type != TYPE_NONE:
+ g_type.pytype = wrapper
elif isinstance(info, RegisteredTypeInfo):
g_type = info.get_g_type()
@@ -188,27 +193,28 @@ class IntrospectionModule(object):
else:
raise NotImplementedError(info)
- # Check if there is already a Python wrapper that is not a parent class
- # of the wrapper being created. If it is a parent, it is ok to clobber
- # g_type.pytype with a new child class wrapper of the existing parent.
- # Note that the return here never occurs under normal circumstances due
- # to caching on the __dict__ itself.
- if g_type != TYPE_NONE:
- type_ = g_type.pytype
- if type_ is not None and type_ not in bases:
- self.__dict__[name] = type_
- return type_
-
- dict_ = {
- '__info__': info,
- '__module__': 'gi.repository.' + self._namespace,
- '__gtype__': g_type
- }
- wrapper = metaclass(name, bases, dict_)
-
- # Register the new Python wrapper.
- if g_type != TYPE_NONE:
- g_type.pytype = wrapper
+ with self._lock:
+ # Check if there is already a Python wrapper that is not a parent class
+ # of the wrapper being created. If it is a parent, it is ok to clobber
+ # g_type.pytype with a new child class wrapper of the existing parent.
+ # Note that the return here never occurs under normal circumstances due
+ # to caching on the __dict__ itself.
+ if g_type != TYPE_NONE:
+ type_ = g_type.pytype
+ if type_ is not None and type_ not in bases:
+ self.__dict__[name] = type_
+ return type_
+
+ dict_ = {
+ '__info__': info,
+ '__module__': 'gi.repository.' + self._namespace,
+ '__gtype__': g_type
+ }
+ wrapper = metaclass(name, bases, dict_)
+
+ # Register the new Python wrapper.
+ if g_type != TYPE_NONE:
+ g_type.pytype = wrapper
elif isinstance(info, FunctionInfo):
wrapper = info