[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH] coroutine: Implement coroutines using gthread
From: |
Aneesh Kumar K.V |
Subject: |
[Qemu-devel] [PATCH] coroutine: Implement coroutines using gthread |
Date: |
Thu, 9 Jun 2011 23:11:06 +0530 |
On platforms that doesn't support makecontext use gthread
based coroutine implementation.
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;
+}
+
--
1.7.4.1
- [Qemu-devel] [PATCH] coroutine: Implement coroutines using gthread,
Aneesh Kumar K.V <=