Learn to use the GCoroutine library instead of qemu own coroutine implementation.
GCoroutine is hosted on github: https://github.com/elmarco/gcoroutine This allows to share the same coroutine implementation between various projects (gtk-vnc and spice-gtk). It is related to the effort to push coroutine support in GLib. See also: https://bugzilla.gnome.org/show_bug.cgi?id=719362 Notes: - there are no GCoroutine releases, the API isn't frozen - this patch hasn't been thoroughly tested - GCoroutine doesn't implement pools yet - GCoroutine is missing sigaltstack based coroutines - spice-gtk and gtk-vnc patches are being worked on --- Makefile.objs | 11 +++- configure | 26 +++++++- coroutine-gcoroutine.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++ include/block/coroutine.h | 15 +++++ 4 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 coroutine-gcoroutine.c diff --git a/Makefile.objs b/Makefile.objs index abeb902..250c58a 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -14,10 +14,17 @@ block-obj-$(CONFIG_WIN32) += aio-win32.o block-obj-y += block/ block-obj-y += qemu-io-cmds.o -block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o -block-obj-y += qemu-coroutine-sleep.o block-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o +ifeq ($(CONFIG_COROUTINE_BACKEND),gcoroutine) +coroutine-gcoroutine.o-cflags := $(GCOROUTINE_CFLAGS) +coroutine-gcoroutine.o-libs := $(GCOROUTINE_LIBS) +else +block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o +endif +block-obj-y += qemu-coroutine-io.o +block-obj-y += qemu-coroutine-sleep.o + block-obj-m = block/ diff --git a/configure b/configure index cae588c..e36ee74 100755 --- a/configure +++ b/configure @@ -1381,7 +1381,7 @@ Advanced options (experts only): --disable-seccomp disable seccomp support --enable-seccomp enable seccomp support --with-coroutine=BACKEND coroutine backend. Supported options: - gthread, ucontext, sigaltstack, windows + gcoroutine, gthread, ucontext, sigaltstack, windows --disable-coroutine-pool disable coroutine freelist (worse performance) --enable-coroutine-pool enable coroutine freelist (better performance) --enable-glusterfs enable GlusterFS backend @@ -3870,8 +3870,16 @@ EOF fi fi +if $pkg_config gcoroutine-1.0 --exists; then + gcoroutine_cflags=`$pkg_config --cflags gcoroutine-1.0` + gcoroutine_libs=`$pkg_config --libs gcoroutine-1.0` + gcoroutine=yes +fi + if test "$coroutine" = ""; then - if test "$mingw32" = "yes"; then + if test "$gcoroutine" = "yes"; then + coroutine=gcoroutine + elif test "$mingw32" = "yes"; then coroutine=win32 elif test "$ucontext_works" = "yes"; then coroutine=ucontext @@ -3898,6 +3906,11 @@ else error_exit "only the 'windows' coroutine backend is valid for Windows" fi ;; + gcoroutine) + if test "$gcoroutine" != "yes"; then + feature_not_found "gcoroutine" + fi + ;; *) error_exit "unknown coroutine backend $coroutine" ;; @@ -4213,7 +4226,7 @@ EOF fi # prepend pixman and ftd flags after all config tests are done -QEMU_CFLAGS="$pixman_cflags $fdt_cflags $QEMU_CFLAGS" +QEMU_CFLAGS="$pixman_cflags $fdt_cflags $gcoroutine_cflags $QEMU_CFLAGS" libs_softmmu="$pixman_libs $libs_softmmu" echo "Install prefix $prefix" @@ -4733,6 +4746,13 @@ else echo "CONFIG_COROUTINE_POOL=0" >> $config_host_mak fi +if test "$coroutine" = "gcoroutine" ; then + echo "CONFIG_GCOROUTINE=1" >> $config_host_mak + echo "GCOROUTINE_LIBS=$gcoroutine_libs" >> $config_host_mak + echo "GCOROUTINE_CFLAGS=$gcoroutine_cflags" >> $config_host_mak +fi + + if test "$open_by_handle_at" = "yes" ; then echo "CONFIG_OPEN_BY_HANDLE=y" >> $config_host_mak fi diff --git a/coroutine-gcoroutine.c b/coroutine-gcoroutine.c new file mode 100644 index 0000000..3d5b749 --- /dev/null +++ b/coroutine-gcoroutine.c @@ -0,0 +1,154 @@ +/* + * GCoroutine wrapper + * + * Copyright (C) 2014 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, see <http://www.gnu.org/licenses/>. + */ + +#include <gcoroutine.h> +#include "qemu-common.h" +#include "block/coroutine_int.h" + +Coroutine *qemu_coroutine_self(void) +{ + return (Coroutine *)g_coroutine_self(); +} + +bool qemu_in_coroutine(void) +{ + return g_in_coroutine(); +} + +void qemu_co_queue_init(CoQueue *queue) +{ + g_co_queue_init(queue); +} + +void coroutine_fn qemu_co_queue_wait(CoQueue *queue) +{ + g_co_queue_yield(queue, NULL); +} + +bool coroutine_fn qemu_co_queue_next(CoQueue *queue) +{ + return g_co_queue_schedule(queue, 1) == 1; +} + +void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue) +{ + g_co_queue_schedule(queue, -1); +} + +bool qemu_co_enter_next(CoQueue *queue) +{ + if (g_co_queue_is_empty(queue)) + return false; + + g_co_queue_resume_head(queue, NULL); + + return true; +} + +bool qemu_co_queue_empty(CoQueue *queue) +{ + return g_co_queue_is_empty(queue); +} + +void qemu_co_mutex_init(CoMutex *mutex) +{ + g_co_mutex_init(mutex); +} + +void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex) +{ + g_co_mutex_lock(mutex); +} + +void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex) +{ + g_co_mutex_unlock(mutex); +} + +void qemu_co_rwlock_init(CoRwlock *lock) +{ + g_co_rw_lock_init(lock); +} + +void qemu_co_rwlock_rdlock(CoRwlock *lock) +{ + g_co_rw_lock_reader_lock(lock); +} + +void qemu_co_rwlock_unlock(CoRwlock *lock) +{ + if (lock->writer) + g_co_rw_lock_writer_unlock(lock); + else + g_co_rw_lock_reader_unlock(lock); +} + +void qemu_co_rwlock_wrlock(CoRwlock *lock) +{ + g_co_rw_lock_writer_lock(lock); +} + +static gboolean coroutine_end_cb(gpointer data) +{ + GCoroutine *co = data; + + g_coroutine_unref(co); + + return FALSE; +} + +static gpointer coroutine_func(gpointer data) +{ + CoroutineEntry *entry = data; + + data = g_coroutine_yield(NULL); + + entry(data); + + g_idle_add(coroutine_end_cb, g_coroutine_self()); + + return NULL; +} + +Coroutine *qemu_coroutine_create(CoroutineEntry *entry) +{ + GCoroutine *co = g_coroutine_new(coroutine_func); + + if (!co) + return NULL; + + g_coroutine_resume(co, entry); + + return (Coroutine*)co; +} + +void qemu_coroutine_enter(Coroutine *co, void *opaque) +{ + g_coroutine_resume((GCoroutine*)co, opaque); +} + +void coroutine_fn qemu_coroutine_yield(void) +{ + g_coroutine_yield(NULL); +} + +void qemu_coroutine_adjust_pool_size(int n) +{ + g_warning("no pool yet"); +} diff --git a/include/block/coroutine.h b/include/block/coroutine.h index 793df0e..cb1fbfc 100644 --- a/include/block/coroutine.h +++ b/include/block/coroutine.h @@ -19,6 +19,9 @@ #include "qemu/typedefs.h" #include "qemu/queue.h" #include "qemu/timer.h" +#ifdef CONFIG_GCOROUTINE +#include <gcoroutine.h> +#endif /** * Coroutines are a mechanism for stack switching and can be used for @@ -103,9 +106,13 @@ bool qemu_in_coroutine(void); * them later. They provide the fundamental primitives on which coroutine locks * are built. */ +#ifdef CONFIG_GCOROUTINE +typedef GCoQueue CoQueue; +#else typedef struct CoQueue { QTAILQ_HEAD(, Coroutine) entries; } CoQueue; +#endif /** * Initialise a CoQueue. This must be called before any other operation is used @@ -145,10 +152,14 @@ bool qemu_co_queue_empty(CoQueue *queue); /** * Provides a mutex that can be used to synchronise coroutines */ +#ifdef CONFIG_GCOROUTINE +typedef GCoMutex CoMutex; +#else typedef struct CoMutex { bool locked; CoQueue queue; } CoMutex; +#endif /** * Initialises a CoMutex. This must be called before any other operation is used @@ -168,11 +179,15 @@ void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex); */ void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex); +#ifdef CONFIG_GCOROUTINE +typedef GCoRWLock CoRwlock; +#else typedef struct CoRwlock { bool writer; int reader; CoQueue queue; } CoRwlock; +#endif /** * Initialises a CoRwlock. This must be called before any other operation -- 2.1.0