[pygobject/wip/type-object-concurrency-fix: 50/50] IntrospectionModule: handle two threads loading type at same time
- From: Ray Strode <halfline src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pygobject/wip/type-object-concurrency-fix: 50/50] IntrospectionModule: handle two threads loading type at same time
- Date: Wed, 5 May 2021 13:33:12 +0000 (UTC)
commit 1212d1cd3d4b47294770408a7abd18bc4c578a64
Author: Ray Strode <rstrode redhat com>
Date: Wed Jun 10 18:04:07 2020 -0400
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.
gi/module.py | 110 +++++++++++++++++++++++++++++++----------------------------
1 file 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
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]