From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([140.186.70.92]:53362) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QNgjJ-00032f-5h for qemu-devel@nongnu.org; Sat, 21 May 2011 03:36:11 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QNgjF-0003Ww-Vf for qemu-devel@nongnu.org; Sat, 21 May 2011 03:36:09 -0400 Received: from mail-qy0-f173.google.com ([209.85.216.173]:37245) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QNgjF-0003Ws-II for qemu-devel@nongnu.org; Sat, 21 May 2011 03:36:05 -0400 Received: by qyk36 with SMTP id 36so99006qyk.4 for ; Sat, 21 May 2011 00:36:04 -0700 (PDT) MIME-Version: 1.0 In-Reply-To: <1305889177-10490-2-git-send-email-stefanha@linux.vnet.ibm.com> References: <1305889177-10490-1-git-send-email-stefanha@linux.vnet.ibm.com> <1305889177-10490-2-git-send-email-stefanha@linux.vnet.ibm.com> From: Blue Swirl Date: Sat, 21 May 2011 10:35:44 +0300 Message-ID: Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Subject: Re: [Qemu-devel] [PATCH v4 1/3] coroutine: introduce coroutines List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Stefan Hajnoczi Cc: Kevin Wolf , Paolo Bonzini , Anthony Liguori , Venkateswararao Jujjuri , qemu-devel@nongnu.org On Fri, May 20, 2011 at 1:59 PM, Stefan Hajnoczi wrote: > From: Kevin Wolf > > Asynchronous code is becoming very complex. =C2=A0At the same time > synchronous code is growing because it is convenient to write. > Sometimes duplicate code paths are even added, one synchronous and the > other asynchronous. =C2=A0This patch introduces coroutines which allow co= de > that looks synchronous but is asynchronous under the covers. > > A coroutine has its own stack and is therefore able to preserve state > across blocking operations, which traditionally require callback > functions and manual marshalling of parameters. > > Creating and starting a coroutine is easy: > > =C2=A0coroutine =3D qemu_coroutine_create(my_coroutine); > =C2=A0qemu_coroutine_enter(coroutine, my_data); > > The coroutine then executes until it returns or yields: > > =C2=A0void coroutine_fn my_coroutine(void *opaque) { > =C2=A0 =C2=A0 =C2=A0MyData *my_data =3D opaque; > > =C2=A0 =C2=A0 =C2=A0/* do some work */ > > =C2=A0 =C2=A0 =C2=A0qemu_coroutine_yield(); > > =C2=A0 =C2=A0 =C2=A0/* do some more work */ > =C2=A0} > > Yielding switches control back to the caller of qemu_coroutine_enter(). > This is typically used to switch back to the main thread's event loop > after issuing an asynchronous I/O request. =C2=A0The request callback wil= l > then invoke qemu_coroutine_enter() once more to switch back to the > coroutine. > > Note that if coroutines are used only from threads which hold the global > mutex they will never execute concurrently. =C2=A0This makes programming = with > coroutines easier than with threads. =C2=A0Race conditions cannot occur s= ince > only one coroutine may be active at any time. =C2=A0Other coroutines can = only > run across yield. > > This coroutines implementation is based on the gtk-vnc implementation > written by Anthony Liguori but it has been > significantly rewritten by Kevin Wolf to use > setjmp()/longjmp() instead of the more expensive swapcontext() and by > Paolo Bonzini for Windows Fibers support. > > Signed-off-by: Kevin Wolf > Signed-off-by: Stefan Hajnoczi > --- > =C2=A0Makefile.objs =C2=A0 =C2=A0 =C2=A0 =C2=A0| =C2=A0 =C2=A07 ++ > =C2=A0coroutine-ucontext.c | =C2=A0229 ++++++++++++++++++++++++++++++++++= ++++++++++++++++ > =C2=A0coroutine-win32.c =C2=A0 =C2=A0| =C2=A0 92 ++++++++++++++++++++ > =C2=A0qemu-coroutine-int.h | =C2=A0 48 +++++++++++ > =C2=A0qemu-coroutine.c =C2=A0 =C2=A0 | =C2=A0 75 ++++++++++++++++ > =C2=A0qemu-coroutine.h =C2=A0 =C2=A0 | =C2=A0 95 +++++++++++++++++++++ > =C2=A0trace-events =C2=A0 =C2=A0 =C2=A0 =C2=A0 | =C2=A0 =C2=A05 + > =C2=A07 files changed, 551 insertions(+), 0 deletions(-) > =C2=A0create mode 100644 coroutine-ucontext.c > =C2=A0create mode 100644 coroutine-win32.c > =C2=A0create mode 100644 qemu-coroutine-int.h > =C2=A0create mode 100644 qemu-coroutine.c > =C2=A0create mode 100644 qemu-coroutine.h > > diff --git a/Makefile.objs b/Makefile.objs > index 4478c61..a8dbd15 100644 > --- a/Makefile.objs > +++ b/Makefile.objs > @@ -11,6 +11,12 @@ oslib-obj-$(CONFIG_WIN32) +=3D oslib-win32.o qemu-thre= ad-win32.o > =C2=A0oslib-obj-$(CONFIG_POSIX) +=3D oslib-posix.o qemu-thread-posix.o > > =C2=A0###################################################################= #### > +# coroutines > +coroutine-obj-y =3D qemu-coroutine.o > +coroutine-obj-$(CONFIG_POSIX) +=3D coroutine-ucontext.o > +coroutine-obj-$(CONFIG_WIN32) +=3D coroutine-win32.o > + > +####################################################################### > =C2=A0# block-obj-y is code used by both qemu system emulation and qemu-i= mg > > =C2=A0block-obj-y =3D cutils.o cache-utils.o qemu-malloc.o qemu-option.o = module.o async.o > @@ -67,6 +73,7 @@ common-obj-y +=3D readline.o console.o cursor.o qemu-er= ror.o > =C2=A0common-obj-y +=3D $(oslib-obj-y) > =C2=A0common-obj-$(CONFIG_WIN32) +=3D os-win32.o > =C2=A0common-obj-$(CONFIG_POSIX) +=3D os-posix.o > +common-obj-y +=3D $(coroutine-obj-y) > > =C2=A0common-obj-y +=3D tcg-runtime.o host-utils.o > =C2=A0common-obj-y +=3D irq.o ioport.o input.o > diff --git a/coroutine-ucontext.c b/coroutine-ucontext.c > new file mode 100644 > index 0000000..bcea2bd > --- /dev/null > +++ b/coroutine-ucontext.c > @@ -0,0 +1,229 @@ > +/* > + * ucontext coroutine initialization code > + * > + * Copyright (C) 2006 =C2=A0Anthony Liguori > + * Copyright (C) 2011 =C2=A0Kevin Wolf > + * > + * 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. =C2=A0See the GN= U > + * 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 . > + */ > + > +/* XXX Is there a nicer way to disable glibc's stack check for longjmp? = */ > +#ifdef _FORTIFY_SOURCE > +#undef _FORTIFY_SOURCE > +#endif > +#include > +#include > +#include > +#include This would break OpenBSD build: CC coroutine-ucontext.o /src/qemu/coroutine-ucontext.c:28:22: warning: ucontext.h: No such file r directory /src/qemu/coroutine-ucontext.c: In function 'coroutine_new': /src/qemu/coroutine-ucontext.c:144: warning: implicit declaration of fun tion 'getcontext' /src/qemu/coroutine-ucontext.c:144: warning: nested extern declaration o 'getcontext' /src/qemu/coroutine-ucontext.c:152: error: 'ucontext_t' has no member na ed 'uc_link' /src/qemu/coroutine-ucontext.c:153: error: 'ucontext_t' has no member na ed 'uc_stack' /src/qemu/coroutine-ucontext.c:154: error: 'ucontext_t' has no member na ed 'uc_stack' /src/qemu/coroutine-ucontext.c:155: error: 'ucontext_t' has no member na ed 'uc_stack' /src/qemu/coroutine-ucontext.c:159: warning: implicit declaration of fun tion 'makecontext' /src/qemu/coroutine-ucontext.c:159: warning: nested extern declaration o 'makecontext' /src/qemu/coroutine-ucontext.c:164: warning: implicit declaration of fun tion 'swapcontext' /src/qemu/coroutine-ucontext.c:164: warning: nested extern declaration o 'swapcontext' Unfortunately these functions are not available on OpenBSD. I don't know which replacements can be used. What is gtk-vnc using on OpenBSD? > +#include "qemu-common.h" > +#include "qemu-coroutine-int.h" > + > +enum { > + =C2=A0 =C2=A0/* Maximum free pool size prevents holding too many freed = coroutines */ > + =C2=A0 =C2=A0POOL_MAX_SIZE =3D 64, > +}; > + > +typedef struct { > + =C2=A0 =C2=A0Coroutine base; > + =C2=A0 =C2=A0void *stack; > + =C2=A0 =C2=A0jmp_buf env; > +} CoroutineUContext; > + > +/** > + * Per-thread coroutine bookkeeping > + */ > +typedef struct { > + =C2=A0 =C2=A0/** Currently executing coroutine */ > + =C2=A0 =C2=A0Coroutine *current; > + > + =C2=A0 =C2=A0/** Free list to speed up creation */ > + =C2=A0 =C2=A0QLIST_HEAD(, Coroutine) pool; > + =C2=A0 =C2=A0unsigned int pool_size; > + > + =C2=A0 =C2=A0/** The default coroutine */ > + =C2=A0 =C2=A0CoroutineUContext leader; > +} CoroutineThreadState; > + > +static pthread_key_t thread_state_key; > + > +/* > + * 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 { > + =C2=A0 =C2=A0void *p; > + =C2=A0 =C2=A0int i[2]; > +}; > + > +static CoroutineThreadState *coroutine_get_thread_state(void) > +{ > + =C2=A0 =C2=A0CoroutineThreadState *s =3D pthread_getspecific(thread_sta= te_key); > + > + =C2=A0 =C2=A0if (!s) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s =3D qemu_mallocz(sizeof(*s)); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->current =3D &s->leader.base; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0QLIST_INIT(&s->pool); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0pthread_setspecific(thread_state_key, s); > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0return s; > +} > + > +static void qemu_coroutine_thread_cleanup(void *opaque) > +{ > + =C2=A0 =C2=A0CoroutineThreadState *s =3D opaque; > + =C2=A0 =C2=A0Coroutine *co; > + =C2=A0 =C2=A0Coroutine *tmp; > + > + =C2=A0 =C2=A0QLIST_FOREACH_SAFE(co, &s->pool, pool_next, tmp) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_free(DO_UPCAST(CoroutineUContext, base,= co)->stack); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_free(co); > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0qemu_free(s); > +} > + > +static void __attribute__((constructor)) coroutine_init(void) > +{ > + =C2=A0 =C2=A0int ret; > + > + =C2=A0 =C2=A0ret =3D pthread_key_create(&thread_state_key, qemu_corouti= ne_thread_cleanup); > + =C2=A0 =C2=A0if (ret !=3D 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "unable to create leader key= : %s\n", strerror(errno)); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0abort(); > + =C2=A0 =C2=A0} > +} > + > +static void coroutine_trampoline(int i0, int i1) > +{ > + =C2=A0 =C2=A0union cc_arg arg; > + =C2=A0 =C2=A0CoroutineUContext *self; > + =C2=A0 =C2=A0Coroutine *co; > + > + =C2=A0 =C2=A0arg.i[0] =3D i0; > + =C2=A0 =C2=A0arg.i[1] =3D i1; > + =C2=A0 =C2=A0self =3D arg.p; > + =C2=A0 =C2=A0co =3D &self->base; > + > + =C2=A0 =C2=A0/* Initialize longjmp environment and switch back the call= er */ > + =C2=A0 =C2=A0if (!setjmp(self->env)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0longjmp(*(jmp_buf *)co->entry_arg, 1); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0while (true) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0co->entry(co->entry_arg); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_coroutine_switch(co, co->caller, COROUT= INE_TERMINATE); > + =C2=A0 =C2=A0} > +} > + > +static Coroutine *coroutine_new(void) > +{ > + =C2=A0 =C2=A0const size_t stack_size =3D 4 << 20; > + =C2=A0 =C2=A0CoroutineUContext *co; > + =C2=A0 =C2=A0ucontext_t old_uc, uc; > + =C2=A0 =C2=A0jmp_buf old_env; > + =C2=A0 =C2=A0union cc_arg arg; > + > + =C2=A0 =C2=A0/* The ucontext functions preserve signal masks which incu= rs a system call > + =C2=A0 =C2=A0 * overhead. =C2=A0setjmp()/longjmp() does not preserve si= gnal masks but only > + =C2=A0 =C2=A0 * works on the current stack. =C2=A0Since we need a way t= o create and switch to > + =C2=A0 =C2=A0 * a new stack, use the ucontext functions for that but se= tjmp()/longjmp() > + =C2=A0 =C2=A0 * for everything else. > + =C2=A0 =C2=A0 */ > + > + =C2=A0 =C2=A0if (getcontext(&uc) =3D=3D -1) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return NULL; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0co =3D qemu_mallocz(sizeof(*co)); > + =C2=A0 =C2=A0co->stack =3D qemu_malloc(stack_size); > + =C2=A0 =C2=A0co->base.entry_arg =3D &old_env; /* stash away our jmp_buf= */ > + > + =C2=A0 =C2=A0uc.uc_link =3D &old_uc; > + =C2=A0 =C2=A0uc.uc_stack.ss_sp =3D co->stack; > + =C2=A0 =C2=A0uc.uc_stack.ss_size =3D stack_size; > + =C2=A0 =C2=A0uc.uc_stack.ss_flags =3D 0; > + > + =C2=A0 =C2=A0arg.p =3D co; > + > + =C2=A0 =C2=A0makecontext(&uc, (void (*)(void))coroutine_trampoline, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A02, arg.i[0], arg= .i[1]); > + > + =C2=A0 =C2=A0/* swapcontext() in, longjmp() back out */ > + =C2=A0 =C2=A0if (!setjmp(old_env)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0swapcontext(&old_uc, &uc); > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0return &co->base; > +} > + > +Coroutine *qemu_coroutine_new(void) > +{ > + =C2=A0 =C2=A0CoroutineThreadState *s =3D coroutine_get_thread_state(); > + =C2=A0 =C2=A0Coroutine *co; > + > + =C2=A0 =C2=A0co =3D QLIST_FIRST(&s->pool); > + =C2=A0 =C2=A0if (co) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0QLIST_REMOVE(co, pool_next); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->pool_size--; > + =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0co =3D coroutine_new(); > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0return co; > +} > + > +void qemu_coroutine_delete(Coroutine *co_) > +{ > + =C2=A0 =C2=A0CoroutineThreadState *s =3D coroutine_get_thread_state(); > + =C2=A0 =C2=A0CoroutineUContext *co =3D DO_UPCAST(CoroutineUContext, bas= e, co_); > + > + =C2=A0 =C2=A0if (s->pool_size < POOL_MAX_SIZE) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0QLIST_INSERT_HEAD(&s->pool, &co->base, pool_= next); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0co->base.caller =3D NULL; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->pool_size++; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0qemu_free(co->stack); > + =C2=A0 =C2=A0qemu_free(co); > +} > + > +CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0CoroutineActi= on action) > +{ > + =C2=A0 =C2=A0CoroutineUContext *from =3D DO_UPCAST(CoroutineUContext, b= ase, from_); > + =C2=A0 =C2=A0CoroutineUContext *to =3D DO_UPCAST(CoroutineUContext, bas= e, to_); > + =C2=A0 =C2=A0CoroutineThreadState *s =3D coroutine_get_thread_state(); > + =C2=A0 =C2=A0int ret; > + > + =C2=A0 =C2=A0s->current =3D to_; > + > + =C2=A0 =C2=A0ret =3D setjmp(from->env); > + =C2=A0 =C2=A0if (ret =3D=3D 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0longjmp(to->env, action); > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0return ret; > +} > + > +Coroutine *qemu_coroutine_self(void) > +{ > + =C2=A0 =C2=A0CoroutineThreadState *s =3D coroutine_get_thread_state(); > + > + =C2=A0 =C2=A0return s->current; > +} > + > +bool qemu_in_coroutine(void) > +{ > + =C2=A0 =C2=A0CoroutineThreadState *s =3D pthread_getspecific(thread_sta= te_key); > + > + =C2=A0 =C2=A0return s && s->current->caller; > +} > diff --git a/coroutine-win32.c b/coroutine-win32.c > new file mode 100644 > index 0000000..2215ae5 > --- /dev/null > +++ b/coroutine-win32.c > @@ -0,0 +1,92 @@ > +/* > + * Win32 coroutine initialization code > + * > + * Copyright (c) 2011 Kevin Wolf > + * > + * Permission is hereby granted, free of charge, to any person obtaining= a copy > + * of this software and associated documentation files (the "Software"),= to deal > + * in the Software without restriction, including without limitation the= rights > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or = sell > + * copies of the Software, and to permit persons to whom the Software is > + * furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be includ= ed in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRE= SS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILI= TY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHA= LL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR = OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISI= NG FROM, > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING= S IN > + * THE SOFTWARE. > + */ > + > +#include "qemu-common.h" > +#include "qemu-coroutine-int.h" > + > +typedef struct > +{ ERROR: open brace '{' following struct go on the same line #397: FILE: coroutine-win32.c:29: +typedef struct +{ total: 1 errors, 0 warnings, 566 lines checked Your patch has style problems, please review. If any of these errors are false positives report them to the maintainer, see CHECKPATCH in MAINTAINERS. > + =C2=A0 =C2=A0Coroutine base; > + > + =C2=A0 =C2=A0LPVOID fiber; > + =C2=A0 =C2=A0CoroutineAction action; > +} CoroutineWin32; > + > +static __thread CoroutineWin32 leader; > +static __thread Coroutine *current; > + > +CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0CoroutineActi= on action) > +{ > + =C2=A0 =C2=A0CoroutineWin32 *from =3D DO_UPCAST(CoroutineWin32, base, f= rom_); > + =C2=A0 =C2=A0CoroutineWin32 *to =3D DO_UPCAST(CoroutineWin32, base, to_= ); > + > + =C2=A0 =C2=A0current =3D to_; > + > + =C2=A0 =C2=A0to->action =3D action; > + =C2=A0 =C2=A0SwitchToFiber(to->fiber); > + =C2=A0 =C2=A0return from->action; > +} > + > +static void CALLBACK coroutine_trampoline(void *co_) > +{ > + =C2=A0 =C2=A0Coroutine *co =3D co_; > + > + =C2=A0 =C2=A0while (true) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0co->entry(co->entry_arg); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_coroutine_switch(co, co->caller, COROUT= INE_TERMINATE); > + =C2=A0 =C2=A0} > +} > + > +Coroutine *qemu_coroutine_new(void) > +{ > + =C2=A0 =C2=A0const size_t stack_size =3D 4 << 20; > + =C2=A0 =C2=A0CoroutineWin32 *co; > + > + =C2=A0 =C2=A0co =3D qemu_mallocz(sizeof(*co)); > + =C2=A0 =C2=A0co->fiber =3D CreateFiber(stack_size, coroutine_trampoline= , &co->base); > + =C2=A0 =C2=A0return &co->base; > +} > + > +void qemu_coroutine_delete(Coroutine *co_) > +{ > + =C2=A0 =C2=A0CoroutineWin32 *co =3D DO_UPCAST(CoroutineWin32, base, co_= ); > + > + =C2=A0 =C2=A0DeleteFiber(co->fiber); > + =C2=A0 =C2=A0qemu_free(co); > +} > + > +Coroutine *qemu_coroutine_self(void) > +{ > + =C2=A0 =C2=A0if (!current) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0current =3D &leader.base; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0leader.fiber =3D ConvertThreadToFiber(NULL); > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0return current; > +} > + > +bool qemu_in_coroutine(void) > +{ > + =C2=A0 =C2=A0return current && current->caller; > +} > diff --git a/qemu-coroutine-int.h b/qemu-coroutine-int.h > new file mode 100644 > index 0000000..64915c2 > --- /dev/null > +++ b/qemu-coroutine-int.h > @@ -0,0 +1,48 @@ > +/* > + * Coroutine internals > + * > + * Copyright (c) 2011 Kevin Wolf > + * > + * Permission is hereby granted, free of charge, to any person obtaining= a copy > + * of this software and associated documentation files (the "Software"),= to deal > + * in the Software without restriction, including without limitation the= rights > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or = sell > + * copies of the Software, and to permit persons to whom the Software is > + * furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be includ= ed in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRE= SS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILI= TY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHA= LL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR = OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISI= NG FROM, > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING= S IN > + * THE SOFTWARE. > + */ > + > +#ifndef QEMU_COROUTINE_INT_H > +#define QEMU_COROUTINE_INT_H > + > +#include "qemu-queue.h" > +#include "qemu-coroutine.h" > + > +typedef enum { > + =C2=A0 =C2=A0COROUTINE_YIELD =3D 1, > + =C2=A0 =C2=A0COROUTINE_TERMINATE =3D 2, > +} CoroutineAction; > + > +struct Coroutine { > + =C2=A0 =C2=A0CoroutineEntry *entry; > + =C2=A0 =C2=A0void *entry_arg; > + =C2=A0 =C2=A0Coroutine *caller; > + =C2=A0 =C2=A0QLIST_ENTRY(Coroutine) pool_next; > +}; > + > +Coroutine *qemu_coroutine_new(void); > +void qemu_coroutine_delete(Coroutine *co); > +CoroutineAction qemu_coroutine_switch(Coroutine *from, Coroutine *to, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0CoroutineActi= on action); > + > +#endif > diff --git a/qemu-coroutine.c b/qemu-coroutine.c > new file mode 100644 > index 0000000..600be26 > --- /dev/null > +++ b/qemu-coroutine.c > @@ -0,0 +1,75 @@ > +/* > + * QEMU coroutines > + * > + * Copyright IBM, Corp. 2011 > + * > + * Authors: > + * =C2=A0Stefan Hajnoczi =C2=A0 =C2=A0 > + * =C2=A0Kevin Wolf =C2=A0 =C2=A0 =C2=A0 =C2=A0 > + * > + * This work is licensed under the terms of the GNU LGPL, version 2 or l= ater. > + * See the COPYING.LIB file in the top-level directory. > + * > + */ > + > +#include "trace.h" > +#include "qemu-common.h" > +#include "qemu-coroutine.h" > +#include "qemu-coroutine-int.h" > + > +Coroutine *qemu_coroutine_create(CoroutineEntry *entry) > +{ > + =C2=A0 =C2=A0Coroutine *co =3D qemu_coroutine_new(); > + =C2=A0 =C2=A0co->entry =3D entry; > + =C2=A0 =C2=A0return co; > +} > + > +static void coroutine_swap(Coroutine *from, Coroutine *to) > +{ > + =C2=A0 =C2=A0CoroutineAction ret; > + > + =C2=A0 =C2=A0ret =3D qemu_coroutine_switch(from, to, COROUTINE_YIELD); > + > + =C2=A0 =C2=A0switch (ret) { > + =C2=A0 =C2=A0case COROUTINE_YIELD: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0case COROUTINE_TERMINATE: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0trace_qemu_coroutine_terminate(to); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_coroutine_delete(to); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0default: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0abort(); > + =C2=A0 =C2=A0} > +} > + > +void qemu_coroutine_enter(Coroutine *co, void *opaque) > +{ > + =C2=A0 =C2=A0Coroutine *self =3D qemu_coroutine_self(); > + > + =C2=A0 =C2=A0trace_qemu_coroutine_enter(self, co, opaque); > + > + =C2=A0 =C2=A0if (co->caller) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "Co-routine re-entered recur= sively\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0abort(); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0co->caller =3D self; > + =C2=A0 =C2=A0co->entry_arg =3D opaque; > + =C2=A0 =C2=A0coroutine_swap(self, co); > +} > + > +void coroutine_fn qemu_coroutine_yield(void) > +{ > + =C2=A0 =C2=A0Coroutine *self =3D qemu_coroutine_self(); > + =C2=A0 =C2=A0Coroutine *to =3D self->caller; > + > + =C2=A0 =C2=A0trace_qemu_coroutine_yield(self, to); > + > + =C2=A0 =C2=A0if (!to) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "Co-routine is yielding to n= o one\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0abort(); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0self->caller =3D NULL; > + =C2=A0 =C2=A0coroutine_swap(self, to); > +} > diff --git a/qemu-coroutine.h b/qemu-coroutine.h > new file mode 100644 > index 0000000..08255c7 > --- /dev/null > +++ b/qemu-coroutine.h > @@ -0,0 +1,95 @@ > +/* > + * QEMU coroutine implementation > + * > + * Copyright IBM, Corp. 2011 > + * > + * Authors: > + * =C2=A0Stefan Hajnoczi =C2=A0 =C2=A0 > + * > + * This work is licensed under the terms of the GNU LGPL, version 2 or l= ater. > + * See the COPYING.LIB file in the top-level directory. > + * > + */ > + > +#ifndef QEMU_COROUTINE_H > +#define QEMU_COROUTINE_H > + > +#include > + > +/** > + * Coroutines are a mechanism for stack switching and can be used for > + * cooperative userspace threading. =C2=A0These functions provide a simp= le but > + * useful flavor of coroutines that is suitable for writing sequential c= ode, > + * rather than callbacks, for operations that need to give up control wh= ile > + * waiting for events to complete. > + * > + * These functions are re-entrant and may be used outside the global mut= ex. > + */ > + > +/** > + * Mark a function that executes in coroutine context > + * > + * Functions that execute in coroutine context cannot be called directly= from > + * normal functions. =C2=A0In the future it would be nice to enable comp= iler or > + * static checker support for catching such errors. =C2=A0This annotatio= n might make > + * it possible and in the meantime it serves as documentation. > + * > + * For example: > + * > + * =C2=A0 static void coroutine_fn foo(void) { > + * =C2=A0 =C2=A0 =C2=A0 .... > + * =C2=A0 } > + */ > +#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 > + * execution continues in 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 corout= ine. > + */ > +Coroutine *qemu_coroutine_create(CoroutineEntry *entry); > + > +/** > + * Transfer control to a coroutine > + * > + * The opaque argument is passed as the argument to the entry point when > + * entering the coroutine for the first time. =C2=A0It is subsequently i= gnored. > + */ > +void qemu_coroutine_enter(Coroutine *coroutine, void *opaque); > + > +/** > + * Transfer control back to a coroutine's caller > + * > + * This function does not return until the coroutine is re-entered using > + * qemu_coroutine_enter(). > + */ > +void coroutine_fn qemu_coroutine_yield(void); > + > +/** > + * Get the currently executing coroutine > + */ > +Coroutine *coroutine_fn qemu_coroutine_self(void); > + > +/** > + * Return whether or not currently inside a coroutine > + * > + * This can be used to write functions that work both when in coroutine = context > + * and when not in coroutine context. =C2=A0Note that such functions can= not use the > + * coroutine_fn annotation since they work outside coroutine context. > + */ > +bool qemu_in_coroutine(void); > + > +#endif /* QEMU_COROUTINE_H */ > diff --git a/trace-events b/trace-events > index 385cb00..e21e67d 100644 > --- a/trace-events > +++ b/trace-events > @@ -377,3 +377,8 @@ disable xen_unmap_block(void* addr, unsigned long siz= e) "%p, size %#lx" > > =C2=A0# exec.c > =C2=A0disable qemu_put_ram_ptr(void* addr) "%p" > + > +# qemu-coroutine.c > +disable qemu_coroutine_enter(void *from, void *to, void *opaque) "from %= p to %p opaque %p" > +disable qemu_coroutine_yield(void *from, void *to) "from %p to %p" > +disable qemu_coroutine_terminate(void *co) "self %p" > -- > 1.7.4.4 > >