Message ID | 1311624250-1670-3-git-send-email-stefanha@linux.vnet.ibm.com |
---|---|
State | New |
Headers | show |
On 07/25/2011 03:04 PM, Stefan Hajnoczi wrote: > From: "Aneesh Kumar K.V"<aneesh.kumar@linux.vnet.ibm.com> > > On platforms that don't support makecontext(3) use gthread based > coroutine implementation. > > Darwin has makecontext(3) but getcontext(3) is stubbed out to return > ENOTSUP. Andreas Färber<andreas.faerber@web.de> debugged this and > contributed the ./configure test which solves the issue for Darwin/ppc64 > (and ppc) v10.5. > > [Original patch by Aneesh, made consistent with coroutine-ucontext.c and > switched to GStaticPrivate by Stefan. Tested on Linux and OpenBSD.] > > Signed-off-by: Aneesh Kumar K.V<aneesh.kumar@linux.vnet.ibm.com> > Signed-off-by: Stefan Hajnoczi<stefanha@linux.vnet.ibm.com> > --- > Makefile.objs | 4 ++ > configure | 18 +++++++ > coroutine-gthread.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 153 insertions(+), 0 deletions(-) > create mode 100644 coroutine-gthread.c > > diff --git a/Makefile.objs b/Makefile.objs > index 28e1762..5679e1f 100644 > --- a/Makefile.objs > +++ b/Makefile.objs > @@ -13,7 +13,11 @@ oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o > ####################################################################### > # coroutines > coroutine-obj-y = qemu-coroutine.o > +ifeq ($(CONFIG_UCONTEXT_COROUTINE),y) > coroutine-obj-$(CONFIG_POSIX) += coroutine-ucontext.o > +else > +coroutine-obj-$(CONFIG_POSIX) += coroutine-gthread.o > +endif > coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o > > ####################################################################### > diff --git a/configure b/configure > index 600da9b..1b6ad87 100755 > --- a/configure > +++ b/configure > @@ -2499,6 +2499,20 @@ if test "$trace_backend" = "dtrace"; then > fi > > ########################################## > +# check if we have makecontext > + > +ucontext_coroutine=no > +if test "$darwin" != "yes"; then > + cat> $TMPC<< EOF > +#include<ucontext.h> > +int main(void) { makecontext(0, 0, 0); } > +EOF > + if compile_prog "" "" ; then > + ucontext_coroutine=yes > + fi > +fi > + Doesn't this make the build non-bisectable on platforms that don't have makecontext? I think the gthread patch needs to come first and then the ucontext version can be used on platforms that we detect it's there. Regards, Anthony Liguori > +########################################## > # End of CC checks > # After here, no more $cc or $ld runs > > @@ -2970,6 +2984,10 @@ if test "$rbd" = "yes" ; then > echo "CONFIG_RBD=y">> $config_host_mak > fi > > +if test "$ucontext_coroutine" = "yes" ; then > + echo "CONFIG_UCONTEXT_COROUTINE=y">> $config_host_mak > +fi > + > # USB host support > case "$usb" in > linux) > diff --git a/coroutine-gthread.c b/coroutine-gthread.c > new file mode 100644 > index 0000000..f09877e > --- /dev/null > +++ b/coroutine-gthread.c > @@ -0,0 +1,131 @@ > +/* > + * GThread coroutine initialization code > + * > + * Copyright (C) 2006 Anthony Liguori<anthony@codemonkey.ws> > + * Copyright (C) 2011 Aneesh Kumar K.V<aneesh.kumar@linux.vnet.ibm.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, see<http://www.gnu.org/licenses/>. > + */ > + > +#include<glib.h> > +#include "qemu-common.h" > +#include "qemu-coroutine-int.h" > + > +typedef struct { > + Coroutine base; > + GThread *thread; > + bool runnable; > + CoroutineAction action; > +} CoroutineGThread; > + > +static GCond *coroutine_cond; > +static GStaticMutex coroutine_lock = G_STATIC_MUTEX_INIT; > +static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT; > + > +static void __attribute__((constructor)) coroutine_init(void) > +{ > + if (!g_thread_supported()) { > + g_thread_init(NULL); > + } > + > + coroutine_cond = g_cond_new(); > +} > + > +static void coroutine_wait_runnable_locked(CoroutineGThread *co) > +{ > + while (!co->runnable) { > + g_cond_wait(coroutine_cond, g_static_mutex_get_mutex(&coroutine_lock)); > + } > +} > + > +static void coroutine_wait_runnable(CoroutineGThread *co) > +{ > + g_static_mutex_lock(&coroutine_lock); > + coroutine_wait_runnable_locked(co); > + g_static_mutex_unlock(&coroutine_lock); > +} > + > +static gpointer coroutine_thread(gpointer opaque) > +{ > + CoroutineGThread *co = opaque; > + > + g_static_private_set(&coroutine_key, co, NULL); > + coroutine_wait_runnable(co); > + co->base.entry(co->base.entry_arg); > + qemu_coroutine_switch(&co->base, co->base.caller, COROUTINE_TERMINATE); > + return NULL; > +} > + > +Coroutine *qemu_coroutine_new(void) > +{ > + CoroutineGThread *co; > + > + co = qemu_mallocz(sizeof(*co)); > + co->thread = g_thread_create_full(coroutine_thread, co, 0, TRUE, TRUE, > + G_THREAD_PRIORITY_NORMAL, NULL); > + if (!co->thread) { > + qemu_free(co); > + return NULL; > + } > + return&co->base; > +} > + > +void qemu_coroutine_delete(Coroutine *co_) > +{ > + CoroutineGThread *co = DO_UPCAST(CoroutineGThread, base, co_); > + > + g_thread_join(co->thread); > + qemu_free(co); > +} > + > +CoroutineAction qemu_coroutine_switch(Coroutine *from_, > + Coroutine *to_, > + CoroutineAction action) > +{ > + CoroutineGThread *from = DO_UPCAST(CoroutineGThread, base, from_); > + CoroutineGThread *to = DO_UPCAST(CoroutineGThread, base, to_); > + > + g_static_mutex_lock(&coroutine_lock); > + from->runnable = false; > + from->action = action; > + to->runnable = true; > + to->action = action; > + g_cond_broadcast(coroutine_cond); > + > + if (action != COROUTINE_TERMINATE) { > + coroutine_wait_runnable_locked(from); > + } > + g_static_mutex_unlock(&coroutine_lock); > + return from->action; > +} > + > +Coroutine *qemu_coroutine_self(void) > +{ > + CoroutineGThread *co = g_static_private_get(&coroutine_key); > + > + if (!co) { > + co = qemu_mallocz(sizeof(*co)); > + co->runnable = true; > + g_static_private_set(&coroutine_key, co, (GDestroyNotify)qemu_free); > + } > + > + return&co->base; > +} > + > +bool qemu_in_coroutine(void) > +{ > + CoroutineGThread *co = g_static_private_get(&coroutine_key); > + > + return co&& co->base.caller; > +}
On Mon, Jul 25, 2011 at 9:38 PM, Anthony Liguori <anthony@codemonkey.ws> wrote: > On 07/25/2011 03:04 PM, Stefan Hajnoczi wrote: >> >> From: "Aneesh Kumar K.V"<aneesh.kumar@linux.vnet.ibm.com> >> >> On platforms that don't support makecontext(3) use gthread based >> coroutine implementation. >> >> Darwin has makecontext(3) but getcontext(3) is stubbed out to return >> ENOTSUP. Andreas Färber<andreas.faerber@web.de> debugged this and >> contributed the ./configure test which solves the issue for Darwin/ppc64 >> (and ppc) v10.5. >> >> [Original patch by Aneesh, made consistent with coroutine-ucontext.c and >> switched to GStaticPrivate by Stefan. Tested on Linux and OpenBSD.] >> >> Signed-off-by: Aneesh Kumar K.V<aneesh.kumar@linux.vnet.ibm.com> >> Signed-off-by: Stefan Hajnoczi<stefanha@linux.vnet.ibm.com> >> --- >> Makefile.objs | 4 ++ >> configure | 18 +++++++ >> coroutine-gthread.c | 131 >> +++++++++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 153 insertions(+), 0 deletions(-) >> create mode 100644 coroutine-gthread.c >> >> diff --git a/Makefile.objs b/Makefile.objs >> index 28e1762..5679e1f 100644 >> --- a/Makefile.objs >> +++ b/Makefile.objs >> @@ -13,7 +13,11 @@ oslib-obj-$(CONFIG_POSIX) += oslib-posix.o >> qemu-thread-posix.o >> ####################################################################### >> # coroutines >> coroutine-obj-y = qemu-coroutine.o >> +ifeq ($(CONFIG_UCONTEXT_COROUTINE),y) >> coroutine-obj-$(CONFIG_POSIX) += coroutine-ucontext.o >> +else >> +coroutine-obj-$(CONFIG_POSIX) += coroutine-gthread.o >> +endif >> coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o >> >> ####################################################################### >> diff --git a/configure b/configure >> index 600da9b..1b6ad87 100755 >> --- a/configure >> +++ b/configure >> @@ -2499,6 +2499,20 @@ if test "$trace_backend" = "dtrace"; then >> fi >> >> ########################################## >> +# check if we have makecontext >> + >> +ucontext_coroutine=no >> +if test "$darwin" != "yes"; then >> + cat> $TMPC<< EOF >> +#include<ucontext.h> >> +int main(void) { makecontext(0, 0, 0); } >> +EOF >> + if compile_prog "" "" ; then >> + ucontext_coroutine=yes >> + fi >> +fi >> + > > Doesn't this make the build non-bisectable on platforms that don't have > makecontext? I think the gthread patch needs to come first and then the > ucontext version can be used on platforms that we detect it's there. You are correct. I have sent a new version that first introduces gthread, and then ucontext and fibers. Stefan
diff --git a/Makefile.objs b/Makefile.objs index 28e1762..5679e1f 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -13,7 +13,11 @@ oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o ####################################################################### # coroutines coroutine-obj-y = qemu-coroutine.o +ifeq ($(CONFIG_UCONTEXT_COROUTINE),y) coroutine-obj-$(CONFIG_POSIX) += coroutine-ucontext.o +else +coroutine-obj-$(CONFIG_POSIX) += coroutine-gthread.o +endif coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o ####################################################################### diff --git a/configure b/configure index 600da9b..1b6ad87 100755 --- a/configure +++ b/configure @@ -2499,6 +2499,20 @@ if test "$trace_backend" = "dtrace"; then fi ########################################## +# check if we have makecontext + +ucontext_coroutine=no +if test "$darwin" != "yes"; then + cat > $TMPC << EOF +#include <ucontext.h> +int main(void) { makecontext(0, 0, 0); } +EOF + if compile_prog "" "" ; then + ucontext_coroutine=yes + fi +fi + +########################################## # End of CC checks # After here, no more $cc or $ld runs @@ -2970,6 +2984,10 @@ if test "$rbd" = "yes" ; then echo "CONFIG_RBD=y" >> $config_host_mak fi +if test "$ucontext_coroutine" = "yes" ; then + echo "CONFIG_UCONTEXT_COROUTINE=y" >> $config_host_mak +fi + # USB host support case "$usb" in linux) diff --git a/coroutine-gthread.c b/coroutine-gthread.c new file mode 100644 index 0000000..f09877e --- /dev/null +++ b/coroutine-gthread.c @@ -0,0 +1,131 @@ +/* + * GThread coroutine initialization code + * + * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws> + * Copyright (C) 2011 Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.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, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include "qemu-common.h" +#include "qemu-coroutine-int.h" + +typedef struct { + Coroutine base; + GThread *thread; + bool runnable; + CoroutineAction action; +} CoroutineGThread; + +static GCond *coroutine_cond; +static GStaticMutex coroutine_lock = G_STATIC_MUTEX_INIT; +static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT; + +static void __attribute__((constructor)) coroutine_init(void) +{ + if (!g_thread_supported()) { + g_thread_init(NULL); + } + + coroutine_cond = g_cond_new(); +} + +static void coroutine_wait_runnable_locked(CoroutineGThread *co) +{ + while (!co->runnable) { + g_cond_wait(coroutine_cond, g_static_mutex_get_mutex(&coroutine_lock)); + } +} + +static void coroutine_wait_runnable(CoroutineGThread *co) +{ + g_static_mutex_lock(&coroutine_lock); + coroutine_wait_runnable_locked(co); + g_static_mutex_unlock(&coroutine_lock); +} + +static gpointer coroutine_thread(gpointer opaque) +{ + CoroutineGThread *co = opaque; + + g_static_private_set(&coroutine_key, co, NULL); + coroutine_wait_runnable(co); + co->base.entry(co->base.entry_arg); + qemu_coroutine_switch(&co->base, co->base.caller, COROUTINE_TERMINATE); + return NULL; +} + +Coroutine *qemu_coroutine_new(void) +{ + CoroutineGThread *co; + + co = qemu_mallocz(sizeof(*co)); + co->thread = g_thread_create_full(coroutine_thread, co, 0, TRUE, TRUE, + G_THREAD_PRIORITY_NORMAL, NULL); + if (!co->thread) { + qemu_free(co); + return NULL; + } + return &co->base; +} + +void qemu_coroutine_delete(Coroutine *co_) +{ + CoroutineGThread *co = DO_UPCAST(CoroutineGThread, base, co_); + + g_thread_join(co->thread); + qemu_free(co); +} + +CoroutineAction qemu_coroutine_switch(Coroutine *from_, + Coroutine *to_, + CoroutineAction action) +{ + CoroutineGThread *from = DO_UPCAST(CoroutineGThread, base, from_); + CoroutineGThread *to = DO_UPCAST(CoroutineGThread, base, to_); + + g_static_mutex_lock(&coroutine_lock); + from->runnable = false; + from->action = action; + to->runnable = true; + to->action = action; + g_cond_broadcast(coroutine_cond); + + if (action != COROUTINE_TERMINATE) { + coroutine_wait_runnable_locked(from); + } + g_static_mutex_unlock(&coroutine_lock); + return from->action; +} + +Coroutine *qemu_coroutine_self(void) +{ + CoroutineGThread *co = g_static_private_get(&coroutine_key); + + if (!co) { + co = qemu_mallocz(sizeof(*co)); + co->runnable = true; + g_static_private_set(&coroutine_key, co, (GDestroyNotify)qemu_free); + } + + return &co->base; +} + +bool qemu_in_coroutine(void) +{ + CoroutineGThread *co = g_static_private_get(&coroutine_key); + + return co && co->base.caller; +}