From f22f73bcbdb6510169c8b7c7c3fea842750c6fe5 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 18 Aug 2022 21:05:33 +0200
Subject: Handle the Python Global Interpreter Lock with more care.

---
 plugins/pychrysalide/analysis/type.c | 30 ++++++++++++++++
 plugins/pychrysalide/core.c          | 67 +++++++++++-------------------------
 plugins/pychrysalide/core.h          |  3 --
 plugins/pychrysalide/format/symbol.c |  5 +++
 plugins/pychrysalide/gui/item.c      | 60 +++++++++++---------------------
 plugins/pychrysalide/helpers.c       |  2 ++
 6 files changed, 77 insertions(+), 90 deletions(-)

diff --git a/plugins/pychrysalide/analysis/type.c b/plugins/pychrysalide/analysis/type.c
index 86a0ffb..357d381 100644
--- a/plugins/pychrysalide/analysis/type.c
+++ b/plugins/pychrysalide/analysis/type.c
@@ -220,6 +220,7 @@ static void py_data_type_init_gclass(GDataTypeClass *class, gpointer unused)
 static guint py_data_type_hash_wrapper(const GDataType *type)
 {
     guint result;                           /* Empreinte à renvoyer        */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
     PyObject *pyret;                        /* Bilan de consultation       */
 
@@ -234,6 +235,8 @@ static guint py_data_type_hash_wrapper(const GDataType *type)
 
     result = 0;
 
+    gstate = PyGILState_Ensure();
+
     pyobj = pygobject_new(G_OBJECT(type));
 
     if (has_python_method(pyobj, "_hash"))
@@ -252,6 +255,8 @@ static guint py_data_type_hash_wrapper(const GDataType *type)
 
     Py_DECREF(pyobj);
 
+    PyGILState_Release(gstate);
+
     return result;
 
 }
@@ -272,6 +277,7 @@ static guint py_data_type_hash_wrapper(const GDataType *type)
 static GDataType *py_data_type_dup_wrapper(const GDataType *type)
 {
     GDataType *result;                      /* Copie à retourner           */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
     PyObject *pyret;                        /* Bilan de consultation       */
 
@@ -287,6 +293,8 @@ static GDataType *py_data_type_dup_wrapper(const GDataType *type)
 
     result = NULL;
 
+    gstate = PyGILState_Ensure();
+
     pyobj = pygobject_new(G_OBJECT(type));
 
     if (has_python_method(pyobj, "_dup"))
@@ -308,6 +316,8 @@ static GDataType *py_data_type_dup_wrapper(const GDataType *type)
 
     Py_DECREF(pyobj);
 
+    PyGILState_Release(gstate);
+
     return result;
 
 }
@@ -329,6 +339,7 @@ static GDataType *py_data_type_dup_wrapper(const GDataType *type)
 static char *py_data_type_to_string_wrapper(const GDataType *type, bool include)
 {
     char *result;                           /* Etiquette à retourner       */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
     PyObject *arg;                          /* Version Python de l'argument*/
     PyObject *args;                         /* Arguments pour l'appel      */
@@ -349,6 +360,8 @@ static char *py_data_type_to_string_wrapper(const GDataType *type, bool include)
 
     result = NULL;
 
+    gstate = PyGILState_Ensure();
+
     pyobj = pygobject_new(G_OBJECT(type));
 
     if (has_python_method(pyobj, "_to_string"))
@@ -375,6 +388,8 @@ static char *py_data_type_to_string_wrapper(const GDataType *type, bool include)
 
     Py_DECREF(pyobj);
 
+    PyGILState_Release(gstate);
+
     return result;
 
 }
@@ -395,6 +410,7 @@ static char *py_data_type_to_string_wrapper(const GDataType *type, bool include)
 static bool py_data_type_handle_namespaces_wrapper(const GDataType *type)
 {
     bool result;                            /* Bilan à retourner           */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
     PyObject *pyret;                        /* Bilan de consultation       */
 
@@ -411,6 +427,8 @@ static bool py_data_type_handle_namespaces_wrapper(const GDataType *type)
 
     result = true;
 
+    gstate = PyGILState_Ensure();
+
     pyobj = pygobject_new(G_OBJECT(type));
 
     if (has_python_method(pyobj, "_handle_namespaces"))
@@ -425,6 +443,8 @@ static bool py_data_type_handle_namespaces_wrapper(const GDataType *type)
 
     Py_DECREF(pyobj);
 
+    PyGILState_Release(gstate);
+
     return result;
 
 }
@@ -445,6 +465,7 @@ static bool py_data_type_handle_namespaces_wrapper(const GDataType *type)
 static bool py_data_type_is_pointer_wrapper(const GDataType *type)
 {
     bool result;                            /* Bilan à retourner           */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
     PyObject *pyret;                        /* Bilan de consultation       */
 
@@ -461,6 +482,8 @@ static bool py_data_type_is_pointer_wrapper(const GDataType *type)
 
     result = false;
 
+    gstate = PyGILState_Ensure();
+
     pyobj = pygobject_new(G_OBJECT(type));
 
     if (has_python_method(pyobj, "_is_pointer"))
@@ -475,6 +498,8 @@ static bool py_data_type_is_pointer_wrapper(const GDataType *type)
 
     Py_DECREF(pyobj);
 
+    PyGILState_Release(gstate);
+
     return result;
 
 }
@@ -495,6 +520,7 @@ static bool py_data_type_is_pointer_wrapper(const GDataType *type)
 static bool py_data_type_is_reference_wrapper(const GDataType *type)
 {
     bool result;                            /* Bilan à retourner           */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
     PyObject *pyret;                        /* Bilan de consultation       */
 
@@ -511,6 +537,8 @@ static bool py_data_type_is_reference_wrapper(const GDataType *type)
 
     result = false;
 
+    gstate = PyGILState_Ensure();
+
     pyobj = pygobject_new(G_OBJECT(type));
 
     if (has_python_method(pyobj, "_is_reference"))
@@ -525,6 +553,8 @@ static bool py_data_type_is_reference_wrapper(const GDataType *type)
 
     Py_DECREF(pyobj);
 
+    PyGILState_Release(gstate);
+
     return result;
 
 }
diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c
index ef119fb..c062d15 100644
--- a/plugins/pychrysalide/core.c
+++ b/plugins/pychrysalide/core.c
@@ -85,9 +85,6 @@ static bool _standalone = true;
 /* Réceptacle pour le chargement forcé */
 static PyObject *_chrysalide_module = NULL;
 
-/* Conservation des informations du thread principal */
-static PyThreadState *_main_tstate = NULL;
-
 
 /* Fournit la révision du programme global. */
 static PyObject *py_chrysalide_revision(PyObject *, PyObject *);
@@ -656,7 +653,6 @@ static void load_python_plugins(GPluginModule *plugin)
     struct dirent *entry;                   /* Elément trouvé              */
     char *modname;                          /* Nom du module pour Python   */
     char *filename;                         /* Chemin d'accès reconstruit  */
-    PyThreadState *tstate;                  /* Contexte d'environnement    */
     GPluginModule *pyplugin;                /* Lien vers un grffon Python  */
     bool status;                            /* Bilan d'une opération       */
     GGenConfig *config;                     /* Configuration à charger     */
@@ -736,17 +732,8 @@ static void load_python_plugins(GPluginModule *plugin)
             filename = stradd(filename, G_DIR_SEPARATOR_S);
             filename = stradd(filename, entry->d_name);
 
-            if (!_standalone)
-            {
-                tstate = get_pychrysalide_main_tstate();
-                PyEval_RestoreThread(tstate);
-            }
-
             pyplugin = g_python_plugin_new(modname, filename);
 
-            if (!_standalone)
-                PyEval_SaveThread();
-
             if (pyplugin == NULL)
             {
                 g_plugin_module_log_variadic_message(plugin, LMT_ERROR, 
@@ -808,6 +795,7 @@ static void load_python_plugins(GPluginModule *plugin)
 G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
 {
     bool result;                            /* Bilan à retourner           */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     int ret;                                /* Bilan de préparatifs        */
 
     _standalone = false;
@@ -825,6 +813,8 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
 
     Py_Initialize();
 
+    gstate = PyGILState_Ensure();
+
     PySys_SetArgv(0, (wchar_t *[]) { NULL });
 
     _chrysalide_module = PyImport_ImportModule("pychrysalide");
@@ -842,9 +832,7 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
 
     result = (_chrysalide_module != NULL);
 
-    _main_tstate = PyThreadState_Get();
-
-    PyEval_ReleaseLock();
+    PyGILState_Release(gstate);
 
  cpi_done:
 
@@ -867,10 +855,16 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
 
 G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *plugin)
 {
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+
+    gstate = PyGILState_Ensure();
+
     clear_all_accesses_to_python_modules();
 
     Py_XDECREF(_chrysalide_module);
 
+    PyGILState_Release(gstate);
+
 }
 
 
@@ -911,6 +905,7 @@ static void free_native_plugin_type(PyTypeObject *type)
 
 G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin, PluginAction action)
 {
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     size_t count;                           /* Quantité de greffons chargés*/
     PyTypeObject *parent;                   /* Type Python pour greffon    */
     PyObject *module;                       /* Module à recompléter        */
@@ -922,6 +917,8 @@ G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin,
     int ret;                                /* Bilan d'un appel            */
     PyTypeObject *type;                     /* Nouveau type dynamique      */
 
+    gstate = PyGILState_Ensure();
+
     if (action == PGA_NATIVE_PLUGINS_LOADED)
     {
         /* Intégration des greffons natifs en Python */
@@ -982,6 +979,8 @@ G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin,
 
     }
 
+    PyGILState_Release(gstate);
+
 }
 
 
@@ -1002,17 +1001,13 @@ G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin,
 G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *plugin, PluginAction action, GType type)
 {
     gpointer result;                        /* Instance à retourner        */
-    PyThreadState *tstate;                  /* Contexte d'environnement    */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyTypeObject *pytype;                   /* Classe Python concernée     */
     PyObject *instance;                     /* Initialisation forcée       */
 
     result = NULL;
 
-    if (!_standalone)
-    {
-        tstate = get_pychrysalide_main_tstate();
-        PyEval_RestoreThread(tstate);
-    }
+    gstate = PyGILState_Ensure();
 
     pytype = pygobject_lookup_class(type);
 
@@ -1025,31 +1020,7 @@ G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *pl
 
     }
 
-    if (!_standalone)
-        PyEval_SaveThread();
-
-    return result;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : -                                                            *
-*                                                                             *
-*  Description : Fournit les informations du thread principal.                *
-*                                                                             *
-*  Retour      : Indications utiles à Python.                                 *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-PyThreadState *get_pychrysalide_main_tstate(void)
-{
-    PyThreadState *result;                  /* Indications à retourner     */
-
-    result = _main_tstate;
+    PyGILState_Release(gstate);
 
     return result;
 
@@ -1078,6 +1049,8 @@ void log_pychrysalide_exception(const char *prefix, ...)
     PyObject *err_string;                   /* Description Python d'erreur */
     const char *err_msg;                    /* Représentation humaine      */
 
+    assert(PyGILState_Check() == 1);
+
     if (PyErr_Occurred())
     {
         /* Base de la communication */
diff --git a/plugins/pychrysalide/core.h b/plugins/pychrysalide/core.h
index 6a7b9d1..88c4140 100644
--- a/plugins/pychrysalide/core.h
+++ b/plugins/pychrysalide/core.h
@@ -55,9 +55,6 @@ G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *, Plugin
 /* Crée une instance à partir d'un type dynamique externe. */
 G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *, PluginAction, GType);
 
-/* Fournit les informations du thread principal. */
-PyThreadState *get_pychrysalide_main_tstate(void);
-
 /* Présente dans le journal une exception survenue. */
 void log_pychrysalide_exception(const char *, ...);
 
diff --git a/plugins/pychrysalide/format/symbol.c b/plugins/pychrysalide/format/symbol.c
index e2f2dda..1559b9a 100644
--- a/plugins/pychrysalide/format/symbol.c
+++ b/plugins/pychrysalide/format/symbol.c
@@ -215,6 +215,7 @@ static void py_binary_symbol_init_gclass(GBinSymbolClass *class, gpointer unused
 static char *py_binary_symbol_get_label_wrapper(const GBinSymbol *symbol)
 {
     char *result;                           /* Etiquette à retourner       */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
     PyObject *pyret;                        /* Bilan de consultation       */
 
@@ -229,6 +230,8 @@ static char *py_binary_symbol_get_label_wrapper(const GBinSymbol *symbol)
 
     result = NULL;
 
+    gstate = PyGILState_Ensure();
+
     pyobj = pygobject_new(G_OBJECT(symbol));
 
     if (has_python_method(pyobj, "_get_label"))
@@ -247,6 +250,8 @@ static char *py_binary_symbol_get_label_wrapper(const GBinSymbol *symbol)
 
     Py_DECREF(pyobj);
 
+    PyGILState_Release(gstate);
+
     return result;
 
 }
diff --git a/plugins/pychrysalide/gui/item.c b/plugins/pychrysalide/gui/item.c
index 2046587..eb140fb 100644
--- a/plugins/pychrysalide/gui/item.c
+++ b/plugins/pychrysalide/gui/item.c
@@ -291,19 +291,16 @@ static GtkWidget *py_editor_item_get_widget_wrapper(const GEditorItem *item)
 
 static void py_editor_item_change_content_wrapper(GEditorItem *item, GLoadedContent *old, GLoadedContent *new)
 {
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
-    PyThreadState *tstate;                  /* Contexte d'environnement    */
     PyObject *pyold;                        /* Conversion ou None          */
     PyObject *pynew;                        /* Conversion ou None          */
     PyObject *args;                         /* Arguments pour l'appel      */
     PyObject *pyret;                        /* Retour de Python            */
 
-    pyobj = pygobject_new(G_OBJECT(item));
-
-    tstate = get_pychrysalide_main_tstate();
+    gstate = PyGILState_Ensure();
 
-    if (tstate != NULL)
-        PyEval_RestoreThread(tstate);
+    pyobj = pygobject_new(G_OBJECT(item));
 
     if (has_python_method(pyobj, "_change_content"))
     {
@@ -334,8 +331,7 @@ static void py_editor_item_change_content_wrapper(GEditorItem *item, GLoadedCont
 
     }
 
-    if (tstate != NULL)
-        PyEval_SaveThread();
+    PyGILState_Release(gstate);
 
 }
 
@@ -356,19 +352,16 @@ static void py_editor_item_change_content_wrapper(GEditorItem *item, GLoadedCont
 
 static void py_editor_item_change_view_wrapper(GEditorItem *item, GLoadedPanel *old, GLoadedPanel *new)
 {
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
-    PyThreadState *tstate;                  /* Contexte d'environnement    */
     PyObject *pyold;                        /* Conversion ou None          */
     PyObject *pynew;                        /* Conversion ou None          */
     PyObject *args;                         /* Arguments pour l'appel      */
     PyObject *pyret;                        /* Retour de Python            */
 
-    pyobj = pygobject_new(G_OBJECT(item));
-
-    tstate = get_pychrysalide_main_tstate();
+    gstate = PyGILState_Ensure();
 
-    if (tstate != NULL)
-        PyEval_RestoreThread(tstate);
+    pyobj = pygobject_new(G_OBJECT(item));
 
     if (has_python_method(pyobj, "_change_view"))
     {
@@ -399,8 +392,7 @@ static void py_editor_item_change_view_wrapper(GEditorItem *item, GLoadedPanel *
 
     }
 
-    if (tstate != NULL)
-        PyEval_SaveThread();
+    PyGILState_Release(gstate);
 
 }
 
@@ -420,17 +412,14 @@ static void py_editor_item_change_view_wrapper(GEditorItem *item, GLoadedPanel *
 
 static void py_editor_item_update_view_wrapper(GEditorItem *item, GLoadedPanel *panel)
 {
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
-    PyThreadState *tstate;                  /* Contexte d'environnement    */
     PyObject *args;                         /* Arguments pour l'appel      */
     PyObject *pyret;                        /* Retour de Python            */
 
-    pyobj = pygobject_new(G_OBJECT(item));
-
-    tstate = get_pychrysalide_main_tstate();
+    gstate = PyGILState_Ensure();
 
-    if (tstate != NULL)
-        PyEval_RestoreThread(tstate);
+    pyobj = pygobject_new(G_OBJECT(item));
 
     if (has_python_method(pyobj, "_update_view"))
     {
@@ -444,8 +433,7 @@ static void py_editor_item_update_view_wrapper(GEditorItem *item, GLoadedPanel *
 
     }
 
-    if (tstate != NULL)
-        PyEval_SaveThread();
+    PyGILState_Release(gstate);
 
 }
 
@@ -466,17 +454,14 @@ static void py_editor_item_update_view_wrapper(GEditorItem *item, GLoadedPanel *
 
 static void py_editor_item_track_cursor_wrapper(GEditorItem *item, GLoadedPanel *panel, const GLineCursor *cursor)
 {
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
-    PyThreadState *tstate;                  /* Contexte d'environnement    */
     PyObject *args;                         /* Arguments pour l'appel      */
     PyObject *pyret;                        /* Retour de Python            */
 
-    pyobj = pygobject_new(G_OBJECT(item));
-
-    tstate = get_pychrysalide_main_tstate();
+    gstate = PyGILState_Ensure();
 
-    if (tstate != NULL)
-        PyEval_RestoreThread(tstate);
+    pyobj = pygobject_new(G_OBJECT(item));
 
     if (has_python_method(pyobj, "_track_cursor"))
     {
@@ -491,8 +476,7 @@ static void py_editor_item_track_cursor_wrapper(GEditorItem *item, GLoadedPanel
 
     }
 
-    if (tstate != NULL)
-        PyEval_SaveThread();
+    PyGILState_Release(gstate);
 
 }
 
@@ -513,17 +497,14 @@ static void py_editor_item_track_cursor_wrapper(GEditorItem *item, GLoadedPanel
 
 static void py_editor_item_focus_cursor_wrapper(GEditorItem *item, GLoadedContent *content, const GLineCursor *cursor)
 {
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
-    PyThreadState *tstate;                  /* Contexte d'environnement    */
     PyObject *args;                         /* Arguments pour l'appel      */
     PyObject *pyret;                        /* Retour de Python            */
 
-    pyobj = pygobject_new(G_OBJECT(item));
-
-    tstate = get_pychrysalide_main_tstate();
+    gstate = PyGILState_Ensure();
 
-    if (tstate != NULL)
-        PyEval_RestoreThread(tstate);
+    pyobj = pygobject_new(G_OBJECT(item));
 
     if (has_python_method(pyobj, "_focus_cursor"))
     {
@@ -538,8 +519,7 @@ static void py_editor_item_focus_cursor_wrapper(GEditorItem *item, GLoadedConten
 
     }
 
-    if (tstate != NULL)
-        PyEval_SaveThread();
+    PyGILState_Release(gstate);
 
 }
 
diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c
index 8e7c10c..e0a3340 100644
--- a/plugins/pychrysalide/helpers.c
+++ b/plugins/pychrysalide/helpers.c
@@ -234,6 +234,8 @@ PyObject *run_python_method(PyObject *module, const char *method, PyObject *args
     PyObject *traceback;                    /* Pile d'appels de l'exception*/
     PyObject *refmsg;                       /* Message de référence        */
 
+    assert(PyGILState_Check() == 1);
+
     /* Exécution */
 
     result = NULL;
-- 
cgit v0.11.2-87-g4458