qemu-devel
[Top][All Lists]
Advanced

[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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]