diff mbox

coroutine: Implement coroutines using gthread

Message ID 1307641266-7726-1-git-send-email-aneesh.kumar@linux.vnet.ibm.com
State New
Headers show

Commit Message

Aneesh Kumar K.V June 9, 2011, 5:41 p.m. UTC
On platforms that doesn't support makecontext use gthread
based coroutine implementation.

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@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

Comments

Stefan Hajnoczi June 10, 2011, 10:24 a.m. UTC | #1
On Thu, Jun 9, 2011 at 6:41 PM, Aneesh Kumar K.V
<aneesh.kumar@linux.vnet.ibm.com> wrote:
> On platforms that doesn't support makecontext use gthread
> based coroutine implementation.
>
> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@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 @@
> +/*

A summary of the purpose of this file would be nice.

> + *
> + * 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, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA

The URL is preferred over the postal address:

 * 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 "qemu-common.h"
> +#include "qemu-coroutine-int.h"
> +#include <glib.h>

It's good practice to include system headers first unless you are
explicitly setting a magic #define to affect the system header.

> +
> +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;

Why pthread_key_t instead of GStaticPrivate?

I've followed the main control flow once but am going to review in
more detail to make sure.

Stefan
Aneesh Kumar K.V June 10, 2011, 11:09 a.m. UTC | #2
On Fri, 10 Jun 2011 11:24:20 +0100, Stefan Hajnoczi <stefanha@gmail.com> wrote:
> On Thu, Jun 9, 2011 at 6:41 PM, Aneesh Kumar K.V
> <aneesh.kumar@linux.vnet.ibm.com> wrote:
> > On platforms that doesn't support makecontext use gthread
> > based coroutine implementation.
> >
> > Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@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 @@
> > +/*
> 
> A summary of the purpose of this file would be nice.
> 
> > + *
> > + * 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, write to the Free Software
> > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
> 
> The URL is preferred over the postal address:
> 
>  * 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/>.

inherited that header from gtk-vnc version of original file
> 
> > + */
> > +
> > +#include "qemu-common.h"
> > +#include "qemu-coroutine-int.h"
> > +#include <glib.h>

Will fix.

> 
> It's good practice to include system headers first unless you are
> explicitly setting a magic #define to affect the system header.
> 
> > +
> > +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;
> 
> Why pthread_key_t instead of GStaticPrivate?

No specific reason other than I was not aware of GStaticPrivate. 

> 
> I've followed the main control flow once but am going to review in
> more detail to make sure.
> 

Thanks.
-aneesh
Stefan Hajnoczi June 10, 2011, 12:12 p.m. UTC | #3
On Fri, Jun 10, 2011 at 12:09 PM, Aneesh Kumar K.V
<aneesh.kumar@linux.vnet.ibm.com> wrote:
> On Fri, 10 Jun 2011 11:24:20 +0100, Stefan Hajnoczi <stefanha@gmail.com> wrote:
>> On Thu, Jun 9, 2011 at 6:41 PM, Aneesh Kumar K.V
>> <aneesh.kumar@linux.vnet.ibm.com> wrote:
>> > On platforms that doesn't support makecontext use gthread
>> > based coroutine implementation.
>> >
>> > Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@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 @@
>> > +/*
>>
>> A summary of the purpose of this file would be nice.
>>
>> > + *
>> > + * 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, write to the Free Software
>> > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
>>
>> The URL is preferred over the postal address:
>>
>>  * 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/>.
>
> inherited that header from gtk-vnc version of original file
>>
>> > + */
>> > +
>> > +#include "qemu-common.h"
>> > +#include "qemu-coroutine-int.h"
>> > +#include <glib.h>
>
> Will fix.
>
>>
>> It's good practice to include system headers first unless you are
>> explicitly setting a magic #define to affect the system header.
>>
>> > +
>> > +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;
>>
>> Why pthread_key_t instead of GStaticPrivate?
>
> No specific reason other than I was not aware of GStaticPrivate.

It would be worth using that to avoid mixing GThread and pthreads
unnecessarily.  I still need to look at the rest of this patch so
don't worry about sending v2 yet.

Stefan
=?UTF-8?q?Bastien=20Roucari=C3=A8s?= June 11, 2011, 12:27 p.m. UTC | #4
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 <aneesh.kumar@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 <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, 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;
> +}
> +
Stefan Hajnoczi June 12, 2011, 8:54 p.m. UTC | #5
On Sat, Jun 11, 2011 at 1:27 PM, Bastien ROUCARIES
<roucaries.bastien@gmail.com> wrote:
> 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?

libcoro provides a different API where you need to know which
coroutine called you in order to yield back to it later.  Since the
common use-case is to transfer control to a coroutine and then have it
yield back, this caller information is very handy and avoids littering
code with explicit caller variables.

We have ucontext, Win32 Fibers, and now GThread implementations of
coroutines.  I think it's not worth switching to libcoro because it is
just a little different with its own quirks and limitations, not
clearly better.  This code actually started out from the gtk-vnc
coroutines implementation, so we did start by reusing code... :)

Stefan
diff mbox

Patch

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 <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, 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;
+}
+