[vte] widget: Reference terminals while processing



commit de1ee80d07ef3d7da138bf7f03395685d7327d97
Author: Christian Persch <chpe src gnome org>
Date:   Sat Oct 3 11:25:33 2020 +0200

    widget: Reference terminals while processing
    
    In some language bindings, the callbacks we emit during processing
    may cause their GC to run, which could finalise a VteTerminal,
    removing it from g_active_terminals list while we're iterating
    over that list.
    
    https://gitlab.gnome.org/GNOME/vte/-/issues/270

 src/vte.cc | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)
---
diff --git a/src/vte.cc b/src/vte.cc
index f8592985..4af90581 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -10346,6 +10346,33 @@ Terminal::process(bool emit_adj_changed)
         return is_active;
 }
 
+
+/* We need to keep a reference to the terminals in the
+ * g_active_terminals list while iterating over it, since
+ * in some language bindings the callbacks we emit
+ * during processing may cause their GC to run, causing
+ * later elements in this list to be removed from the list.
+ * See issue vte#270.
+ */
+
+static void
+unref_active_terminals(GList* list)
+{
+        g_list_free_full(list, GDestroyNotify(g_object_unref));
+}
+
+static auto
+ref_active_terminals() noexcept
+{
+        GList* list = nullptr;
+        for (auto l = g_active_terminals; l != nullptr; l = l->next) {
+                auto that = reinterpret_cast<vte::terminal::Terminal*>(l->data);
+                list = g_list_prepend(list, g_object_ref(that->vte_terminal()));
+        }
+
+        return std::unique_ptr<GList, decltype(&unref_active_terminals)>{list, &unref_active_terminals};
+}
+
 /* This function is called after DISPLAY_TIMEOUT ms.
  * It makes sure initial output is never delayed by more than DISPLAY_TIMEOUT
  */
@@ -10363,6 +10390,8 @@ try
                           "Process timeout:  %d active\n",
                           g_list_length(g_active_terminals));
 
+        auto death_grip = ref_active_terminals();
+
        for (l = g_active_terminals; l != NULL; l = next) {
                auto that = reinterpret_cast<vte::terminal::Terminal*>(l->data);
                bool active;
@@ -10455,6 +10484,8 @@ update_repeat_timeout (gpointer data)
                           "Repeat timeout:  %d active\n",
                           g_list_length(g_active_terminals));
 
+        auto death_grip = ref_active_terminals();
+
        for (l = g_active_terminals; l != NULL; l = next) {
                auto that = reinterpret_cast<vte::terminal::Terminal*>(l->data);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]