[gtk-vnc-devel] [PATCH] Delay signal emission from coroutines



This appears to fix the problem when configuring --with-coroutine=thread.

I'll push this change to the outgoing repo tomorrow after everyone's gotten a chance to look over it.

Regards,

Anthony Liguori
# HG changeset patch
# User Anthony Liguori <anthony codemonkey ws>
# Date 1202350491 21600
# Node ID 0639b5d4ae890a163eb4cbd498c9158d9d4120d5
# Parent  e270ad1c273674048ca6c2f84daf9c04c2b3e47d
Always emit signals from system coroutine.

When using threads to simulate coroutines, GTK gets very confused as signal
handlers are run in a different thread than the main loop when the signal
handlers aren't expecting it.

This patch changes all of the signals emitted from the VNC coroutine to use
a delay function to actually issue the signals.  This appears to fix the
GThread coroutine issue.

diff -r e270ad1c2736 -r 0639b5d4ae89 src/vncdisplay.c
--- a/src/vncdisplay.c	Wed Feb 06 19:46:02 2008 -0600
+++ b/src/vncdisplay.c	Wed Feb 06 20:14:51 2008 -0600
@@ -81,6 +81,28 @@ struct _VncDisplayPrivate
 	gboolean read_only;
 	gboolean allow_lossy;
 	gboolean allow_scaling;
+};
+
+/* Delayed signal emmission.
+ *
+ * We want signals to be delivered in the system coroutine.  This helps avoid
+ * confusing applications.  This is particularily important when using
+ * GThread based coroutines since GTK gets very upset if a signal handler is
+ * run in a different thread from the main loop if that signal handler isn't
+ * written to use explicit locking.
+ */
+struct signal_data
+{
+	VncDisplay *obj;
+	struct coroutine *caller;
+
+	int signum;
+	GValueArray *cred_list;
+	int width;
+	int height;
+	const char *msg;
+	unsigned int auth_type;
+	GString *str;
 };
 
 G_DEFINE_TYPE(VncDisplay, vnc_display, GTK_TYPE_DRAWING_AREA)
@@ -788,10 +810,72 @@ static void setup_gl_image(VncDisplay *o
 }
 #endif
 
+static gboolean emit_signal_auth_cred(gpointer opaque)
+{
+	struct signal_data *s = opaque;
+
+	switch (s->signum) {
+	case VNC_AUTH_CREDENTIAL:
+		g_signal_emit(G_OBJECT(s->obj),
+			      signals[VNC_AUTH_CREDENTIAL],
+			      0,
+			      s->cred_list);
+		break;
+	case VNC_DESKTOP_RESIZE:
+		g_signal_emit(G_OBJECT(s->obj),
+			      signals[VNC_DESKTOP_RESIZE],
+			      0,
+			      s->width, s->height);
+		break;
+	case VNC_AUTH_FAILURE:
+		g_signal_emit(G_OBJECT(s->obj),
+			      signals[VNC_AUTH_FAILURE],
+			      0,
+			      s->msg);
+		break;
+	case VNC_AUTH_UNSUPPORTED:
+		g_signal_emit(G_OBJECT(s->obj),
+			      signals[VNC_AUTH_UNSUPPORTED],
+			      0,
+			      s->auth_type);
+		break;
+	case VNC_SERVER_CUT_TEXT:
+		g_signal_emit(G_OBJECT(s->obj),
+			      signals[VNC_SERVER_CUT_TEXT],
+			      0,
+			      s->str->str);
+		break;
+	case VNC_BELL:
+	case VNC_CONNECTED:
+	case VNC_INITIALIZED:
+	case VNC_DISCONNECTED:
+		g_signal_emit(G_OBJECT(s->obj),
+			      signals[s->signum],
+			      0);
+		break;
+	}
+
+	coroutine_yieldto(s->caller, NULL);
+	
+	return FALSE;
+}
+
+/* This function should be used to emit signals from gvnc callbacks */
+static void emit_signal_delayed(VncDisplay *obj, int signum,
+				struct signal_data *data)
+{
+	data->obj = obj;
+	data->caller = coroutine_self();
+	data->signum = signum;
+	g_idle_add(emit_signal_auth_cred, data);
+	coroutine_yield(NULL);
+}
+
 static gboolean on_resize(void *opaque, int width, int height)
 {
 	VncDisplay *obj = VNC_DISPLAY(opaque);
 	VncDisplayPrivate *priv = obj->priv;
+	struct signal_data s;
 
 	if (priv->gvnc == NULL || !gvnc_is_initialized(priv->gvnc))
 		return TRUE;
@@ -830,10 +914,9 @@ static gboolean on_resize(void *opaque, 
 
 	gvnc_set_local(priv->gvnc, &priv->fb);
 
-	g_signal_emit (G_OBJECT (obj),
-		       signals[VNC_DESKTOP_RESIZE],
-		       0,
-		       width, height);
+	s.width = width;
+	s.height = height;
+	emit_signal_delayed(obj, VNC_DESKTOP_RESIZE, &s);
 
 	return TRUE;
 }
@@ -1004,6 +1087,7 @@ static gboolean on_auth_cred(void *opaqu
 	VncDisplay *obj = VNC_DISPLAY(opaque);
 	GValueArray *cred_list;
 	GValue username, password, clientname;
+	struct signal_data s;
 
 	memset(&username, 0, sizeof(username));
 	memset(&password, 0, sizeof(password));
@@ -1026,10 +1110,8 @@ static gboolean on_auth_cred(void *opaqu
 		cred_list = g_value_array_append(cred_list, &clientname);
 	}
 
-	g_signal_emit (G_OBJECT (obj),
-		       signals[VNC_AUTH_CREDENTIAL],
-		       0,
-		       cred_list);
+	s.cred_list = cred_list;
+	emit_signal_delayed(obj, VNC_AUTH_CREDENTIAL, &s);
 
 	g_value_array_free(cred_list);
 
@@ -1069,11 +1151,10 @@ static gboolean on_auth_failure(void *op
 static gboolean on_auth_failure(void *opaque, const char *msg)
 {
 	VncDisplay *obj = VNC_DISPLAY(opaque);
+	struct signal_data s;
 
-	g_signal_emit (G_OBJECT (obj),
-		       signals[VNC_AUTH_FAILURE],
-		       0,
-		       msg);
+	s.msg = msg;
+	emit_signal_delayed(obj, VNC_AUTH_FAILURE, &s);
 
 	return TRUE;
 }
@@ -1081,11 +1162,10 @@ static gboolean on_auth_unsupported(void
 static gboolean on_auth_unsupported(void *opaque, unsigned int auth_type)
 {
 	VncDisplay *obj = VNC_DISPLAY(opaque);
+	struct signal_data s;
 
-	g_signal_emit (G_OBJECT (obj),
-		       signals[VNC_AUTH_UNSUPPORTED],
-		       0,
-		       auth_type);
+	s.auth_type = auth_type;
+	emit_signal_delayed(obj, VNC_AUTH_UNSUPPORTED, &s);
 
 	return TRUE;
 }
@@ -1094,11 +1174,10 @@ static gboolean on_server_cut_text(void 
 {
 	VncDisplay *obj = VNC_DISPLAY(opaque);
 	GString *str = g_string_new_len ((const gchar *)text, len);
+	struct signal_data s;
 
-	g_signal_emit (G_OBJECT (obj),
-		       signals[VNC_SERVER_CUT_TEXT],
-		       0,
-		       str->str);
+	s.str = str;
+	emit_signal_delayed(obj, VNC_SERVER_CUT_TEXT, &s);
 
 	g_string_free (str, TRUE);
 	return TRUE;
@@ -1107,10 +1186,9 @@ static gboolean on_bell(void *opaque)
 static gboolean on_bell(void *opaque)
 {
 	VncDisplay *obj = VNC_DISPLAY(opaque);
+	struct signal_data s;
 
-	g_signal_emit (G_OBJECT (obj),
-		       signals[VNC_BELL],
-		       0);
+	emit_signal_delayed(obj, VNC_BELL, &s);
 
 	return TRUE;
 }
@@ -1237,8 +1315,8 @@ static void *vnc_coroutine(void *opaque)
 				GVNC_ENCODING_RAW };
 	int32_t *encodingsp;
 	int n_encodings;
-
 	int ret;
+	struct signal_data s;
 
 	if (priv->gvnc == NULL || gvnc_is_open(priv->gvnc)) {
 		g_idle_add(delayed_unref_object, obj);
@@ -1254,17 +1332,13 @@ static void *vnc_coroutine(void *opaque)
 			goto cleanup;
 	}
 
-	g_signal_emit (G_OBJECT (obj),
-		       signals[VNC_CONNECTED],
-		       0);
+	emit_signal_delayed(obj, VNC_CONNECTED, &s);
 
 	GVNC_DEBUG("Protocol initialization\n");
 	if (!gvnc_initialize(priv->gvnc, FALSE))
 		goto cleanup;
 
-	g_signal_emit (G_OBJECT (obj),
-		       signals[VNC_INITIALIZED],
-		       0);
+	emit_signal_delayed(obj, VNC_INITIALIZED, &s);
 
 	encodingsp = encodings;
 	n_encodings = ARRAY_SIZE(encodings);
@@ -1295,9 +1369,7 @@ static void *vnc_coroutine(void *opaque)
  cleanup:
 	GVNC_DEBUG("Doing final VNC cleanup\n");
 	gvnc_close(priv->gvnc);
-	g_signal_emit (G_OBJECT (obj),
-		       signals[VNC_DISCONNECTED],
-		       0);
+	emit_signal_delayed(obj, VNC_DISCONNECTED, &s);
 	g_idle_add(delayed_unref_object, obj);
 	/* Co-routine exits now - the VncDisplay object may no longer exist,
 	   so don't do anything else now unless you like SEGVs */


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