[Spice-devel] =?utf-8?q?=5BPATCH_2/5=5D_gtk=3A_implement_coroutin?= =?utf-8?q?es_using_Windows_fibers?=

Submitted by Marc-André Lureau on Aug. 17, 2011, 7:45 p.m.

Details

Message ID 1313585115-1459-2-git-send-email-marcandre.lureau@redhat.com
State New, archived
Headers show

Not browsing as part of any series.

Commit Message

Marc-André Lureau Aug. 17, 2011, 7:45 p.m.
---
 configure.ac              |   40 +++++++++++----
 gtk/Makefile.am           |   14 ++++-
 gtk/continuation.c        |    6 +-
 gtk/coroutine.h           |    5 ++
 gtk/coroutine_winfibers.c |  120 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 168 insertions(+), 17 deletions(-)
 create mode 100644 gtk/coroutine_winfibers.c

Patch hide | download patch | download mbox

diff --git a/configure.ac b/configure.ac
index 8c9c5db..e60ed78 100644
--- a/configure.ac
+++ b/configure.ac
@@ -311,31 +311,49 @@  else
 fi
 
 AC_ARG_WITH([coroutine],
-  AS_HELP_STRING([--with-coroutine=@<:@ucontext/gthread@:>@],
-                 [use ucontext or GThread for coroutines @<:@default=ucontext@:>@]),
+  AS_HELP_STRING([--with-coroutine=@<:@ucontext/gthread/winfiber/auto@:>@],
+                 [use ucontext or GThread for coroutines @<:@default=auto@:>@]),
   [],
-  [with_coroutine=ucontext])
+  [with_coroutine=auto])
 
 case $with_coroutine in
-  ucontext|gthread) ;;
+  ucontext|gthread|winfiber|auto) ;;
   *) AC_MSG_ERROR(Unsupported coroutine type)
 esac
 
+if test "$with_coroutine" = "auto"; then
+  if test "$os_win32" = "yes"; then
+    with_coroutine=winfiber
+  else
+    with_coroutine=ucontext
+  fi
+fi
+
 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
-  # gthread is required anyway
-  WITH_UCONTEXT=0
-else
-  WITH_UCONTEXT=1
-fi
+WITH_UCONTEXT=0
+WITH_GTHREAD=0
+WITH_WINFIBER=0
+
+case $with_coroutine in
+  ucontext) WITH_UCONTEXT=1 ;;
+  gthread) WITH_GTHREAD=1 ;;
+  winfiber) WITH_WINFIBER=1 ;;
+  *) AC_MSG_ERROR(Unsupported coroutine type)
+esac
 
 AC_DEFINE_UNQUOTED(WITH_UCONTEXT,[$WITH_UCONTEXT], [Whether to use ucontext coroutine impl])
-AM_CONDITIONAL(WITH_UCONTEXT, [test "$WITH_UCONTEXT" != "0"])
+AM_CONDITIONAL(WITH_UCONTEXT, [test "x$WITH_UCONTEXT" = "x1"])
+
+AC_DEFINE_UNQUOTED(WITH_WINFIBER,[$WITH_WINFIBER], [Whether to use fiber coroutine impl])
+AM_CONDITIONAL(WITH_WINFIBER, [test "x$WITH_WINFIBER" = "x1"])
+
+AC_DEFINE_UNQUOTED(WITH_GTHREAD,[$WITH_GTHREAD], [Whether to use gthread coroutine impl])
+AM_CONDITIONAL(WITH_GTHREAD, [test "x$WITH_GTHREAD" = "x1"])
 
 AM_CONDITIONAL([HAVE_INTROSPECTION], [test "0" != "1"])
 PKG_CHECK_MODULES([GOBJECT_INTROSPECTION],
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index edec166..cbcaa79 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -10,6 +10,10 @@  EXTRA_DIST =					\
 	keymap-gen.pl				\
 	keymaps.csv				\
 	decode-glz-tmpl.c			\
+	coroutine_gthread.c			\
+	coroutine_ucontext.c			\
+	coroutine_winfibers.c			\
+	continuation.h continuation.c		\
 	map-file				\
 	$(NULL)
 
@@ -255,11 +259,15 @@  endif
 
 if WITH_UCONTEXT
 libspice_client_glib_2_0_la_SOURCES += continuation.h continuation.c coroutine_ucontext.c
-EXTRA_DIST += coroutine_gthread.c
-else
+endif
+
+if WITH_WINFIBER
+libspice_client_glib_2_0_la_SOURCES += coroutine_winfibers.c
+endif
+
+if WITH_GTHREAD
 libspice_client_glib_2_0_la_SOURCES += coroutine_gthread.c
 libspice_client_glib_2_0_la_LIBADD += $(GTHREAD_LIBS)
-EXTRA_DIST += continuation.h continuation.c coroutine_ucontext.c
 endif
 
 displaysrc = \
diff --git a/gtk/continuation.c b/gtk/continuation.c
index 6eaed3c..9cdd578 100644
--- a/gtk/continuation.c
+++ b/gtk/continuation.c
@@ -81,9 +81,9 @@  int cc_swap(struct continuation *from, struct continuation *to)
 	if (getcontext(&to->last) == -1)
 		return -1;
 	else if (to->exited == 0)
-		to->exited = 1;
-	else if (to->exited == 1)
-		return 1;
+		to->exited = 1; // so when coroutine finishes
+        else if (to->exited == 1)
+                return 1; // it ends up here
 
 	if (_setjmp(from->jmp) == 0)
 		_longjmp(to->jmp, 1);
diff --git a/gtk/coroutine.h b/gtk/coroutine.h
index 90ad9e8..031a97b 100644
--- a/gtk/coroutine.h
+++ b/gtk/coroutine.h
@@ -25,6 +25,8 @@ 
 
 #if WITH_UCONTEXT
 #include "continuation.h"
+#elif WITH_WINFIBER
+#include <windows.h>
 #else
 #include <glib.h>
 #endif
@@ -44,6 +46,9 @@  struct coroutine
 
 #if WITH_UCONTEXT
 	struct continuation cc;
+#elif WITH_WINFIBER
+        LPVOID fiber;
+        int ret;
 #else
 	GThread *thread;
 	gboolean runnable;
diff --git a/gtk/coroutine_winfibers.c b/gtk/coroutine_winfibers.c
new file mode 100644
index 0000000..a22da3b
--- /dev/null
+++ b/gtk/coroutine_winfibers.c
@@ -0,0 +1,120 @@ 
+/*
+ * SpiceGtk coroutine with Windows fibers
+ *
+ * Copyright (C) 2011  Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.0 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+
+#include "coroutine.h"
+
+static struct coroutine leader = { 0, };
+static struct coroutine *current = NULL;
+static struct coroutine *caller = NULL;
+
+int coroutine_release(struct coroutine *co)
+{
+	DeleteFiber(co->fiber);
+	return 0;
+}
+
+static void WINAPI coroutine_trampoline(LPVOID lpParameter)
+{
+	struct coroutine *co = (struct coroutine *)lpParameter;
+
+	co->data = co->entry(co->data);
+
+	if (co->release)
+		co->ret = co->release(co);
+	else
+		co->ret = 0;
+
+	co->caller = NULL;
+
+	// and switch back to caller
+	co->ret = 1;
+	SwitchToFiber(caller->fiber);
+}
+
+int coroutine_init(struct coroutine *co)
+{
+	if (leader.fiber == NULL) {
+		leader.fiber = ConvertThreadToFiber(&leader);
+		if (leader.fiber == NULL)
+			return -1;
+	}
+
+	co->fiber = CreateFiber(0, &coroutine_trampoline, co);
+	if (co->fiber == NULL)
+		return -1;
+
+	return 0;
+}
+
+struct coroutine *coroutine_self(void)
+{
+	if (current == NULL)
+		current = &leader;
+	return current;
+}
+
+void *coroutine_swap(struct coroutine *from, struct coroutine *to, void *arg)
+{
+	to->data = arg;
+	current = to;
+	caller = from;
+	SwitchToFiber(to->fiber);
+	if (to->ret == 0)
+		return from->data;
+	else if (to->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:
+ */