From patchwork Sat Jan 22 09:29:16 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Hajnoczi X-Patchwork-Id: 79989 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 191F4B710A for ; Sat, 22 Jan 2011 20:51:42 +1100 (EST) Received: from localhost ([127.0.0.1]:60061 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Pga8B-0003IH-9G for incoming@patchwork.ozlabs.org; Sat, 22 Jan 2011 04:51:39 -0500 Received: from [140.186.70.92] (port=57026 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PgZnL-000399-5O for qemu-devel@nongnu.org; Sat, 22 Jan 2011 04:30:09 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PgZnG-0001oz-Nh for qemu-devel@nongnu.org; Sat, 22 Jan 2011 04:30:06 -0500 Received: from mtagate4.uk.ibm.com ([194.196.100.164]:33397) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PgZnG-0001lF-7f for qemu-devel@nongnu.org; Sat, 22 Jan 2011 04:30:02 -0500 Received: from d06nrmr1307.portsmouth.uk.ibm.com (d06nrmr1307.portsmouth.uk.ibm.com [9.149.38.129]) by mtagate4.uk.ibm.com (8.13.1/8.13.1) with ESMTP id p0M9ToeK020702 for ; Sat, 22 Jan 2011 09:29:50 GMT Received: from d06av07.portsmouth.uk.ibm.com (d06av07.portsmouth.uk.ibm.com [9.149.37.248]) by d06nrmr1307.portsmouth.uk.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p0M9TrEc1540270 for ; Sat, 22 Jan 2011 09:29:53 GMT Received: from d06av07.portsmouth.uk.ibm.com (loopback [127.0.0.1]) by d06av07.portsmouth.uk.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p0M9ToJN028116 for ; Sat, 22 Jan 2011 02:29:50 -0700 Received: from stefanha-thinkpad.ibm.com (gbv82324-009146172195.uk.ibm.com [9.146.172.195]) by d06av07.portsmouth.uk.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id p0M9TlNV028082; Sat, 22 Jan 2011 02:29:49 -0700 From: Stefan Hajnoczi To: Date: Sat, 22 Jan 2011 09:29:16 +0000 Message-Id: <1295688567-25496-2-git-send-email-stefanha@linux.vnet.ibm.com> X-Mailer: git-send-email 1.7.2.3 In-Reply-To: <1295688567-25496-1-git-send-email-stefanha@linux.vnet.ibm.com> References: <1295688567-25496-1-git-send-email-stefanha@linux.vnet.ibm.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) Cc: Kevin Wolf , Anthony Liguori , Stefan Hajnoczi Subject: [Qemu-devel] [RFC][PATCH 01/12] coroutine: Add gtk-vnc coroutines library X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- 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 + * + * 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 + * + * 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 + +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 + * + * 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 +#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 + * + * 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 +#include +#include +#include +#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: + */