qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [RFC][PATCH 01/12] coroutine: Add gtk-vnc coroutines librar


From: Stefan Hajnoczi
Subject: [Qemu-devel] [RFC][PATCH 01/12] coroutine: Add gtk-vnc coroutines library
Date: Sat, 22 Jan 2011 09:29:16 +0000

Asynchronous image format code is becoming very complex.  Let's try
using coroutines to write sequential code without callbacks but use
coroutines to switch stacks under the hood.

Signed-off-by: Stefan Hajnoczi <address@hidden>
---
 Makefile.objs        |    2 +-
 continuation.c       |   87 +++++++++++++++++++++++++++++++++
 continuation.h       |   58 ++++++++++++++++++++++
 coroutine.h          |   72 +++++++++++++++++++++++++++
 coroutine_ucontext.c |  131 ++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 349 insertions(+), 1 deletions(-)
 create mode 100644 continuation.c
 create mode 100644 continuation.h
 create mode 100644 coroutine.h
 create mode 100644 coroutine_ucontext.c

diff --git a/Makefile.objs b/Makefile.objs
index c3e52c5..ae26396 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -15,7 +15,7 @@ oslib-obj-$(CONFIG_POSIX) += oslib-posix.o
 
 block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o
 block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o
-block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
+block-obj-$(CONFIG_POSIX) += posix-aio-compat.o continuation.o 
coroutine_ucontext.o
 block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
 
 block-nested-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o 
vvfat.o
diff --git a/continuation.c b/continuation.c
new file mode 100644
index 0000000..f61aeb5
--- /dev/null
+++ b/continuation.c
@@ -0,0 +1,87 @@
+/*
+ * GTK VNC Widget
+ *
+ * Copyright (C) 2006  Anthony Liguori <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 "continuation.h"
+
+/*
+ * va_args to makecontext() must be type 'int', so passing
+ * the pointer we need may require several int args. This
+ * union is a quick hack to let us do that
+ */
+union cc_arg {
+       void *p;
+       int i[2];
+};
+
+static void continuation_trampoline(int i0, int i1)
+{
+       union cc_arg arg;
+       struct continuation *cc;
+       arg.i[0] = i0;
+       arg.i[1] = i1;
+       cc = arg.p;
+
+       cc->entry(cc);
+}
+
+int cc_init(struct continuation *cc)
+{
+       volatile union cc_arg arg;
+       arg.p = cc;
+       if (getcontext(&cc->uc) == -1)
+               return -1;
+
+       cc->uc.uc_link = &cc->last;
+       cc->uc.uc_stack.ss_sp = cc->stack;
+       cc->uc.uc_stack.ss_size = cc->stack_size;
+       cc->uc.uc_stack.ss_flags = 0;
+
+       makecontext(&cc->uc, (void *)continuation_trampoline, 2, arg.i[0], 
arg.i[1]);
+
+       return 0;
+}
+
+int cc_release(struct continuation *cc)
+{
+       if (cc->release)
+               return cc->release(cc);
+
+       return 0;
+}
+
+int cc_swap(struct continuation *from, struct continuation *to)
+{
+       to->exited = 0;
+       if (getcontext(&to->last) == -1)
+               return -1;
+       else if (to->exited == 0)
+               to->exited = 1;
+       else if (to->exited == 1)
+               return 1;
+
+       return swapcontext(&from->uc, &to->uc);
+}
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff --git a/continuation.h b/continuation.h
new file mode 100644
index 0000000..585788e
--- /dev/null
+++ b/continuation.h
@@ -0,0 +1,58 @@
+/*
+ * GTK VNC Widget
+ *
+ * Copyright (C) 2006  Anthony Liguori <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
+ */
+
+#ifndef _CONTINUATION_H_
+#define _CONTINUATION_H_
+
+#include <ucontext.h>
+
+struct continuation
+{
+       char *stack;
+       size_t stack_size;
+       void (*entry)(struct continuation *cc);
+       int (*release)(struct continuation *cc);
+
+       /* private */
+       ucontext_t uc;
+       ucontext_t last;
+       int exited;
+};
+
+int cc_init(struct continuation *cc);
+
+int cc_release(struct continuation *cc);
+
+/* you can use an uninitialized struct continuation for from if you do not have
+   the current continuation handy. */
+int cc_swap(struct continuation *from, struct continuation *to);
+
+#define offset_of(type, member) ((unsigned long)(&((type *)0)->member))
+#define container_of(obj, type, member) \
+        (type *)(((char *)obj) - offset_of(type, member))
+
+#endif
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff --git a/coroutine.h b/coroutine.h
new file mode 100644
index 0000000..5316535
--- /dev/null
+++ b/coroutine.h
@@ -0,0 +1,72 @@
+/*
+ * GTK VNC Widget
+ *
+ * Copyright (C) 2006  Anthony Liguori <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
+ */
+
+#ifndef _COROUTINE_H_
+#define _COROUTINE_H_
+
+#define WITH_UCONTEXT 1
+
+#if WITH_UCONTEXT
+#include "continuation.h"
+#else
+#include <glib.h>
+#endif
+
+struct coroutine
+{
+       size_t stack_size;
+       void *(*entry)(void *);
+       int (*release)(struct coroutine *);
+
+       /* read-only */
+       int exited;
+
+       /* private */
+       struct coroutine *caller;
+       void *data;
+
+#if WITH_UCONTEXT
+       struct continuation cc;
+#else
+       GThread *thread;
+       gboolean runnable;
+#endif
+};
+
+int coroutine_init(struct coroutine *co);
+
+int coroutine_release(struct coroutine *co);
+
+void *coroutine_swap(struct coroutine *from, struct coroutine *to, void *arg);
+
+struct coroutine *coroutine_self(void);
+
+void *coroutine_yieldto(struct coroutine *to, void *arg);
+
+void *coroutine_yield(void *arg);
+
+#endif
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff --git a/coroutine_ucontext.c b/coroutine_ucontext.c
new file mode 100644
index 0000000..25e8687
--- /dev/null
+++ b/coroutine_ucontext.c
@@ -0,0 +1,131 @@
+/*
+ * GTK VNC Widget
+ *
+ * Copyright (C) 2006  Anthony Liguori <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 <sys/types.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "coroutine.h"
+
+int coroutine_release(struct coroutine *co)
+{
+       return cc_release(&co->cc);
+}
+
+static int _coroutine_release(struct continuation *cc)
+{
+       struct coroutine *co = container_of(cc, struct coroutine, cc);
+
+       if (co->release) {
+               int ret = co->release(co);
+               if (ret < 0)
+                       return ret;
+       }
+
+       co->caller = NULL;
+
+       return 0;
+}
+
+static void coroutine_trampoline(struct continuation *cc)
+{
+       struct coroutine *co = container_of(cc, struct coroutine, cc);
+       co->data = co->entry(co->data);
+}
+
+int coroutine_init(struct coroutine *co)
+{
+       if (co->stack_size == 0)
+               co->stack_size = 16 << 20;
+
+       co->cc.stack_size = co->stack_size;
+       co->cc.stack = mmap(0, co->stack_size,
+                           PROT_READ | PROT_WRITE,
+                           MAP_PRIVATE | MAP_ANONYMOUS,
+                           -1, 0);
+       if (co->cc.stack == MAP_FAILED)
+               return -1;
+       co->cc.entry = coroutine_trampoline;
+       co->cc.release = _coroutine_release;
+       co->exited = 0;
+
+       return cc_init(&co->cc);
+}
+
+#if 0
+static __thread struct coroutine leader;
+static __thread struct coroutine *current;
+#else
+static struct coroutine leader;
+static struct coroutine *current;
+#endif
+
+struct coroutine *coroutine_self(void)
+{
+       if (current == NULL)
+               current = &leader;
+       return current;
+}
+
+void *coroutine_swap(struct coroutine *from, struct coroutine *to, void *arg)
+{
+       int ret;
+       to->data = arg;
+       current = to;
+       ret = cc_swap(&from->cc, &to->cc);
+       if (ret == 0)
+               return from->data;
+       else if (ret == 1) {
+               coroutine_release(to);
+               current = &leader;
+               to->exited = 1;
+               return to->data;
+       }
+
+       return NULL;
+}
+
+void *coroutine_yieldto(struct coroutine *to, void *arg)
+{
+       if (to->caller) {
+               fprintf(stderr, "Co-routine is re-entering itself\n");
+               abort();
+       }
+       to->caller = coroutine_self();
+       return coroutine_swap(coroutine_self(), to, arg);
+}
+
+void *coroutine_yield(void *arg)
+{
+       struct coroutine *to = coroutine_self()->caller;
+       if (!to) {
+               fprintf(stderr, "Co-routine is yielding to no one\n");
+               abort();
+       }
+       coroutine_self()->caller = NULL;
+       return coroutine_swap(coroutine_self(), to, arg);
+}
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
-- 
1.7.2.3




reply via email to

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