|
|
261f90 |
From eb791e7cca0998bc75e0b3f7e8ecf2672c96d7f8 Mon Sep 17 00:00:00 2001
|
|
|
261f90 |
From: Ray Strode <rstrode@redhat.com>
|
|
|
261f90 |
Date: Wed, 10 Jun 2020 18:04:07 -0400
|
|
|
261f90 |
Subject: [PATCH] IntrospectionModule: handle two threads loading type at same
|
|
|
261f90 |
time
|
|
|
261f90 |
|
|
|
261f90 |
If two threads are trying to load a type at exactly the same time,
|
|
|
261f90 |
it's possible for two wrappers to get generated for the type.
|
|
|
261f90 |
One thread will end up with the wrapper that's not blessed as the
|
|
|
261f90 |
"real" one and future calls will fail. The blessed wrapper will
|
|
|
261f90 |
be incomplete, and so future calls from it will fail as well.
|
|
|
261f90 |
|
|
|
261f90 |
This commit adds a lock to ensure the two threads don't stomp
|
|
|
261f90 |
on each others toes.
|
|
|
261f90 |
---
|
|
|
261f90 |
gi/module.py | 110 +++++++++++++++++++++++++++------------------------
|
|
|
261f90 |
1 file changed, 58 insertions(+), 52 deletions(-)
|
|
|
261f90 |
|
|
|
261f90 |
--- a/gi/module.py 2020-06-10 18:09:19.292409072 -0400
|
|
|
261f90 |
+++ b/gi/module.py 2020-06-10 18:11:24.727045466 -0400
|
|
|
261f90 |
@@ -24,6 +24,7 @@ from __future__ import absolute_import
|
|
|
261f90 |
|
|
|
261f90 |
import sys
|
|
|
261f90 |
import importlib
|
|
|
261f90 |
+from threading import Lock
|
|
|
261f90 |
|
|
|
261f90 |
_have_py3 = (sys.version_info[0] >= 3)
|
|
|
261f90 |
|
|
|
261f90 |
@@ -131,6 +132,8 @@ class IntrospectionModule(object):
|
|
|
261f90 |
if self._version is None:
|
|
|
261f90 |
self._version = repository.get_version(self._namespace)
|
|
|
261f90 |
|
|
|
261f90 |
+ self._lock = Lock()
|
|
|
261f90 |
+
|
|
|
261f90 |
def __getattr__(self, name):
|
|
|
261f90 |
info = repository.find_by_name(self._namespace, name)
|
|
|
261f90 |
if not info:
|
|
|
261f90 |
@@ -139,39 +142,40 @@ class IntrospectionModule(object):
|
|
|
261f90 |
|
|
|
261f90 |
if isinstance(info, EnumInfo):
|
|
|
261f90 |
g_type = info.get_g_type()
|
|
|
261f90 |
- wrapper = g_type.pytype
|
|
|
261f90 |
+ with self._lock:
|
|
|
261f90 |
+ wrapper = g_type.pytype
|
|
|
261f90 |
|
|
|
261f90 |
- if wrapper is None:
|
|
|
261f90 |
- if info.is_flags():
|
|
|
261f90 |
- if g_type.is_a(TYPE_FLAGS):
|
|
|
261f90 |
- wrapper = flags_add(g_type)
|
|
|
261f90 |
- else:
|
|
|
261f90 |
- assert g_type == TYPE_NONE
|
|
|
261f90 |
- wrapper = flags_register_new_gtype_and_add(info)
|
|
|
261f90 |
- else:
|
|
|
261f90 |
- if g_type.is_a(TYPE_ENUM):
|
|
|
261f90 |
- wrapper = enum_add(g_type)
|
|
|
261f90 |
+ if wrapper is None:
|
|
|
261f90 |
+ if info.is_flags():
|
|
|
261f90 |
+ if g_type.is_a(TYPE_FLAGS):
|
|
|
261f90 |
+ wrapper = flags_add(g_type)
|
|
|
261f90 |
+ else:
|
|
|
261f90 |
+ assert g_type == TYPE_NONE
|
|
|
261f90 |
+ wrapper = flags_register_new_gtype_and_add(info)
|
|
|
261f90 |
else:
|
|
|
261f90 |
- assert g_type == TYPE_NONE
|
|
|
261f90 |
- wrapper = enum_register_new_gtype_and_add(info)
|
|
|
261f90 |
-
|
|
|
261f90 |
- wrapper.__info__ = info
|
|
|
261f90 |
- wrapper.__module__ = 'gi.repository.' + info.get_namespace()
|
|
|
261f90 |
-
|
|
|
261f90 |
- # Don't use upper() here to avoid locale specific
|
|
|
261f90 |
- # identifier conversion (e. g. in Turkish 'i'.upper() == 'i')
|
|
|
261f90 |
- # see https://bugzilla.gnome.org/show_bug.cgi?id=649165
|
|
|
261f90 |
- ascii_upper_trans = maketrans(
|
|
|
261f90 |
- 'abcdefgjhijklmnopqrstuvwxyz',
|
|
|
261f90 |
- 'ABCDEFGJHIJKLMNOPQRSTUVWXYZ')
|
|
|
261f90 |
- for value_info in info.get_values():
|
|
|
261f90 |
- value_name = value_info.get_name_unescaped().translate(ascii_upper_trans)
|
|
|
261f90 |
- setattr(wrapper, value_name, wrapper(value_info.get_value()))
|
|
|
261f90 |
- for method_info in info.get_methods():
|
|
|
261f90 |
- setattr(wrapper, method_info.__name__, method_info)
|
|
|
261f90 |
+ if g_type.is_a(TYPE_ENUM):
|
|
|
261f90 |
+ wrapper = enum_add(g_type)
|
|
|
261f90 |
+ else:
|
|
|
261f90 |
+ assert g_type == TYPE_NONE
|
|
|
261f90 |
+ wrapper = enum_register_new_gtype_and_add(info)
|
|
|
261f90 |
+
|
|
|
261f90 |
+ wrapper.__info__ = info
|
|
|
261f90 |
+ wrapper.__module__ = 'gi.repository.' + info.get_namespace()
|
|
|
261f90 |
+
|
|
|
261f90 |
+ # Don't use upper() here to avoid locale specific
|
|
|
261f90 |
+ # identifier conversion (e. g. in Turkish 'i'.upper() == 'i')
|
|
|
261f90 |
+ # see https://bugzilla.gnome.org/show_bug.cgi?id=649165
|
|
|
261f90 |
+ ascii_upper_trans = maketrans(
|
|
|
261f90 |
+ 'abcdefgjhijklmnopqrstuvwxyz',
|
|
|
261f90 |
+ 'ABCDEFGJHIJKLMNOPQRSTUVWXYZ')
|
|
|
261f90 |
+ for value_info in info.get_values():
|
|
|
261f90 |
+ value_name = value_info.get_name_unescaped().translate(ascii_upper_trans)
|
|
|
261f90 |
+ setattr(wrapper, value_name, wrapper(value_info.get_value()))
|
|
|
261f90 |
+ for method_info in info.get_methods():
|
|
|
261f90 |
+ setattr(wrapper, method_info.__name__, method_info)
|
|
|
261f90 |
|
|
|
261f90 |
- if g_type != TYPE_NONE:
|
|
|
261f90 |
- g_type.pytype = wrapper
|
|
|
261f90 |
+ if g_type != TYPE_NONE:
|
|
|
261f90 |
+ g_type.pytype = wrapper
|
|
|
261f90 |
|
|
|
261f90 |
elif isinstance(info, RegisteredTypeInfo):
|
|
|
261f90 |
g_type = info.get_g_type()
|
|
|
261f90 |
@@ -202,27 +206,28 @@ class IntrospectionModule(object):
|
|
|
261f90 |
else:
|
|
|
261f90 |
raise NotImplementedError(info)
|
|
|
261f90 |
|
|
|
261f90 |
- # Check if there is already a Python wrapper that is not a parent class
|
|
|
261f90 |
- # of the wrapper being created. If it is a parent, it is ok to clobber
|
|
|
261f90 |
- # g_type.pytype with a new child class wrapper of the existing parent.
|
|
|
261f90 |
- # Note that the return here never occurs under normal circumstances due
|
|
|
261f90 |
- # to caching on the __dict__ itself.
|
|
|
261f90 |
- if g_type != TYPE_NONE:
|
|
|
261f90 |
- type_ = g_type.pytype
|
|
|
261f90 |
- if type_ is not None and type_ not in bases:
|
|
|
261f90 |
- self.__dict__[name] = type_
|
|
|
261f90 |
- return type_
|
|
|
261f90 |
-
|
|
|
261f90 |
- dict_ = {
|
|
|
261f90 |
- '__info__': info,
|
|
|
261f90 |
- '__module__': 'gi.repository.' + self._namespace,
|
|
|
261f90 |
- '__gtype__': g_type
|
|
|
261f90 |
- }
|
|
|
261f90 |
- wrapper = metaclass(name, bases, dict_)
|
|
|
261f90 |
-
|
|
|
261f90 |
- # Register the new Python wrapper.
|
|
|
261f90 |
- if g_type != TYPE_NONE:
|
|
|
261f90 |
- g_type.pytype = wrapper
|
|
|
261f90 |
+ with self._lock:
|
|
|
261f90 |
+ # Check if there is already a Python wrapper that is not a parent class
|
|
|
261f90 |
+ # of the wrapper being created. If it is a parent, it is ok to clobber
|
|
|
261f90 |
+ # g_type.pytype with a new child class wrapper of the existing parent.
|
|
|
261f90 |
+ # Note that the return here never occurs under normal circumstances due
|
|
|
261f90 |
+ # to caching on the __dict__ itself.
|
|
|
261f90 |
+ if g_type != TYPE_NONE:
|
|
|
261f90 |
+ type_ = g_type.pytype
|
|
|
261f90 |
+ if type_ is not None and type_ not in bases:
|
|
|
261f90 |
+ self.__dict__[name] = type_
|
|
|
261f90 |
+ return type_
|
|
|
261f90 |
+
|
|
|
261f90 |
+ dict_ = {
|
|
|
261f90 |
+ '__info__': info,
|
|
|
261f90 |
+ '__module__': 'gi.repository.' + self._namespace,
|
|
|
261f90 |
+ '__gtype__': g_type
|
|
|
261f90 |
+ }
|
|
|
261f90 |
+ wrapper = metaclass(name, bases, dict_)
|
|
|
261f90 |
+
|
|
|
261f90 |
+ # Register the new Python wrapper.
|
|
|
261f90 |
+ if g_type != TYPE_NONE:
|
|
|
261f90 |
+ g_type.pytype = wrapper
|
|
|
261f90 |
|
|
|
261f90 |
elif isinstance(info, FunctionInfo):
|
|
|
261f90 |
wrapper = info
|