[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH] coroutine: Implement coroutines using gthread
From: |
Bastien ROUCARIES |
Subject: |
Re: [Qemu-devel] [PATCH] coroutine: Implement coroutines using gthread |
Date: |
Sat, 11 Jun 2011 14:27:24 +0200 |
User-agent: |
KMail/1.13.5 (Linux/2.6.37-trunk-amd64; KDE/4.4.5; x86_64; ; ) |
Le jeudi 9 juin 2011 19:41:06, Aneesh Kumar K.V a écrit :
> On platforms that doesn't support makecontext use gthread
> based coroutine implementation.
Why not using one of the existing lib of coroutine or improving it ?
Could you give some hints ?
Why not use http://cvs.schmorp.de/libcoro/coro.h what is the base of the perl
coroutine lib and thus well tested on a lot of
plateform?
Bastien
>
> Signed-off-by: Aneesh Kumar K.V <address@hidden>
> ---
>
> 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 <address@hidden>
> + * Copyright (C) 2011 Aneesh Kumar K.V <address@hidden>
> + *
> + * 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;
> +}
> +