diff mbox

[RFC,05/12] coroutine: Add coroutines

Message ID 1295688567-25496-6-git-send-email-stefanha@linux.vnet.ibm.com
State New
Headers show

Commit Message

Stefan Hajnoczi Jan. 22, 2011, 9:29 a.m. UTC
Add functions to create coroutines and transfer control into a coroutine
and back out again.

Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
---
 Makefile.objs    |    2 +-
 qemu-coroutine.c |   40 ++++++++++++++++++++++++++++
 qemu-coroutine.h |   76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 117 insertions(+), 1 deletions(-)
 create mode 100644 qemu-coroutine.c
 create mode 100644 qemu-coroutine.h

Comments

Avi Kivity Jan. 26, 2011, 3:29 p.m. UTC | #1
On 01/22/2011 11:29 AM, Stefan Hajnoczi wrote:
> Add functions to create coroutines and transfer control into a coroutine
> and back out again.
>
>
> +
> +struct Coroutine {
> +    struct coroutine co;
> +};
> +
>
> +/**
> + * Coroutine entry point
> + *
> + * When the coroutine is entered for the first time, opaque is passed in as an
> + * argument.
> + *
> + * When this function returns, the coroutine is destroyed automatically and the
> + * return value is passed back to the caller who last entered the coroutine.
> + */
> +typedef void * coroutine_fn CoroutineEntry(void *opaque);

The more modern style is to use the Coroutine structure as argument, and 
let the coroutine function use container_of() to obtain access to its 
own data structures.  Similarly it can store any return value there, 
avoiding casts to and from void pointers.
Anthony Liguori Jan. 26, 2011, 4 p.m. UTC | #2
On 01/26/2011 09:29 AM, Avi Kivity wrote:
> On 01/22/2011 11:29 AM, Stefan Hajnoczi wrote:
>> Add functions to create coroutines and transfer control into a coroutine
>> and back out again.
>>
>>
>> +
>> +struct Coroutine {
>> +    struct coroutine co;
>> +};
>> +
>>
>> +/**
>> + * Coroutine entry point
>> + *
>> + * When the coroutine is entered for the first time, opaque is 
>> passed in as an
>> + * argument.
>> + *
>> + * When this function returns, the coroutine is destroyed 
>> automatically and the
>> + * return value is passed back to the caller who last entered the 
>> coroutine.
>> + */
>> +typedef void * coroutine_fn CoroutineEntry(void *opaque);
>
> The more modern style is to use the Coroutine structure as argument, 
> and let the coroutine function use container_of() to obtain access to 
> its own data structures.  Similarly it can store any return value 
> there, avoiding casts to and from void pointers.

This is old (shared) code from other projects.  My plan is to split into 
a separate library instead of actually including this in QEMU.

Regards,

Anthony Liguori
Stefan Hajnoczi Jan. 27, 2011, 9:40 a.m. UTC | #3
On Wed, Jan 26, 2011 at 05:29:23PM +0200, Avi Kivity wrote:
> On 01/22/2011 11:29 AM, Stefan Hajnoczi wrote:
> >Add functions to create coroutines and transfer control into a coroutine
> >and back out again.
> >
> >
> >+
> >+struct Coroutine {
> >+    struct coroutine co;
> >+};
> >+
> >
> >+/**
> >+ * Coroutine entry point
> >+ *
> >+ * When the coroutine is entered for the first time, opaque is passed in as an
> >+ * argument.
> >+ *
> >+ * When this function returns, the coroutine is destroyed automatically and the
> >+ * return value is passed back to the caller who last entered the coroutine.
> >+ */
> >+typedef void * coroutine_fn CoroutineEntry(void *opaque);
> 
> The more modern style is to use the Coroutine structure as argument,
> and let the coroutine function use container_of() to obtain access
> to its own data structures.  Similarly it can store any return value
> there, avoiding casts to and from void pointers.

Yes, container_of() would be nice but we need to be careful to support
pooling Coroutine structs.  Or maybe just pool the mmaped stacks.

Stefan
diff mbox

Patch

diff --git a/Makefile.objs b/Makefile.objs
index ae26396..7933fc9 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 continuation.o coroutine_ucontext.o
+block-obj-$(CONFIG_POSIX) += posix-aio-compat.o continuation.o coroutine_ucontext.o qemu-coroutine.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/qemu-coroutine.c b/qemu-coroutine.c
new file mode 100644
index 0000000..dd2cd8e
--- /dev/null
+++ b/qemu-coroutine.c
@@ -0,0 +1,40 @@ 
+/*
+ * QEMU coroutines
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ *  Stefan Hajnoczi    <stefanha@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "coroutine.h"
+
+#include "qemu-common.h"
+#include "qemu-coroutine.h"
+
+struct Coroutine {
+    struct coroutine co;
+};
+
+Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
+{
+    Coroutine *coroutine = qemu_mallocz(sizeof(*coroutine));
+
+    coroutine->co.entry = entry;
+    coroutine_init(&coroutine->co);
+    return coroutine;
+}
+
+void *qemu_coroutine_enter(Coroutine *coroutine, void *opaque)
+{
+    return coroutine_yieldto(&coroutine->co, opaque);
+}
+
+void * coroutine_fn qemu_coroutine_yield(void *opaque)
+{
+    return coroutine_yield(opaque);
+}
diff --git a/qemu-coroutine.h b/qemu-coroutine.h
new file mode 100644
index 0000000..22fe4ea
--- /dev/null
+++ b/qemu-coroutine.h
@@ -0,0 +1,76 @@ 
+/*
+ * QEMU coroutine implementation
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ *  Stefan Hajnoczi    <stefanha@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_COROUTINE_H
+#define QEMU_COROUTINE_H
+
+/**
+ * Mark a function that executes in coroutine context
+ *
+ * Functions that execute in coroutine context cannot be called directly from
+ * normal functions.  In the future it would be nice to enable compiler or
+ * static checker support for catching such errors.  This annotation might make
+ * it possible and in the meantime it serves as documentation.
+ *
+ * For example:
+ *
+ *   static void coroutine_fn foo(void) {
+ *       ....
+ *   }
+ */
+#define coroutine_fn
+
+typedef struct Coroutine Coroutine;
+
+/**
+ * Coroutine entry point
+ *
+ * When the coroutine is entered for the first time, opaque is passed in as an
+ * argument.
+ *
+ * When this function returns, the coroutine is destroyed automatically and the
+ * return value is passed back to the caller who last entered the coroutine.
+ */
+typedef void * coroutine_fn CoroutineEntry(void *opaque);
+
+/**
+ * Create a new coroutine
+ *
+ * Use qemu_coroutine_enter() to actually transfer control to the coroutine.
+ */
+Coroutine *qemu_coroutine_create(CoroutineEntry *entry);
+
+/**
+ * Transfer control to a coroutine
+ *
+ * The opaque argument is made available to the coroutine either as the entry
+ * function argument if this is the first time a new coroutine is entered, or
+ * as the return value from qemu_coroutine_yield().
+ *
+ * The return value from this function is either an opaque value yielded by the
+ * coroutine or the coroutine entry function return value when the coroutine
+ * terminates.
+ */
+void *qemu_coroutine_enter(Coroutine *coroutine, void *opaque);
+
+/**
+ * Transfer control back to a coroutine's caller
+ *
+ * The opaque argument is returned from the calling qemu_coroutine_enter().
+ *
+ * The return value is the argument passed back in from the next
+ * qemu_coroutine_enter().
+ */
+void * coroutine_fn qemu_coroutine_yield(void *opaque);
+
+#endif /* QEMU_COROUTINE_H */