This class provided interfaces and helper functions for an event loop context modelled closely on GLib's GMainContext. It drives a number of QSources, which are in turn modelled on GLib GSources, and driven in very similar fashion (prepare/poll/check/dispatch interfaces). It also provides a mechanism for creating a thread and driving itself.
QContexts are attached to the QOM composition tree via unique paths and make use of that to provide global registration/query mechanisms. QContexts implementations can be instantiated, referenced, and run via the -object command-line parameter. Signed-off-by: Michael Roth <mdr...@linux.vnet.ibm.com> --- include/qcontext/qcontext.h | 76 +++++++++++++ qcontext/qcontext.c | 252 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 328 insertions(+) create mode 100644 include/qcontext/qcontext.h create mode 100644 qcontext/qcontext.c diff --git a/include/qcontext/qcontext.h b/include/qcontext/qcontext.h new file mode 100644 index 0000000..845d1a0 --- /dev/null +++ b/include/qcontext/qcontext.h @@ -0,0 +1,76 @@ +/* + * QContext: QEMU event loop context class + * + * Copyright IBM Corp. 2013 + * + * Authors: + * Michael Roth <mdr...@linux.vnet.ibm.com> + * + * 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 QCONTEXT_H +#define QCONTEXT_H + +#include "qom/object.h" +#include "qcontext/qsource.h" +#include "qapi/error.h" +#include "qemu/thread.h" + +/* QContext base class */ + +typedef struct QContext QContext; + +typedef struct QContextClass { + ObjectClass parent_class; + + /* called after QContext id property has been set */ + void (*set_id_hook)(QContext *ctx, const char *name, Error **errp); + + /* QContext event loop functions, abstract interfaces */ + bool (*prepare)(QContext *ctx, int *timeout); + bool (*poll)(QContext *ctx, int timeout); + bool (*check)(QContext *ctx); + void (*dispatch)(QContext *ctx); + void (*notify)(QContext *ctx); + + /* QSource registration, abstract interfaces */ + void (*attach)(QContext *ctx, QSource *qsource, Error **errp); + void (*detach)(QContext *ctx, QSource *qsource, Error **errp); + QSource *(*find_source_by_name)(QContext *ctx, const char *name); +} QContextClass; + +struct QContext { + Object parent_obj; + Object *container; + char *id; + QemuThread thread; + bool threaded; + bool should_run; +}; + +#define TYPE_QCONTEXT "qcontext" +#define QCONTEXT(obj) OBJECT_CHECK(QContext, (obj), TYPE_QCONTEXT) +#define QCONTEXT_CLASS(klass) OBJECT_CLASS_CHECK(QContextClass, (klass), TYPE_QCONTEXT) +#define QCONTEXT_GET_CLASS(obj) OBJECT_GET_CLASS(QContextClass, (obj), TYPE_QCONTEXT) + +/* wrapper functions for object methods */ + +bool qcontext_prepare(QContext *ctx, int *timeout); +bool qcontext_poll(QContext *ctx, int timeout); +bool qcontext_check(QContext *ctx); +void qcontext_dispatch(QContext *ctx); +void qcontext_notify(QContext *ctx); +void qcontext_attach(QContext *ctx, QSource *qsource, Error **errp); +void qcontext_detach(QContext *ctx, QSource *qsource, Error **errp); +QSource *qcontext_find_source_by_name(QContext *ctx, const char *name); + +/* helper functions for working with qcontexts */ + +QContext *qcontext_find_by_name(const char *name, Error **errp); +bool qcontext_iterate(QContext *ctx, bool blocking); +void qcontext_create_thread(QContext *ctx); +void qcontext_stop_thread(QContext *ctx); +void qcontext_register_types(void); + +#endif /* QCONTEXT_H */ diff --git a/qcontext/qcontext.c b/qcontext/qcontext.c new file mode 100644 index 0000000..88cc86f --- /dev/null +++ b/qcontext/qcontext.c @@ -0,0 +1,252 @@ +/* + * QContext: QEMU event loop context class + * + * Copyright IBM Corp. 2013 + * + * Authors: + * Michael Roth <mdr...@linux.vnet.ibm.com> + * + * 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 <stdio.h> +#include "qom/object.h" +#include "qemu/module.h" +#include "qcontext/qcontext.h" +#include "qapi/error.h" +#include "string.h" + +/* TODO: this is for compatibility with -object, but really these + * should probably live in /qcontexts or something + */ +#define QCONTEXT_ROOT_CONTAINER "/objects" + +/* QContext property accessors */ + +static char *qcontext_get_id(Object *obj, Error **errp) +{ + QContext *ctx = QCONTEXT(obj); + + return ctx->id ? g_strdup(ctx->id) : NULL; +} + +static void qcontext_set_id(Object *obj, const char *id, Error **errp) +{ + QContext *ctx = QCONTEXT(obj); + QContextClass *ctxk = QCONTEXT_GET_CLASS(ctx); + Object *root_container = container_get(object_get_root(), + QCONTEXT_ROOT_CONTAINER); + + if (id) { + object_property_add_child(root_container, id, OBJECT(ctx), errp); + ctx->id = g_strdup(id); + } else { + ctx->id = object_property_add_unnamed_child(root_container, + OBJECT(ctx), errp); + } + + if (ctxk->set_id_hook) { + ctxk->set_id_hook(ctx, id, errp); + } +} + +static char *qcontext_get_threaded(Object *obj, Error **errp) +{ + QContext *ctx = QCONTEXT(obj); + + return ctx->threaded ? g_strdup("yes") : g_strdup("no"); +} + +static void qcontext_set_threaded(Object *obj, const char *threaded, + Error **errp) +{ + QContext *ctx = QCONTEXT(obj); + + if (strcmp(threaded, "yes") == 0) { + ctx->threaded = true; + ctx->should_run = true; + } else if (strcmp(threaded, "no") == 0) { + ctx->threaded = false; + ctx->should_run = false; + } else { + error_setg(errp, + "invalid value for \"threaded\"," + " must specify \"yes\" or \"no\""); + } +} + +/* QOM interfaces */ + +static void qcontext_initfn(Object *obj) +{ + /* note: controlling these as properties is somewhat awkward. these are + * really static initialization parameters, but we do it this way so we + * can instantiate from the command-line via -object. + */ + object_property_add_str(obj, "id", + qcontext_get_id, + qcontext_set_id, + NULL); + object_property_add_str(obj, "threaded", + qcontext_get_threaded, + qcontext_set_threaded, + NULL); +} + +static void qcontext_init_completionfn(Object *obj) +{ + QContext *ctx = QCONTEXT(obj); + QContextClass *ctxk = QCONTEXT_GET_CLASS(ctx); + gchar *path, *id; + + /* this means we were created via -object, and were added to + * the qom tree outside of the "id" property setter. Update + * our internal structures to reflect this, and execute the + * set_id_hook() accordingly + */ + if (!ctx->id) { + path = object_get_canonical_path(obj); + id = g_strrstr(path, "/") + 1; + ctx->id = g_strdup(id); + g_free(path); + if (ctxk->set_id_hook) { + ctxk->set_id_hook(ctx, ctx->id, NULL); + } + } + + if (ctx->threaded) { + qcontext_create_thread(ctx); + } +} + +static void qcontext_finalizefn(Object *obj) +{ + QContext *ctx = QCONTEXT(obj); + + if (ctx->threaded) { + qcontext_stop_thread(ctx); + } +} + +static void qcontext_class_initfn(ObjectClass *class, void *data) +{ +} + +static const TypeInfo qcontext_type_info = { + .name = TYPE_QCONTEXT, + .parent = TYPE_OBJECT, + .instance_size = sizeof(QContext), + .instance_init = qcontext_initfn, + .instance_init_completion = qcontext_init_completionfn, + .instance_finalize = qcontext_finalizefn, + .class_size = sizeof(QContextClass), + .class_init = qcontext_class_initfn, + .abstract = true, /* this should be abstract, just for testing */ +}; + +void qcontext_register_types(void) +{ + type_register_static(&qcontext_type_info); +} + +/* FIXME: for some very strange reason, the constructor function this + * generates doesn't get executed for qemu. it does for test-qcontext + * though, and in both cases the constructor for glib-qcontext gets + * executed okay, so for now just do registration for qcontext there + * as well by making qcontext_register_types() a global and calling it + * from there + */ +//type_init(qcontext_register_types) + +/* QContext method wrappers. Somewhat redundant but it saves on typing */ + +bool qcontext_prepare(QContext *ctx, int *timeout) +{ + return QCONTEXT_GET_CLASS(ctx)->prepare(ctx, timeout); +} + +bool qcontext_poll(QContext *ctx, int timeout) +{ + return QCONTEXT_GET_CLASS(ctx)->poll(ctx, timeout); +} + +bool qcontext_check(QContext *ctx) +{ + return QCONTEXT_GET_CLASS(ctx)->check(ctx); +} + +void qcontext_dispatch(QContext *ctx) +{ + QCONTEXT_GET_CLASS(ctx)->dispatch(ctx); +} + +void qcontext_notify(QContext *ctx) +{ + QCONTEXT_GET_CLASS(ctx)->notify(ctx); +} + +void qcontext_attach(QContext *ctx, QSource *source, Error **errp) +{ + QCONTEXT_GET_CLASS(ctx)->attach(ctx, source, errp); +} + +void qcontext_detach(QContext *ctx, QSource *source, Error **errp) +{ + QCONTEXT_GET_CLASS(ctx)->detach(ctx, source, errp); +} + +QSource *qcontext_find_source_by_name(QContext *ctx, const char *name) +{ + return QCONTEXT_GET_CLASS(ctx)->find_source_by_name(ctx, name); +} + +/* Helper functions for working with QContexts */ + +QContext *qcontext_find_by_name(const char *name, Error **errp) +{ + char path[256]; + + sprintf(path, "%s/%s", QCONTEXT_ROOT_CONTAINER, name); + return QCONTEXT(object_resolve_path_type(path, TYPE_QCONTEXT, NULL)); +} + +bool qcontext_iterate(QContext *ctx, bool blocking) +{ + int timeout = 0; + + if (qcontext_prepare(ctx, &timeout)) { + qcontext_dispatch(ctx); + return true; + } + + if (qcontext_poll(ctx, blocking ? timeout : 0) && + qcontext_check(ctx)) { + qcontext_dispatch(ctx); + return true; + } + + return false; +} + +static void *qcontext_thread_fn(void *opaque) +{ + QContext *ctx = opaque; + while (ctx->should_run) { + qcontext_iterate(ctx, true); + } + return NULL; +} + +void qcontext_create_thread(QContext *ctx) +{ + qemu_thread_create(&ctx->thread, qcontext_thread_fn, + ctx, QEMU_THREAD_JOINABLE); +} + +void qcontext_stop_thread(QContext *ctx) +{ + ctx->should_run = false; + qcontext_notify(ctx); + qemu_thread_join(&ctx->thread); + ctx->threaded = false; +} -- 1.7.9.5