diff --git a/SOURCES/0001-IntrospectionModule-handle-two-threads-loading-type-.patch b/SOURCES/0001-IntrospectionModule-handle-two-threads-loading-type-.patch new file mode 100644 index 0000000..d279132 --- /dev/null +++ b/SOURCES/0001-IntrospectionModule-handle-two-threads-loading-type-.patch @@ -0,0 +1,158 @@ +From eb791e7cca0998bc75e0b3f7e8ecf2672c96d7f8 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 10 Jun 2020 18:04:07 -0400 +Subject: [PATCH] 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(-) + +--- a/gi/module.py 2020-06-10 18:09:19.292409072 -0400 ++++ b/gi/module.py 2020-06-10 18:11:24.727045466 -0400 +@@ -24,6 +24,7 @@ from __future__ import absolute_import + + import sys + import importlib ++from threading import Lock + + _have_py3 = (sys.version_info[0] >= 3) + +@@ -131,6 +132,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: +@@ -139,39 +142,40 @@ class IntrospectionModule(object): + + if isinstance(info, EnumInfo): + g_type = info.get_g_type() +- wrapper = g_type.pytype ++ 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: +- if g_type.is_a(TYPE_ENUM): +- wrapper = enum_add(g_type) ++ 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.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 ++ if g_type != TYPE_NONE: ++ g_type.pytype = wrapper + + elif isinstance(info, RegisteredTypeInfo): + g_type = info.get_g_type() +@@ -202,27 +206,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 diff --git a/SPECS/pygobject3.spec b/SPECS/pygobject3.spec index f3d3437..e8cd524 100644 --- a/SPECS/pygobject3.spec +++ b/SPECS/pygobject3.spec @@ -10,7 +10,7 @@ Name: pygobject3 Version: 3.28.3 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Python bindings for GObject Introspection License: LGPLv2+ and MIT @@ -39,6 +39,8 @@ BuildRequires: dejavu-serif-fonts BuildRequires: dbus-x11 %endif # with_check +Patch0: 0001-IntrospectionModule-handle-two-threads-loading-type-.patch + %description The %{name} package provides a convenient wrapper for the GObject library for use in Python programs. @@ -72,6 +74,7 @@ This package contains files required to embed PyGObject %prep %setup -q -n pygobject-%{version} +%patch0 -p1 -b .lockablock find -name '*.py' | xargs sed -i '1s|^#!python|#!%{__python3}|' @@ -126,6 +129,11 @@ xvfb-run make DESTDIR=$RPM_BUILD_ROOT check V=1 %{_libdir}/pkgconfig/pygobject-3.0.pc %changelog +* Thu Jun 11 2020 Ray Strode - 3.28.3-2 +- Add lock to avoid two type object wrappers getting generated at + the same time in multi-threaded programs. + Resolves: #1844578 + * Fri Aug 10 2018 Kalev Lember - 3.28.3-1 - Update to 3.28.3