--- 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
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.lur...@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: + */ -- 1.7.6 _______________________________________________ Spice-devel mailing list Spice-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/spice-devel