From patchwork Fri May 3 16:03:48 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Roth X-Patchwork-Id: 241346 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id DFAAC2C00D4 for ; Sat, 4 May 2013 02:18:21 +1000 (EST) Received: from localhost ([::1]:50303 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UYIXW-0006ML-7t for incoming@patchwork.ozlabs.org; Fri, 03 May 2013 12:08:54 -0400 Received: from eggs.gnu.org ([208.118.235.92]:44546) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UYIU1-0001Ya-8D for qemu-devel@nongnu.org; Fri, 03 May 2013 12:05:19 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UYITz-0007ee-BG for qemu-devel@nongnu.org; Fri, 03 May 2013 12:05:17 -0400 Received: from mail-vb0-x22c.google.com ([2607:f8b0:400c:c02::22c]:56489) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UYITz-0007eZ-73 for qemu-devel@nongnu.org; Fri, 03 May 2013 12:05:15 -0400 Received: by mail-vb0-f44.google.com with SMTP id e13so1480782vbg.31 for ; Fri, 03 May 2013 09:05:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:sender:from:to:cc:subject:date:message-id:x-mailer :in-reply-to:references; bh=51FwXFktnO0tR5et27tZprgfqV0Ung3FEcpYMTMGo/o=; b=C+5zZbKVjMS1Zo3ZQahFe1ORzZUmekc4kATzA9+loAUBd2b+mmfhvjKn29AayY6Eju JhNXwYEFYPmZ1pucpr8MAw28D5KQxLOeqOSqaCtcEvA575FjR6IMWTqkT3IhZm4qH5An MzWVupRg8YVCwuBut1C+cQeiqaRtHCJUAUxm1iTeulO+LEE4pb6QreIypQJLvxpT/cCi v1Fr0o++mcVy6YxSxuZkiv3SKRfWrow9BepLPmkIpON49NJwPl4iQe8D2AfEaEB1YpcE xpPTel7E3JRYTYVk2pVw9k40vTyLoNGq6Y3iXlcyLFjhxXCiDJ85dAiK1/h8fWOaiXeE hD2g== X-Received: by 10.52.94.227 with SMTP id df3mr3282984vdb.48.1367597114862; Fri, 03 May 2013 09:05:14 -0700 (PDT) Received: from localhost (cpe-72-177-121-217.austin.res.rr.com. [72.177.121.217]) by mx.google.com with ESMTPSA id 6sm8891766vei.0.2013.05.03.09.05.13 for (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Fri, 03 May 2013 09:05:14 -0700 (PDT) From: Michael Roth To: qemu-devel@nongnu.org Date: Fri, 3 May 2013 11:03:48 -0500 Message-Id: <1367597032-28934-6-git-send-email-mdroth@linux.vnet.ibm.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1367597032-28934-1-git-send-email-mdroth@linux.vnet.ibm.com> References: <1367597032-28934-1-git-send-email-mdroth@linux.vnet.ibm.com> X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2607:f8b0:400c:c02::22c Cc: pbonzini@redhat.com, aliguori@us.ibm.com, qemulist@gmail.com, stefanha@redhat.com Subject: [Qemu-devel] [PATCH 5/9] GlibQContext: a QContext wrapper around GMainContexts X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This QContext implementation wraps a GMainContext and maps most interfaces to the obvious/corresponding GMainContext interfaces. It can be used as a near drop-in replacement for event loops based directly around GMainContexts, and provides support for direct handling of GSources (which is unique to GlibQContext, since GSources are opaque outside of GLib) This can be used as a stop-gap between GLib event loops and QContext implementations that don't rely on GLib. Most GSources can be trivially ported to QSources to decouple such event loops from GLib, though due to the use of things like GIOChannels where we can't easily take this approach, we'll likely have at least one GlibQContext event loop for the forseeable future. Signed-off-by: Michael Roth --- include/qcontext/glib-qcontext.h | 59 ++++++++ qcontext/glib-qcontext.c | 280 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 339 insertions(+) create mode 100644 include/qcontext/glib-qcontext.h create mode 100644 qcontext/glib-qcontext.c diff --git a/include/qcontext/glib-qcontext.h b/include/qcontext/glib-qcontext.h new file mode 100644 index 0000000..4c01c8a --- /dev/null +++ b/include/qcontext/glib-qcontext.h @@ -0,0 +1,59 @@ +/* + * GlibQContext: GLib-based QContext implementation + * + * Copyright IBM Corp. 2013 + * + * Authors: + * Michael Roth + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef GLIB_QCONTEXT_H +#define GLIB_QCONTEXT_H + +#include +#include "qom/object.h" +#include "qapi/error.h" +#include "qcontext/qcontext.h" + +/* QContextGlib implementation */ + +#define TYPE_GLIB_QCONTEXT "glib-qcontext" +#define GLIB_QCONTEXT(obj) \ + OBJECT_CHECK(GlibQContext, (obj), TYPE_GLIB_QCONTEXT) +#define GLIB_QCONTEXT_CLASS(klass) \ + OBJECT_CLASS_CHECK(GlibQContextClass, (klass), TYPE_GLIB_QCONTEXT) +#define GLIB_QCONTEXT_GET_CLASS(obj) \ + OBJECT_GET_CLASS(GlibQContextClass, (obj), TYPE_GLIB_QCONTEXT) + +#define GLIB_QCONTEXT_MAX_POLL_FDS (2 * 1024) + +typedef struct GlibQSource GlibQSource; + +typedef struct GlibQContext { + /* */ +} GlibQContext; + +typedef struct GlibQContextClass { + QContextClass parent; + + void (*init)(GlibQContext *gctx, const char *name, Error **errp); + void (*set_context)(GlibQContext *gctx, GMainContext *ctx); + GMainContext *(*get_context)(GlibQContext *gctx); +} GlibQContextClass; + +GlibQContext *glib_qcontext_new(const char *id, bool threaded, Error **errp); +GMainContext *glib_qcontext_get_context(GlibQContext *gctx); + +#endif /* GLIB_QCONTEXT_H */ diff --git a/qcontext/glib-qcontext.c b/qcontext/glib-qcontext.c new file mode 100644 index 0000000..bd9b245 --- /dev/null +++ b/qcontext/glib-qcontext.c @@ -0,0 +1,280 @@ +/* + * GlibQContext: GLib-based QContext implementation + * + * Copyright IBM Corp. 2013 + * + * Authors: + * Michael Roth + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include +#include +#include "qom/object.h" +#include "qcontext/qcontext.h" +#include "qcontext/glib-qcontext.h" +#include "qemu/module.h" +#include "qapi/error.h" + +struct GlibQSource { + GSource source; + guint source_id; + char *name; + QSource *qsource; + QTAILQ_ENTRY(GlibQSource) next; +}; + +static gboolean glib_qcontext_gsource_prepare(GSource *source, int *timeout) +{ + GlibQSource *gqsource = (GlibQSource *)source; + QSource *qsource = gqsource->qsource; + + return qsource->source_funcs.prepare(qsource, timeout); +} + +static gboolean glib_qcontext_gsource_check(GSource *source) +{ + GlibQSource *gqsource = (GlibQSource *)source; + QSource *qsource = gqsource->qsource; + + return qsource->source_funcs.check(qsource); +} + +static gboolean glib_qcontext_gsource_dispatch(GSource *source, GSourceFunc cb, + gpointer user_data) +{ + GlibQSource *gqsource = (GlibQSource *)source; + QSource *qsource = gqsource->qsource; + + return qsource->source_funcs.dispatch(qsource); +} + +static void glib_qcontext_gsource_finalize(GSource *source) +{ + GlibQSource *gqsource = (GlibQSource *)source; + QSource *qsource = gqsource->qsource; + + qsource->source_funcs.finalize(qsource); +} + +GSourceFuncs glib_gsource_funcs = { + glib_qcontext_gsource_prepare, + glib_qcontext_gsource_check, + glib_qcontext_gsource_dispatch, + glib_qcontext_gsource_finalize, +}; + +/* external interfaces */ + +static bool glib_qcontext_prepare(QContext *ctx, int *timeout) +{ + GlibQContext *gctx = GLIB_QCONTEXT(ctx); + gint calculated_timeout = 0; + gboolean ret = g_main_context_prepare(gctx->g_main_context, + &gctx->max_priority); + + gctx->n_poll_fds = g_main_context_query(gctx->g_main_context, + gctx->max_priority, + &calculated_timeout, + gctx->poll_fds, + GLIB_QCONTEXT_MAX_POLL_FDS); + if (timeout) { + *timeout = calculated_timeout; + } + + return ret; +} + +static bool glib_qcontext_poll(QContext *ctx, int timeout) +{ + GlibQContext *gctx = GLIB_QCONTEXT(ctx); + + return g_poll(gctx->poll_fds, gctx->n_poll_fds, timeout) > 0; +} + +static bool glib_qcontext_check(QContext *ctx) +{ + GlibQContext *gctx = GLIB_QCONTEXT(ctx); + + return g_main_context_check(gctx->g_main_context, + gctx->max_priority, + gctx->poll_fds, + gctx->n_poll_fds); +} + +static void glib_qcontext_dispatch(QContext *ctx) +{ + GlibQContext *gctx = GLIB_QCONTEXT(ctx); + g_main_context_dispatch(gctx->g_main_context); +} + +static void glib_qcontext_notify(QContext *ctx) +{ + GlibQContext *gctx = GLIB_QCONTEXT(ctx); + GlibQContextClass *gctxk = GLIB_QCONTEXT_GET_CLASS(gctx); + g_main_context_wakeup(gctxk->get_context(gctx)); +} + + +static void glib_qcontext_attach(QContext *ctx, QSource *qsource, Error **errp) +{ + GlibQContext *gctx = GLIB_QCONTEXT(ctx); + GSource *source = g_source_new(&glib_gsource_funcs, sizeof(GlibQSource)+512); + GlibQSource *gqsource = NULL, *new_gqsource = (GlibQSource *)source; + guint i; + + if (qsource->name) { + QTAILQ_FOREACH(gqsource, &gctx->sources, next) { + if (strcmp(gqsource->name, qsource->name) == 0) { + error_setg(errp, "duplicate name associated with source"); + g_source_destroy(source); + return; + } + } + } + + for (i = 0; i < qsource->poll_fds->len; i++) { + GPollFD *pfd = g_array_index(qsource->poll_fds, GPollFD *, i); + g_source_add_poll(source, pfd); + } + + new_gqsource->qsource = qsource; + new_gqsource->source_id = g_source_attach(source, gctx->g_main_context); + new_gqsource->name = g_strdup(qsource->name); + QTAILQ_INSERT_TAIL(&gctx->sources, new_gqsource, next); + qsource->ctx = ctx; +} + +static void glib_qcontext_detach(QContext *ctx, QSource *qsource, Error **errp) +{ + GlibQContext *gctx = GLIB_QCONTEXT(ctx); + GlibQSource *gqsource = NULL; + + QTAILQ_FOREACH(gqsource, &gctx->sources, next) { + if (gqsource->qsource == qsource) { + break; + } + } + + if (gqsource) { + g_free(gqsource->name); + g_source_remove(gqsource->source_id); + QTAILQ_REMOVE(&gctx->sources, gqsource, next); + } + + qsource->ctx = NULL; +} + +static QSource *glib_qcontext_find_source_by_name(QContext *ctx, const char *name) +{ + GlibQContext *gctx = GLIB_QCONTEXT(ctx); + GlibQSource *gqsource = NULL; + + QTAILQ_FOREACH(gqsource, &gctx->sources, next) { + if (strcmp(gqsource->name, name) == 0) { + break; + } + } + + return gqsource->qsource; +} + +static void glib_qcontext_set_id_hook(QContext *ctx, const char *id, + Error **errp) +{ + GlibQContext *gctx = GLIB_QCONTEXT(ctx); + + if (strcmp(id, "main") != 0) { + gctx->g_main_context = g_main_context_new(); + } +} + +/* QOM-driven interfaces */ +static void glib_qcontext_initfn(Object *obj) +{ + GlibQContext *gctx = GLIB_QCONTEXT(obj); + + /* TODO: this will be replaced with a new context if we set an ID + * property other than "main". there's no guarantee we won't attempt + * to spawn a main loop thread for this context (also done via a dynamic + * property so we can be fully instantiated via -object) before this + * happens though. This means we can accidentally execute a number of + * iterations of the default glib context (bad, since that requires + * special handling of BQL) before we switch over to the intended + * context. + * + * We seem to need a realizefn for Objects... + */ + gctx->g_main_context = g_main_context_default(); + QTAILQ_INIT(&gctx->sources); +} + +static void glib_qcontext_class_initfn(ObjectClass *class, void *data) +{ + QContextClass *ctxk = QCONTEXT_CLASS(class); + GlibQContextClass *gctxk = GLIB_QCONTEXT_CLASS(class); + + ctxk->prepare = glib_qcontext_prepare; + ctxk->poll = glib_qcontext_poll; + ctxk->check = glib_qcontext_check; + ctxk->dispatch = glib_qcontext_dispatch; + ctxk->notify = glib_qcontext_notify; + + ctxk->attach = glib_qcontext_attach; + ctxk->detach = glib_qcontext_detach; + ctxk->find_source_by_name = glib_qcontext_find_source_by_name; + ctxk->set_id_hook = glib_qcontext_set_id_hook; + + gctxk->get_context = glib_qcontext_get_context; +} + +static const TypeInfo glib_qcontext_info = { + .name = TYPE_GLIB_QCONTEXT, + .parent = TYPE_QCONTEXT, + .instance_size = sizeof(GlibQContext), + .class_size = sizeof(GlibQContextClass), + .instance_init = glib_qcontext_initfn, + .class_init = glib_qcontext_class_initfn, + .interfaces = (InterfaceInfo[]) { + { }, + }, +}; + +static void glib_qcontext_register_types(void) +{ + type_register_static(&glib_qcontext_info); +} + +type_init(glib_qcontext_register_types) +type_init(qcontext_register_types) + +GlibQContext *glib_qcontext_new(const char *id, bool threaded, Error **errp) +{ + GlibQContext *gctx = GLIB_QCONTEXT(object_new(TYPE_GLIB_QCONTEXT)); + + object_property_set_str(OBJECT(gctx), id, "id", errp); + if (error_is_set(errp)) { + object_unref(OBJECT(gctx)); + g_warning("marker 0"); + return NULL; + } + + object_property_set_str(OBJECT(gctx), + threaded ? "yes" : "no", + "threaded", errp); + if (error_is_set(errp)) { + object_unref(OBJECT(gctx)); + g_warning("marker 1"); + return NULL; + } + + object_init_completion(OBJECT(gctx)); + + return gctx; +} + +GMainContext *glib_qcontext_get_context(GlibQContext *gctx) +{ + return gctx->g_main_context; +}