Re: [gtk-vnc-devel] [RFC] GThread based coroutines



Daniel P. Berrange wrote:
On Thu, Dec 20, 2007 at 05:17:05PM -0500, Anthony Liguori wrote:
I think the solution to this is to remove the gvnc functions from the export and not install the coroutine headers.

We'd need to make gvnc stuff into a GObject so you can use it frrom
the KVM test stuff though, right ?

KVM test actually uses vncdisplay. I wrote KVM test in python so using the struct gvnc interface wasn't a good option. I'd like to add a GObject interface so that I can switch kvm test over to being headless.

I observed that the structs required for ucontext vs gthread were more or
less the same except for the GThread * vs struct continuation. So I unified them into a generic 'void *context'. In the implementation this context points to a GThread * or a 'struct continuation *' as needed. The latter means the 'container_of' trick doesn't work, so I simply added 'void *owner'
to the 'struct continuation'.
Yes, you're on the right track, but I wanted to push the logic into the continuation layer.

Yeah, that might be even simpler, if we just had 2 internal impls of
the continuation module


I'll let you carry on working against my patch now that the autoconf stuff
is done.

Attached is my patch incorporating your autoconf changes. Let me know if you see anything wrong with it before I apply it.

Thanks again,

Anthony Liguori

Dan.

diff -r cb3e43b06ada configure.ac
--- a/configure.ac	Thu Dec 20 14:36:05 2007 -0500
+++ b/configure.ac	Thu Dec 20 20:57:57 2007 -0500
@@ -9,6 +9,7 @@ AC_SUBST(GNUTLS_REQUIRED)
 AC_SUBST(GNUTLS_REQUIRED)
 
 PYGTK_REQUIRED=2.0.0
+GTHREAD_REQUIRED=2.0.0
 PYTHON_REQUIRED=2.4
 
 AC_CONFIG_HEADERS([config.h:config.hin])
@@ -19,6 +20,7 @@ AM_INIT_AUTOMAKE(gtk-vnc, 0.3.1)
 AM_INIT_AUTOMAKE(gtk-vnc, 0.3.1)
 
 AC_PROG_CC_STDC
+AM_PROG_CC_C_O
 AC_PROG_LIBTOOL
 
 AC_ARG_WITH(python,
@@ -85,6 +87,39 @@ AC_SUBST(GNUTLS_CFLAGS)
 AC_SUBST(GNUTLS_CFLAGS)
 AC_SUBST(GNUTLS_LIBS)
 
+GTHREAD_CFLAGS=
+GTHREAD_LIBS=
+
+WITH_UCONTEXT=1
+
+AC_ARG_WITH(coroutine,
+[  --with-coroutine=ucontext/gthread  use ucontext or GThread for coroutines],
+[],[with_coroutine=ucontext])
+
+case $with_coroutine in
+  ucontext)
+    ;;
+  gthread)
+    ;;
+  *)
+    AC_ERROR([Unsupported coroutine type])
+esac
+
+if test "$with_coroutine" = "ucontext"; then
+  AC_CHECK_FUNC(makecontext, [],[with_coroutine=gthread])
+  AC_CHECK_FUNC(swapcontext, [],[with_coroutine=gthread])
+  AC_CHECK_FUNC(getcontext, [],[with_coroutine=gthread])
+fi
+
+if test "$with_coroutine" = "gthread"; then
+  PKG_CHECK_MODULES(GTHREAD, gthread-2.0 > $GTHREAD_REQUIRED)
+  WITH_UCONTEXT=0
+fi
+AC_SUBST(GTHREAD_CFLAGS)
+AC_SUBST(GTHREAD_LIBS)
+AC_DEFINE_UNQUOTED(WITH_UCONTEXT,[$WITH_UCONTEXT], [Whether to use ucontext coroutine impl])
+AM_CONDITIONAL(WITH_UCONTEXT, [test "$WITH_UCONTEXT" != "0"])
+
 if test "$WITH_PYTHON" = "yes"; then
   PKG_CHECK_MODULES(PYGTK, pygtk-2.0 >= $PYGTK_REQUIRED)
   AC_SUBST(PYGTK_CFLAGS)
diff -r cb3e43b06ada src/Makefile.am
--- a/src/Makefile.am	Thu Dec 20 14:36:05 2007 -0500
+++ b/src/Makefile.am	Thu Dec 20 20:57:57 2007 -0500
@@ -3,24 +3,34 @@ EXTRA_DIST = libgtk-vnc_sym.version vncm
 
 lib_LTLIBRARIES = libgtk-vnc-1.0.la
 
-libgtk_vnc_1_0_la_LIBADD = @GTK_LIBS@ @GNUTLS_LIBS@
-libgtk_vnc_1_0_la_CFLAGS = @GTK_CFLAGS@ @GNUTLS_CFLAGS@ @WARNING_CFLAGS@ \
+libgtk_vnc_1_0_la_LIBADD = @GTK_LIBS@ @GNUTLS_LIBS@ @GTHREAD_LIBS@
+libgtk_vnc_1_0_la_CFLAGS = @GTK_CFLAGS@ @GNUTLS_CFLAGS@ @GTHREAD_CFLAGS@ \
+			   @WARNING_CFLAGS@ \
                            -DSYSCONFDIR=\""$(sysconfdir)"\" \
                            @DEBUG_CFLAGS@
 libgtk_vnc_1_0_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libgtk-vnc_sym.version \
                             -version-info 0:1:0
 
 gtk_vnc_includedir = $(includedir)/gtk-vnc-1.0/
-gtk_vnc_include_HEADERS = vncdisplay.h gvnc.h coroutine.h continuation.h
+gtk_vnc_include_HEADERS = vncdisplay.h gvnc.h
 
 libgtk_vnc_1_0_la_SOURCES = blt.h blt1.h \
-	continuation.h continuation.c \
-	coroutine.h coroutine.c \
+	coroutine.h \
 	d3des.h d3des.c \
 	gvnc.h gvnc.c \
 	vncdisplay.h vncdisplay.c \
         vncmarshal.h vncmarshal.c \
 	utils.h
+
+if WITH_UCONTEXT
+libgtk_vnc_1_0_la_SOURCES += continuation.h continuation.c \
+			     coroutine_ucontext.h coroutine_ucontext.c
+EXTRA_DIST += coroutine_gthread.h coroutine_gthread.c
+else
+libgtk_vnc_1_0_la_SOURCES += coroutine_gthread.h coroutine_gthread.c
+EXTRA_DIST += continuation.h continuation.c \
+	      coroutine_ucontext.h coroutine_ucontext.c
+endif
 
 vncmarshal.c: vncmarshal.txt
 	glib-genmarshal --body $< > $@ || (rm -f $@ && exit 1)
diff -r cb3e43b06ada src/coroutine.c
--- a/src/coroutine.c	Thu Dec 20 14:36:05 2007 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2006  Anthony Liguori <anthony codemonkey ws>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 2 as
- * published by the Free Software Foundation.
- *
- *  GTK VNC Widget
- */
-
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "coroutine.h"
-
-int coroutine_release(struct coroutine *co)
-{
-	return cc_release(&co->cc);
-}
-
-static int _coroutine_release(struct continuation *cc)
-{
-	struct coroutine *co = container_of(cc, struct coroutine, cc);
-
-	if (co->release) {
-		int ret = co->release(co);
-		if (ret < 0)
-			return ret;
-	}
-
-	co->caller = NULL;
-
-	return munmap(cc->stack, cc->stack_size);
-}
-
-static void coroutine_trampoline(struct continuation *cc)
-{
-	struct coroutine *co = container_of(cc, struct coroutine, cc);
-	co->data = co->entry(co->data);
-}
-
-int coroutine_init(struct coroutine *co)
-{
-	if (co->stack_size == 0)
-		co->stack_size = 16 << 20;
-
-	co->cc.stack_size = co->stack_size;
-	co->cc.stack = mmap(0, co->stack_size,
-			    PROT_READ | PROT_WRITE,
-			    MAP_PRIVATE | MAP_ANONYMOUS,
-			    -1, 0);
-	if (co->cc.stack == MAP_FAILED)
-		return -1;
-	co->cc.entry = coroutine_trampoline;
-	co->cc.release = _coroutine_release;
-	co->exited = 0;
-
-	return cc_init(&co->cc);
-}
-
-#if 0
-static __thread struct coroutine leader;
-static __thread struct coroutine *current;
-#else
-static struct coroutine leader;
-static struct coroutine *current;
-#endif
-
-struct coroutine *coroutine_self(void)
-{
-	if (current == NULL)
-		current = &leader;
-	return current;
-}
-
-void *coroutine_swap(struct coroutine *from, struct coroutine *to, void *arg)
-{
-	int ret;
-	to->data = arg;
-	current = to;
-	ret = cc_swap(&from->cc, &to->cc);
-	if (ret == 0)
-		return from->data;
-	else if (ret == 1) {
-		coroutine_release(to);
-		current = &leader;
-		to->exited = 1;
-		return to->data;
-	}
-
-	return NULL;
-}
-
-void *coroutine_yieldto(struct coroutine *to, void *arg)
-{
-	if (to->caller) {
-		fprintf(stderr, "Co-routine is re-entering itself\n");
-		abort();
-	}
-	to->caller = coroutine_self();
-	return coroutine_swap(coroutine_self(), to, arg);
-}
-
-void *coroutine_yield(void *arg)
-{
-	struct coroutine *to = coroutine_self()->caller;
-	if (!to) {
-		fprintf(stderr, "Co-routine is yielding to no one\n");
-		abort();
-	}
-	coroutine_self()->caller = NULL;
-	return coroutine_swap(coroutine_self(), to, arg);
-}
-/*
- * Local variables:
- *  c-indent-level: 8
- *  c-basic-offset: 8
- *  tab-width: 8
- * End:
- */
diff -r cb3e43b06ada src/coroutine.h
--- a/src/coroutine.h	Thu Dec 20 14:36:05 2007 -0500
+++ b/src/coroutine.h	Thu Dec 20 20:57:57 2007 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006  Anthony Liguori <anthony codemonkey ws>
+ * Copyright (C) 2007  Anthony Liguori <anthony codemonkey ws>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License version 2 as
@@ -11,41 +11,12 @@
 #ifndef _COROUTINE_H_
 #define _COROUTINE_H_
 
-#include "continuation.h"
+#include "config.h"
 
-struct coroutine
-{
-	size_t stack_size;
-	void *(*entry)(void *);
-	int (*release)(struct coroutine *);
-
-	/* read-only */
-	int exited;
-
-	/* private */
-	struct coroutine *caller;
-	void *data;
-
-	struct continuation cc;
-};
-
-int coroutine_init(struct coroutine *co);
-
-int coroutine_release(struct coroutine *co);
-
-void *coroutine_swap(struct coroutine *from, struct coroutine *to, void *arg);
-
-struct coroutine *coroutine_self(void);
-
-void *coroutine_yieldto(struct coroutine *to, void *arg);
-
-void *coroutine_yield(void *arg);
+#if WITH_UCONTEXT
+#include "coroutine_ucontext.h"
+#else
+#include "coroutine_gthread.h"
+#endif
 
 #endif
-/*
- * Local variables:
- *  c-indent-level: 8
- *  c-basic-offset: 8
- *  tab-width: 8
- * End:
- */
diff -r cb3e43b06ada src/coroutine_gthread.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/coroutine_gthread.c	Thu Dec 20 20:57:57 2007 -0500
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2007  Anthony Liguori <anthony codemonkey ws>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  GTK VNC Widget
+ */
+
+#include "coroutine.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+static GCond *run_cond;
+static GMutex *run_lock;
+static struct coroutine *current;
+static struct coroutine leader;
+
+static void coroutine_system_init(void)
+{
+	if (!g_thread_supported())
+		g_thread_init(NULL);
+
+	run_cond = g_cond_new();
+	run_lock = g_mutex_new();
+
+	/* The thread that creates the first coroutine is the system coroutine
+	 * so let's fill out a structure for it */
+	leader.entry = NULL;
+	leader.release = NULL;
+	leader.stack_size = 0;
+	leader.exited = 0;
+	leader.thread = g_thread_self();
+	leader.runnable = TRUE; /* we're the one running right now */
+	leader.caller = NULL;
+	leader.data = NULL;
+
+	current = &leader;
+}
+
+static gpointer coroutine_thread(gpointer opaque)
+{
+	struct coroutine *co = opaque;
+
+	g_mutex_lock(run_lock);
+	while (!co->runnable)
+		g_cond_wait(run_cond, run_lock);
+
+	current = co;
+	co->data = co->entry(co->data);
+	co->exited = 1;
+
+	co->caller->runnable = TRUE;
+	g_cond_broadcast(run_cond);
+	g_mutex_unlock(run_lock);
+
+	return NULL;
+}
+
+int coroutine_init(struct coroutine *co)
+{
+	if (run_cond == NULL)
+		coroutine_system_init();
+	
+	co->thread = g_thread_create_full(coroutine_thread, co, co->stack_size,
+					  FALSE, TRUE,
+					  G_THREAD_PRIORITY_NORMAL,
+					  NULL);
+	if (co->thread == NULL)
+		return -1;
+
+	co->exited = 0;
+	co->runnable = FALSE;
+	co->caller = NULL;
+
+	return 0;
+}
+
+int coroutine_release(struct coroutine *co)
+{
+	return 0;
+}
+
+void *coroutine_swap(struct coroutine *from, struct coroutine *to, void *arg)
+{
+	from->runnable = FALSE;
+	to->runnable = TRUE;
+	to->data = arg;
+	to->caller = from;
+	g_cond_broadcast(run_cond);
+	g_mutex_unlock(run_lock);
+
+	g_mutex_lock(run_lock);
+	while (!from->runnable)
+		g_cond_wait(run_cond, run_lock);
+
+	current = from;
+
+	return from->data;
+}
+
+struct coroutine *coroutine_self(void)
+{
+	return current;
+}
+
+void *coroutine_yieldto(struct coroutine *to, void *arg)
+{
+	if (to->caller) {
+		fprintf(stderr, "Co-routine is re-entering itself\n");
+		abort();
+	}
+	to->caller = coroutine_self();
+	return coroutine_swap(coroutine_self(), to, arg);
+}
+
+void *coroutine_yield(void *arg)
+{
+	struct coroutine *to = coroutine_self()->caller;
+	if (!to) {
+		fprintf(stderr, "Co-routine is yielding to no one\n");
+		abort();
+	}
+	coroutine_self()->caller = NULL;
+	return coroutine_swap(coroutine_self(), to, arg);
+}
+
diff -r cb3e43b06ada src/coroutine_gthread.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/coroutine_gthread.h	Thu Dec 20 20:57:57 2007 -0500
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007  Anthony Liguori <anthony codemonkey ws>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  GTK VNC Widget
+ */
+
+#ifndef _GTHREAD_COROUTINE_H_
+#define _GTHREAD_COROUTINE_H_
+
+#include <glib.h>
+
+struct coroutine
+{
+	size_t stack_size;
+	void *(*entry)(void *);
+	int (*release)(struct coroutine *);
+
+	/* read-only */
+	int exited;
+
+	/* private */
+	GThread *thread;
+	gboolean runnable;
+	struct coroutine *caller;
+	void *data;
+};
+
+int coroutine_init(struct coroutine *co);
+
+int coroutine_release(struct coroutine *co);
+
+void *coroutine_swap(struct coroutine *from, struct coroutine *to, void *arg);
+
+struct coroutine *coroutine_self(void);
+
+void *coroutine_yieldto(struct coroutine *to, void *arg);
+
+void *coroutine_yield(void *arg);
+
+#endif
diff -r cb3e43b06ada src/coroutine_ucontext.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/coroutine_ucontext.c	Thu Dec 20 20:57:57 2007 -0500
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2006  Anthony Liguori <anthony codemonkey ws>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  GTK VNC Widget
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "coroutine.h"
+
+int coroutine_release(struct coroutine *co)
+{
+	return cc_release(&co->cc);
+}
+
+static int _coroutine_release(struct continuation *cc)
+{
+	struct coroutine *co = container_of(cc, struct coroutine, cc);
+
+	if (co->release) {
+		int ret = co->release(co);
+		if (ret < 0)
+			return ret;
+	}
+
+	co->caller = NULL;
+
+	return 0;
+}
+
+static void coroutine_trampoline(struct continuation *cc)
+{
+	struct coroutine *co = container_of(cc, struct coroutine, cc);
+	co->data = co->entry(co->data);
+}
+
+int coroutine_init(struct coroutine *co)
+{
+	if (co->stack_size == 0)
+		co->stack_size = 16 << 20;
+
+	co->cc.stack_size = co->stack_size;
+	co->cc.stack = mmap(0, co->stack_size,
+			    PROT_READ | PROT_WRITE,
+			    MAP_PRIVATE | MAP_ANONYMOUS,
+			    -1, 0);
+	if (co->cc.stack == MAP_FAILED)
+		return -1;
+	co->cc.entry = coroutine_trampoline;
+	co->cc.release = _coroutine_release;
+	co->exited = 0;
+
+	return cc_init(&co->cc);
+}
+
+#if 0
+static __thread struct coroutine leader;
+static __thread struct coroutine *current;
+#else
+static struct coroutine leader;
+static struct coroutine *current;
+#endif
+
+struct coroutine *coroutine_self(void)
+{
+	if (current == NULL)
+		current = &leader;
+	return current;
+}
+
+void *coroutine_swap(struct coroutine *from, struct coroutine *to, void *arg)
+{
+	int ret;
+	to->data = arg;
+	current = to;
+	ret = cc_swap(&from->cc, &to->cc);
+	if (ret == 0)
+		return from->data;
+	else if (ret == 1) {
+		coroutine_release(to);
+		current = &leader;
+		to->exited = 1;
+		return to->data;
+	}
+
+	return NULL;
+}
+
+void *coroutine_yieldto(struct coroutine *to, void *arg)
+{
+	if (to->caller) {
+		fprintf(stderr, "Co-routine is re-entering itself\n");
+		abort();
+	}
+	to->caller = coroutine_self();
+	return coroutine_swap(coroutine_self(), to, arg);
+}
+
+void *coroutine_yield(void *arg)
+{
+	struct coroutine *to = coroutine_self()->caller;
+	if (!to) {
+		fprintf(stderr, "Co-routine is yielding to no one\n");
+		abort();
+	}
+	coroutine_self()->caller = NULL;
+	return coroutine_swap(coroutine_self(), to, arg);
+}
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff -r cb3e43b06ada src/coroutine_ucontext.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/coroutine_ucontext.h	Thu Dec 20 20:57:57 2007 -0500
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2006  Anthony Liguori <anthony codemonkey ws>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  GTK VNC Widget
+ */
+
+#ifndef _UCONTEXT_COROUTINE_H_
+#define _UCONTEXT_COROUTINE_H_
+
+#include "continuation.h"
+
+struct coroutine
+{
+	size_t stack_size;
+	void *(*entry)(void *);
+	int (*release)(struct coroutine *);
+
+	/* read-only */
+	int exited;
+
+	/* private */
+	struct coroutine *caller;
+	void *data;
+
+	struct continuation cc;
+};
+
+int coroutine_init(struct coroutine *co);
+
+int coroutine_release(struct coroutine *co);
+
+void *coroutine_swap(struct coroutine *from, struct coroutine *to, void *arg);
+
+struct coroutine *coroutine_self(void);
+
+void *coroutine_yieldto(struct coroutine *to, void *arg);
+
+void *coroutine_yield(void *arg);
+
+#endif
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff -r cb3e43b06ada src/libgtk-vnc_sym.version
--- a/src/libgtk-vnc_sym.version	Thu Dec 20 14:36:05 2007 -0500
+++ b/src/libgtk-vnc_sym.version	Thu Dec 20 20:57:57 2007 -0500
@@ -28,41 +28,6 @@
 
     vnc_display_client_cut_text;
 
-    gvnc_new;
-    gvnc_free;
-    gvnc_close;
-    gvnc_shutdown;
-
-    gvnc_open_fd;
-    gvnc_open_host;
-    gvnc_is_open;
-
-    gvnc_initialize;
-    gvnc_is_initialized;
-
-    gvnc_server_message;
-    gvnc_client_cut_text;
-    gvnc_pointer_event;
-    gvnc_key_event;
-    gvnc_framebuffer_update_request;
-    gvnc_set_encodings;
-    gvnc_set_pixel_format;
-    gvnc_set_shared_buffer;
-    gvnc_has_error;
-    gvnc_set_local;
-    gvnc_set_vnc_ops;
-    gvnc_shared_memory_enabled;
-    gvnc_get_name;
-    gvnc_get_width;
-    gvnc_get_height;
-
-    coroutine_init;
-    coroutine_release;
-    coroutine_swap;
-    coroutine_self;
-    coroutine_yieldto;
-    coroutine_yield;
-
   local:
       *;
 };


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