Skip to content

Commit

Permalink
qapi: Add new visit_complete() function
Browse files Browse the repository at this point in the history
Making each output visitor provide its own output collection
function was the only remaining reason for exposing visitor
sub-types to the rest of the code base.  Add a polymorphic
visit_complete() function which is a no-op for input visitors,
and which populates an opaque pointer for output visitors.  For
maximum type-safety, also add a parameter to the output visitor
constructors with a type-correct version of the output pointer,
and assert that the two uses match.

This approach was considered superior to either passing the
output parameter only during construction (action at a distance
during visit_free() feels awkward) or only during visit_complete()
(defeating type safety makes it easier to use incorrectly).

Most callers were function-local, and therefore a mechanical
conversion; the testsuite was a bit trickier, but the previous
cleanup patch minimized the churn here.

The visit_complete() function may be called at most once; doing
so lets us use transfer semantics rather than duplication or
ref-count semantics to get the just-built output back to the
caller, even though it means our behavior is not idempotent.

Generated code is simplified as follows for events:

|@@ -26,7 +26,7 @@ void qapi_event_send_acpi_device_ost(ACP
|     QDict *qmp;
|     Error *err = NULL;
|     QMPEventFuncEmit emit;
|-    QmpOutputVisitor *qov;
|+    QObject *obj;
|     Visitor *v;
|     q_obj_ACPI_DEVICE_OST_arg param = {
|         info
|@@ -39,8 +39,7 @@ void qapi_event_send_acpi_device_ost(ACP
|
|     qmp = qmp_event_build_dict("ACPI_DEVICE_OST");
|
|-    qov = qmp_output_visitor_new();
|-    v = qmp_output_get_visitor(qov);
|+    v = qmp_output_visitor_new(&obj);
|
|     visit_start_struct(v, "ACPI_DEVICE_OST", NULL, 0, &err);
|     if (err) {
|@@ -55,7 +54,8 @@ void qapi_event_send_acpi_device_ost(ACP
|         goto out;
|     }
|
|-    qdict_put_obj(qmp, "data", qmp_output_get_qobject(qov));
|+    visit_complete(v, &obj);
|+    qdict_put_obj(qmp, "data", obj);
|     emit(QAPI_EVENT_ACPI_DEVICE_OST, qmp, &err);

and for commands:

| {
|     Error *err = NULL;
|-    QmpOutputVisitor *qov = qmp_output_visitor_new();
|     Visitor *v;
|
|-    v = qmp_output_get_visitor(qov);
|+    v = qmp_output_visitor_new(ret_out);
|     visit_type_AddfdInfo(v, "unused", &ret_in, &err);
|-    if (err) {
|-        goto out;
|+    if (!err) {
|+        visit_complete(v, ret_out);
|     }
|-    *ret_out = qmp_output_get_qobject(qov);
|-
|-out:
|     error_propagate(errp, err);

Signed-off-by: Eric Blake <[email protected]>
Message-Id: <[email protected]>
Reviewed-by: Markus Armbruster <[email protected]>
Signed-off-by: Markus Armbruster <[email protected]>
  • Loading branch information
ebblake authored and Markus Armbruster committed Jul 6, 2016
1 parent 23d1705 commit 3b098d5
Show file tree
Hide file tree
Showing 23 changed files with 166 additions and 160 deletions.
9 changes: 4 additions & 5 deletions block/qapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -690,16 +690,15 @@ static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
ImageInfoSpecific *info_spec)
{
QmpOutputVisitor *ov = qmp_output_visitor_new();
QObject *obj, *data;
Visitor *v = qmp_output_visitor_new(&obj);

visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), NULL, &info_spec,
&error_abort);
obj = qmp_output_get_qobject(ov);
visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort);
visit_complete(v, &obj);
assert(qobject_type(obj) == QTYPE_QDICT);
data = qdict_get(qobject_to_qdict(obj), "data");
dump_qobject(func_fprintf, f, 1, data);
visit_free(qmp_output_get_visitor(ov));
visit_free(v);
}

void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
Expand Down
9 changes: 4 additions & 5 deletions blockdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -3950,10 +3950,10 @@ void hmp_drive_add_node(Monitor *mon, const char *optstr)

void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
{
QmpOutputVisitor *ov = qmp_output_visitor_new();
BlockDriverState *bs;
BlockBackend *blk = NULL;
QObject *obj;
Visitor *v = qmp_output_visitor_new(&obj);
QDict *qdict;
Error *local_err = NULL;

Expand All @@ -3972,14 +3972,13 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
}
}

visit_type_BlockdevOptions(qmp_output_get_visitor(ov), NULL, &options,
&local_err);
visit_type_BlockdevOptions(v, NULL, &options, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto fail;
}

obj = qmp_output_get_qobject(ov);
visit_complete(v, &obj);
qdict = qobject_to_qdict(obj);

qdict_flatten(qdict);
Expand Down Expand Up @@ -4020,7 +4019,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
}

fail:
visit_free(qmp_output_get_visitor(ov));
visit_free(v);
}

void qmp_x_blockdev_del(bool has_id, const char *id,
Expand Down
10 changes: 3 additions & 7 deletions docs/qapi-code-gen.txt
Original file line number Diff line number Diff line change
Expand Up @@ -980,17 +980,13 @@ Example:
static void qmp_marshal_output_UserDefOne(UserDefOne *ret_in, QObject **ret_out, Error **errp)
{
Error *err = NULL;
QmpOutputVisitor *qov = qmp_output_visitor_new();
Visitor *v;

v = qmp_output_get_visitor(qov);
v = qmp_output_visitor_new(ret_out);
visit_type_UserDefOne(v, "unused", &ret_in, &err);
if (err) {
goto out;
if (!err) {
visit_complete(v, ret_out);
}
*ret_out = qmp_output_get_qobject(qov);

out:
error_propagate(errp, err);
visit_free(v);
v = qapi_dealloc_visitor_new();
Expand Down
11 changes: 5 additions & 6 deletions hmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1983,15 +1983,14 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict)
Error *err = NULL;
MemdevList *memdev_list = qmp_query_memdev(&err);
MemdevList *m = memdev_list;
StringOutputVisitor *ov;
Visitor *v;
char *str;
int i = 0;


while (m) {
ov = string_output_visitor_new(false);
visit_type_uint16List(string_output_get_visitor(ov), NULL,
&m->value->host_nodes, NULL);
v = string_output_visitor_new(false, &str);
visit_type_uint16List(v, NULL, &m->value->host_nodes, NULL);
monitor_printf(mon, "memory backend: %d\n", i);
monitor_printf(mon, " size: %" PRId64 "\n", m->value->size);
monitor_printf(mon, " merge: %s\n",
Expand All @@ -2002,11 +2001,11 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict)
m->value->prealloc ? "true" : "false");
monitor_printf(mon, " policy: %s\n",
HostMemPolicy_lookup[m->value->policy]);
str = string_output_get_string(ov);
visit_complete(v, &str);
monitor_printf(mon, " host nodes: %s\n", str);

g_free(str);
visit_free(string_output_get_visitor(ov));
visit_free(v);
m = m->next;
i++;
}
Expand Down
11 changes: 7 additions & 4 deletions include/qapi/qmp-output-visitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@

typedef struct QmpOutputVisitor QmpOutputVisitor;

QmpOutputVisitor *qmp_output_visitor_new(void);

QObject *qmp_output_get_qobject(QmpOutputVisitor *v);
Visitor *qmp_output_get_visitor(QmpOutputVisitor *v);
/*
* Create a new QMP output visitor.
*
* If everything else succeeds, pass @result to visit_complete() to
* collect the result of the visit.
*/
Visitor *qmp_output_visitor_new(QObject **result);

#endif
13 changes: 9 additions & 4 deletions include/qapi/string-output-visitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,18 @@
typedef struct StringOutputVisitor StringOutputVisitor;

/*
* Create a new string output visitor.
*
* Using @human creates output that is a bit easier for humans to read
* (for example, showing integer values in both decimal and hex).
*
* If everything else succeeds, pass @result to visit_complete() to
* collect the result of the visit.
*
* The string output visitor does not implement support for visiting
* QAPI structs, alternates, null, or arbitrary QTypes. It also
* requires a non-null list argument to visit_start_list().
*/
StringOutputVisitor *string_output_visitor_new(bool human);

char *string_output_get_string(StringOutputVisitor *v);
Visitor *string_output_get_visitor(StringOutputVisitor *v);
Visitor *string_output_visitor_new(bool human, char **result);

#endif
3 changes: 3 additions & 0 deletions include/qapi/visitor-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ struct Visitor
/* Must be set */
VisitorType type;

/* Must be set for output visitors, optional otherwise. */
void (*complete)(Visitor *v, void *opaque);

/* Must be set */
void (*free)(Visitor *v);
};
Expand Down
50 changes: 30 additions & 20 deletions include/qapi/visitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,15 @@
*
* All of the visitors are created via:
*
* Type *subtype_visitor_new(parameters...);
*
* where Type is either directly 'Visitor *', or is a subtype that can
* be trivially upcast to Visitor * via another function:
*
* Visitor *subtype_get_visitor(SubtypeVisitor *);
* Visitor *subtype_visitor_new(parameters...);
*
* A visitor should be used for exactly one top-level visit_type_FOO()
* or virtual walk, then passed to visit_free() to clean up resources.
* or virtual walk; if that is successful, the caller can optionally
* call visit_complete() (for now, useful only for output visits, but
* safe to call on all visits). Then, regardless of success or
* failure, the user should call visit_free() to clean up resources.
* It is okay to free the visitor without completing the visit, if
* some other error is detected in the meantime. Output visitors
* provide an additional function, for collecting the final results of
* a successful visit: string_output_get_string() and
* qmp_output_get_qobject(); this collection function should not be
* called if any errors were reported during the visit.
* some other error is detected in the meantime.
*
* All QAPI types have a corresponding function with a signature
* roughly compatible with this:
Expand Down Expand Up @@ -123,14 +117,14 @@
* Error *err = NULL;
* Visitor *v;
*
* v = ...obtain input visitor...
* v = FOO_visitor_new(...);
* visit_type_Foo(v, NULL, &f, &err);
* if (err) {
* ...handle error...
* } else {
* ...use f...
* }
* ...clean up v...
* visit_free(v);
* qapi_free_Foo(f);
* </example>
*
Expand All @@ -140,7 +134,7 @@
* Error *err = NULL;
* Visitor *v;
*
* v = ...obtain input visitor...
* v = FOO_visitor_new(...);
* visit_type_FooList(v, NULL, &l, &err);
* if (err) {
* ...handle error...
Expand All @@ -149,7 +143,7 @@
* ...use l->value...
* }
* }
* ...clean up v...
* visit_free(v);
* qapi_free_FooList(l);
* </example>
*
Expand All @@ -159,13 +153,17 @@
* Foo *f = ...obtain populated object...
* Error *err = NULL;
* Visitor *v;
* Type *result;
*
* v = ...obtain output visitor...
* v = FOO_visitor_new(..., &result);
* visit_type_Foo(v, NULL, &f, &err);
* if (err) {
* ...handle error...
* } else {
* visit_complete(v, &result);
* ...use result...
* }
* ...clean up v...
* visit_free(v);
* </example>
*
* When visiting a real QAPI struct, this file provides several
Expand All @@ -191,7 +189,7 @@
* Error *err = NULL;
* int value;
*
* v = ...obtain visitor...
* v = FOO_visitor_new(...);
* visit_start_struct(v, NULL, NULL, 0, &err);
* if (err) {
* goto out;
Expand Down Expand Up @@ -219,7 +217,7 @@
* visit_end_struct(v, NULL);
* out:
* error_propagate(errp, err);
* ...clean up v...
* visit_free(v);
* </example>
*/

Expand All @@ -242,6 +240,18 @@ typedef struct GenericAlternate {

/*** Visitor cleanup ***/

/*
* Complete the visit, collecting any output.
*
* May only be called only once after a successful top-level
* visit_type_FOO() or visit_end_ITEM(), and marks the end of the
* visit. The @opaque pointer should match the output parameter
* passed to the subtype_visitor_new() used to create an output
* visitor, or NULL for any other visitor. Needed for output
* visitors, but may also be called with other visitors.
*/
void visit_complete(Visitor *v, void *opaque);

/*
* Free @v and any resources it has tied up.
*
Expand Down
11 changes: 5 additions & 6 deletions net/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -1198,19 +1198,18 @@ static void netfilter_print_info(Monitor *mon, NetFilterState *nf)
char *str;
ObjectProperty *prop;
ObjectPropertyIterator iter;
StringOutputVisitor *ov;
Visitor *v;

/* generate info str */
object_property_iter_init(&iter, OBJECT(nf));
while ((prop = object_property_iter_next(&iter))) {
if (!strcmp(prop->name, "type")) {
continue;
}
ov = string_output_visitor_new(false);
object_property_get(OBJECT(nf), string_output_get_visitor(ov),
prop->name, NULL);
str = string_output_get_string(ov);
visit_free(string_output_get_visitor(ov));
v = string_output_visitor_new(false, &str);
object_property_get(OBJECT(nf), v, prop->name, NULL);
visit_complete(v, &str);
visit_free(v);
monitor_printf(mon, ",%s=%s", prop->name, str);
g_free(str);
}
Expand Down
8 changes: 8 additions & 0 deletions qapi/qapi-visit-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@
#include "qapi/visitor.h"
#include "qapi/visitor-impl.h"

void visit_complete(Visitor *v, void *opaque)
{
assert(v->type != VISITOR_OUTPUT || v->complete);
if (v->complete) {
v->complete(v, opaque);
}
}

void visit_free(Visitor *v)
{
if (v) {
Expand Down
21 changes: 12 additions & 9 deletions qapi/qmp-output-visitor.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct QmpOutputVisitor
Visitor visitor;
QStack stack; /* Stack of containers that haven't yet been finished */
QObject *root; /* Root of the output visit */
QObject **result; /* User's storage location for result */
};

#define qmp_output_add(qov, name, value) \
Expand Down Expand Up @@ -200,18 +201,17 @@ static void qmp_output_type_null(Visitor *v, const char *name, Error **errp)
/* Finish building, and return the root object.
* The root object is never null. The caller becomes the object's
* owner, and should use qobject_decref() when done with it. */
QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
static void qmp_output_complete(Visitor *v, void *opaque)
{
QmpOutputVisitor *qov = to_qov(v);

/* A visit must have occurred, with each start paired with end. */
assert(qov->root && QTAILQ_EMPTY(&qov->stack));
assert(opaque == qov->result);

qobject_incref(qov->root);
return qov->root;
}

Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
{
return &v->visitor;
*qov->result = qov->root;
qov->result = NULL;
}

static void qmp_output_free(Visitor *v)
Expand All @@ -228,7 +228,7 @@ static void qmp_output_free(Visitor *v)
g_free(qov);
}

QmpOutputVisitor *qmp_output_visitor_new(void)
Visitor *qmp_output_visitor_new(QObject **result)
{
QmpOutputVisitor *v;

Expand All @@ -247,9 +247,12 @@ QmpOutputVisitor *qmp_output_visitor_new(void)
v->visitor.type_number = qmp_output_type_number;
v->visitor.type_any = qmp_output_type_any;
v->visitor.type_null = qmp_output_type_null;
v->visitor.complete = qmp_output_complete;
v->visitor.free = qmp_output_free;

QTAILQ_INIT(&v->stack);
*result = NULL;
v->result = result;

return v;
return &v->visitor;
}
Loading

0 comments on commit 3b098d5

Please sign in to comment.