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



Hi,

Attached a thread-based coroutine implementation. This should make the Windows port much easier. I've tested it with gvncviewer and vinagre (with multiple tabs) so I think it works pretty well. The basic idea is to use a GCond and GMutex to run all threads in lock-step. It was tricky getting the ucontext coroutines working properly so testing/review is greatly appreciated!

Also, I'm hoping someone who has a bit more autoconf foo can help me with the proper autoconf/automake integration. We need to detect ucontext in configure and if it's available, compile in ucontext_coroutine.[ch], continuation.[ch]. Otherwise, we need to add a dependency to gthread-2.0 and compile in gthread_coroutine.[ch].

Regards,

Anthony Liguori
diff -r 71c9bd03a6c7 src/Makefile.am
--- a/src/Makefile.am	Tue Dec 18 21:34:09 2007 -0500
+++ b/src/Makefile.am	Tue Dec 18 22:42:49 2007 -0500
@@ -3,7 +3,7 @@ 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_LIBADD = @GTK_LIBS@ @GNUTLS_LIBS@ -lgthread-2.0
 libgtk_vnc_1_0_la_CFLAGS = @GTK_CFLAGS@ @GNUTLS_CFLAGS@ @WARNING_CFLAGS@ \
                            -DSYSCONFDIR=\""$(sysconfdir)"\" \
                            @DEBUG_CFLAGS@
@@ -11,11 +11,10 @@ libgtk_vnc_1_0_la_LDFLAGS = -Wl,--versio
                             -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 coroutine.h gthread_coroutine.h
 
 libgtk_vnc_1_0_la_SOURCES = blt.h blt1.h \
-	continuation.h continuation.c \
-	coroutine.h coroutine.c \
+	gthread_coroutine.h gthread_coroutine.c \
 	d3des.h d3des.c \
 	gvnc.h gvnc.c \
 	vncdisplay.h vncdisplay.c \
diff -r 71c9bd03a6c7 src/coroutine.c
--- a/src/coroutine.c	Tue Dec 18 21:34:09 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 71c9bd03a6c7 src/coroutine.h
--- a/src/coroutine.h	Tue Dec 18 21:34:09 2007 -0500
+++ b/src/coroutine.h	Tue Dec 18 22:42:49 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,10 @@
 #ifndef _COROUTINE_H_
 #define _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);
+#ifdef HAVE_UCONTEXT
+#include "ucontext_corutine.h"
+#else
+#include "gthread_coroutine.h"
+#endif
 
 #endif
-/*
- * Local variables:
- *  c-indent-level: 8
- *  c-basic-offset: 8
- *  tab-width: 8
- * End:
- */
diff -r 71c9bd03a6c7 src/gthread_coroutine.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gthread_coroutine.c	Tue Dec 18 22:42:49 2007 -0500
@@ -0,0 +1,129 @@
+/*
+ * 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 "gthread_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 G_GNUC_UNUSED)
+{
+	/* should we join the thread? */
+	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 71c9bd03a6c7 src/gthread_coroutine.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gthread_coroutine.h	Tue Dec 18 22:42:49 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


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