[Spice-devel] [spice-gtk 2/3] coroutine: Track idle notification/emission in struct coroutine

Christophe Fergeau cfergeau at redhat.com
Mon Feb 23 02:55:47 PST 2015


This allows to catch situations when we try to reinit the coroutine
using coroutine_init() while a signal/notify idle is pending.

This can actually happen with cancelled migration and connect_delayed
(which reinits the current coroutine).
If channel_disconnect is called from coroutine context when state is
SPICE_CHANNEL_STATE_SWITCHING:
    - channel_connect() is called and queues an idle which will call
      connect_delayed()
    - then spice_session_set_migration_state() is called and will call
      g_coroutine_object_notify(), which will queue an idle to emit the
      notification, and yield to the main context
    - connect_delayed() gets called in an idle, resets the current
      coroutine, and then yield back to the coroutine context
    - g_coroutine_object_notify() resumes and is confused as the
      notification did not happen.

This can be triggered by adding a g_usleep(10000000); at the beginning
of spice_session_start_migrating(), and by migrating an oVirt VM after
connecting to it.

This is based on a patch suggestion from Marc-André.
---
 gtk/coroutine.h          |  1 +
 gtk/coroutine_ucontext.c |  5 +++++
 gtk/gio-coroutine.c      | 16 ++++++++++++----
 3 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/gtk/coroutine.h b/gtk/coroutine.h
index 78dc467..5525fd3 100644
--- a/gtk/coroutine.h
+++ b/gtk/coroutine.h
@@ -53,6 +53,7 @@ struct coroutine
 	GThread *thread;
 	gboolean runnable;
 #endif
+	guint idle_id;
 };
 
 void coroutine_init(struct coroutine *co);
diff --git a/gtk/coroutine_ucontext.c b/gtk/coroutine_ucontext.c
index d709a33..6580fd7 100644
--- a/gtk/coroutine_ucontext.c
+++ b/gtk/coroutine_ucontext.c
@@ -65,6 +65,11 @@ static void coroutine_trampoline(struct continuation *cc)
 
 void coroutine_init(struct coroutine *co)
 {
+	if (co->idle_id != 0) {
+		g_warn_if_reached();
+		g_source_remove(co->idle_id);
+		co->idle_id = 0;
+	}
 	if (co->stack_size == 0)
 		co->stack_size = 16 << 20;
 
diff --git a/gtk/gio-coroutine.c b/gtk/gio-coroutine.c
index 1458d05..7527138 100644
--- a/gtk/gio-coroutine.c
+++ b/gtk/gio-coroutine.c
@@ -197,6 +197,7 @@ static gboolean emit_main_context(gpointer opaque)
 {
     struct signal_data *signal = opaque;
 
+    g_warn_if_fail(signal->caller->idle_id != 0);
     g_signal_emit_valist(signal->instance, signal->signal_id,
                          signal->detail, signal->var_args);
     signal->notified = TRUE;
@@ -207,12 +208,14 @@ static gboolean emit_main_context(gpointer opaque)
 }
 
 static void
-run_in_idle(GSourceFunc idle_callback, gpointer user_data)
+run_in_idle(GSourceFunc idle_callback, gpointer opaque)
 {
-    struct signal_data *data = (struct signal_data *)user_data;
+    struct signal_data *data = opaque;
+
+    g_warn_if_fail(data->caller->idle_id == 0);
 
     g_object_ref(data->instance);
-    g_idle_add(idle_callback, data);
+    data->caller->idle_id = g_idle_add(idle_callback, data);
     /* This switches to the system coroutine context, lets
      * the idle function run to dispatch the signal, and
      * finally returns once complete. ie this is synchronous
@@ -220,7 +223,11 @@ run_in_idle(GSourceFunc idle_callback, gpointer user_data)
      * an idle function involved
      */
     coroutine_yield(NULL);
-    g_warn_if_fail(data->notified);
+    if (!data->notified) {
+        g_warn_if_reached();
+        g_source_remove(data->caller->idle_id);
+    }
+    data->caller->idle_id = 0;
     g_object_unref(data->instance);
 }
 
@@ -251,6 +258,7 @@ static gboolean notify_main_context(gpointer opaque)
 {
     struct signal_data *signal = opaque;
 
+    g_warn_if_fail(signal->caller->idle_id != 0);
     g_object_notify(signal->instance, signal->propname);
     signal->notified = TRUE;
 
-- 
2.1.0



More information about the Spice-devel mailing list