On platforms that doesn't support makecontext use gthread based coroutine implementation.
Signed-off-by: Aneesh Kumar K.V <aneesh.ku...@linux.vnet.ibm.com> --- NOTE: Tested on linux with force compliation of coroutine-gthread.c Makefile.objs | 5 ++ configure | 18 +++++ coroutine-gthread.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+), 0 deletions(-) create mode 100644 coroutine-gthread.c diff --git a/Makefile.objs b/Makefile.objs index 0f1d7df..d354d3c 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -13,9 +13,14 @@ oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o ####################################################################### # coroutines coroutine-obj-y = qemu-coroutine.o qemu-coroutine-lock.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 + ####################################################################### # block-obj-y is code used by both qemu system emulation and qemu-img diff --git a/configure b/configure index 980914a..529d8c4 100755 --- a/configure +++ b/configure @@ -2568,6 +2568,20 @@ if test "$trace_backend" = "dtrace"; then fi ########################################## +# check if we have makecontext + +ucontext_coroutine=no +cat > $TMPC << EOF +#include <ucontext.h> +int main(void) { makecontext(0, 0, 0); } +EOF +if compile_prog "" "" ; then + ucontext_coroutine=yes +fi + + + +########################################## # End of CC checks # After here, no more $cc or $ld runs @@ -3031,6 +3045,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..37e5a16 --- /dev/null +++ b/coroutine-gthread.c @@ -0,0 +1,172 @@ +/* + * + * Copyright (C) 2006 Anthony Liguori <anth...@codemonkey.ws> + * Copyright (C) 2011 Aneesh Kumar K.V <aneesh.ku...@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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "qemu-common.h" +#include "qemu-coroutine-int.h" +#include <glib.h> + +typedef struct { + Coroutine qemu_co; + GThread *thread; + gboolean runnable; +} CoroutineGthread; + +typedef struct { + /** Currently executing coroutine */ + CoroutineGthread *current; + + /** The default coroutine */ + CoroutineGthread leader; +} CoroutineThreadState; + +static GCond *run_cond; +static GMutex *run_lock; +static pthread_key_t thread_state_key; + +static void qemu_coroutine_thread_cleanup(void *opaque) +{ + CoroutineThreadState *s = opaque; + + qemu_free(s); +} + +static void __attribute__((constructor)) coroutine_system_init(void) +{ + int ret; + if (!g_thread_supported()) { + g_thread_init(NULL); + } + run_cond = g_cond_new(); + run_lock = g_mutex_new(); + + ret = pthread_key_create(&thread_state_key, qemu_coroutine_thread_cleanup); + if (ret != 0) { + fprintf(stderr, "unable to create leader key: %s\n", strerror(errno)); + abort(); + } +} + +static CoroutineThreadState *coroutine_get_thread_state(void) +{ + CoroutineThreadState *s = pthread_getspecific(thread_state_key); + + if (!s) { + s = qemu_mallocz(sizeof(*s)); + s->current = &s->leader; + pthread_setspecific(thread_state_key, s); + } + return s; +} + +static gpointer coroutine_thread(gpointer opaque) +{ + CoroutineGthread *co = opaque; + CoroutineThreadState *s = coroutine_get_thread_state(); + + s->current = co; + + /* Wait for somebody make it runnable */ + g_mutex_lock(run_lock); + while (!co->runnable) { + g_cond_wait(run_cond, run_lock); + } + g_mutex_unlock(run_lock); + /* + * run the coroutine function + * Coroutines can run in parallel. + */ + co->qemu_co.entry(co->qemu_co.entry_arg); + + /* Now yield with terminating status */ + qemu_coroutine_switch(&co->qemu_co, + co->qemu_co.caller, COROUTINE_TERMINATE); + return NULL; +} + +Coroutine *qemu_coroutine_new(void) +{ + CoroutineGthread *co; + if (run_cond == NULL) { + abort(); + } + co = qemu_mallocz(sizeof(*co)); + co->runnable = FALSE; + co->thread = g_thread_create_full(coroutine_thread, co, 0, + FALSE, TRUE, + G_THREAD_PRIORITY_NORMAL, + NULL); + if (co->thread == NULL) { + qemu_free(co); + return NULL; + } + co->qemu_co.caller = NULL; + return &co->qemu_co; +} + +Coroutine *qemu_coroutine_self(void) +{ + CoroutineThreadState *s = coroutine_get_thread_state(); + + return &s->current->qemu_co; +} + +CoroutineAction qemu_coroutine_switch(Coroutine *qemu_co_from, + Coroutine *qemu_co_to, + CoroutineAction action) +{ + CoroutineGthread *from = DO_UPCAST(CoroutineGthread, qemu_co, qemu_co_from); + CoroutineGthread *to = DO_UPCAST(CoroutineGthread, qemu_co, qemu_co_to); + + /* Wakeup the runnable to coroutine */ + g_mutex_lock(run_lock); + to->qemu_co.caller = qemu_co_from; + from->runnable = FALSE; + to->runnable = TRUE; + g_cond_broadcast(run_cond); + g_mutex_unlock(run_lock); + + /* Don't wait if we are going to terminate */ + if (action == COROUTINE_TERMINATE) { + return action; + } + + /* Now wait for somebody to make from runnable */ + g_mutex_lock(run_lock); + while (!from->runnable) { + g_cond_wait(run_cond, run_lock); + } + g_mutex_unlock(run_lock); + return action; +} + +bool qemu_in_coroutine(void) +{ + CoroutineThreadState *s = pthread_getspecific(thread_state_key); + + return s && s->current->qemu_co.caller; +} + +void qemu_coroutine_delete(Coroutine *qemu_co) +{ + CoroutineGthread *co = DO_UPCAST(CoroutineGthread, qemu_co, qemu_co); + qemu_free(co); + return; +} + -- 1.7.4.1