All of lore.kernel.org
 help / color / mirror / Atom feed
From: Markus Armbruster <armbru@redhat.com>
To: Eric Blake <eblake@redhat.com>
Cc: marcandre.lureau@redhat.com, qemu-devel@nongnu.org,
	Michael Roth <mdroth@linux.vnet.ibm.com>
Subject: Re: [Qemu-devel] [PATCH v9 24/37] qmp: Tighten output visitor rules
Date: Fri, 22 Jan 2016 20:11:53 +0100	[thread overview]
Message-ID: <87d1stfmyu.fsf@blackfin.pond.sub.org> (raw)
In-Reply-To: <1453219845-30939-25-git-send-email-eblake@redhat.com> (Eric Blake's message of "Tue, 19 Jan 2016 09:10:32 -0700")

Eric Blake <eblake@redhat.com> writes:

> Add a new qmp_output_visitor_reset(), which must be called before
> reusing an exising QmpOutputVisitor on a new root object.  Tighten
> assertions to require that qmp_output_get_qobject() can only be
> called after pairing a visit_end_* for every visit_start_* (rather
> than allowing it to return a partially built object), that it must
> not be called unless at least one visit_type_* or visit_start/
> visit_end pair has occurred since creation/reset (the accidental
> return of NULL fixed by commit ab8bf1d7 would have been much
> easier to diagnose),

Makes sense.

>                      and that it may only be called once per visit.

Why?

Does it have a side effect?

> Meanwhile, nothing was using the return value of qmp_output_pop().

Well, pop returns the value popped, otherwise it's not a pop.

> Also, adding a parameter will let us diagnose any programming bugs
> due to mismatched push(struct)/pop(list) or push(list)/pop(struct).

Hmm.

> To keep the semantics of test_visitor_out_empty, we now have to
> explicitly request a top-level visit of a NULL object, by
> implementing the just-added visitor type_null() callback.

The fact that we implement type_null() in the QMP output visitor is
mentioned only in passing, and not clearly.

Could the previous patch implement it for both QMP input and output?

> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: rebase to added patch, squash in more sanity checks, drop
> Marc-Andre's R-b
> v8: rename qmp_output_reset to qmp_output_visitor_reset
> v7: new patch, based on discussion about spapr_drc.c
> ---
>  include/qapi/qmp-output-visitor.h |  1 +
>  include/qapi/visitor-impl.h       |  4 ++--
>  qapi/qmp-output-visitor.c         | 50 +++++++++++++++++++++++----------------
>  tests/test-qmp-output-visitor.c   |  2 ++
>  4 files changed, 35 insertions(+), 22 deletions(-)
>
> diff --git a/include/qapi/qmp-output-visitor.h b/include/qapi/qmp-output-visitor.h
> index 2266770..5093f0d 100644
> --- a/include/qapi/qmp-output-visitor.h
> +++ b/include/qapi/qmp-output-visitor.h
> @@ -21,6 +21,7 @@ typedef struct QmpOutputVisitor QmpOutputVisitor;
>
>  QmpOutputVisitor *qmp_output_visitor_new(void);
>  void qmp_output_visitor_cleanup(QmpOutputVisitor *v);
> +void qmp_output_visitor_reset(QmpOutputVisitor *v);
>
>  QObject *qmp_output_get_qobject(QmpOutputVisitor *v);
>  Visitor *qmp_output_get_visitor(QmpOutputVisitor *v);
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index 95408a5..913f1b0 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -75,8 +75,8 @@ struct Visitor
>       * visitors do not currently visit arbitrary types).  */
>      void (*type_any)(Visitor *v, const char *name, QObject **obj,
>                       Error **errp);
> -    /* Must be provided to visit explicit null values (right now, only the
> -     * dealloc and qmp-input visitors support this).  */
> +    /* Must be provided to visit explicit null values (the opts and string
> +     * visitors do not currently visit an explicit null).  */

Will need updating.

>      void (*type_null)(Visitor *v, const char *name, Error **errp);
>
>      /* May be NULL; most useful for input visitors. */
> diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> index df22999..2eb200d 100644
> --- a/qapi/qmp-output-visitor.c
> +++ b/qapi/qmp-output-visitor.c
> @@ -1,6 +1,7 @@
>  /*
>   * Core Definitions for QAPI/QMP Command Registry
>   *
> + * Copyright (C) 2015-2016 Red Hat, Inc.
>   * Copyright IBM, Corp. 2011
>   *
>   * Authors:
> @@ -56,17 +57,15 @@ static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
>      QTAILQ_INSERT_HEAD(&qov->stack, e, node);
>  }
>
> -/* Grab and remove the most recent QObject from the stack */
> -static QObject *qmp_output_pop(QmpOutputVisitor *qov)
> +/* Remove the most recent QObject with given type from the stack */
> +static void qmp_output_pop(QmpOutputVisitor *qov, QType type)
>  {
>      QStackEntry *e = QTAILQ_FIRST(&qov->stack);
> -    QObject *value;
>
>      assert(e);
>      QTAILQ_REMOVE(&qov->stack, e, node);
> -    value = e->value;
> +    assert(qobject_type(e->value) == type);
>      g_free(e);
> -    return value;
>  }
>
>
>  /* Grab the most recent QObject from the stack, if any */
> @@ -88,9 +87,8 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
>      cur = qmp_output_last(qov);
>
>      if (!cur) {
> -        /* FIXME we should require the user to reset the visitor, rather
> -         * than throwing away the previous root */
> -        qobject_decref(qov->root);
> +        /* Don't allow reuse of visitor on more than one root */
> +        assert(!qov->root);
>          qov->root = value;
>      } else {
>          switch (qobject_type(cur)) {
> @@ -99,6 +97,7 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
>              qdict_put_obj(qobject_to_qdict(cur), name, value);
>              break;
>          case QTYPE_QLIST:
> +            assert(!name);

Okay if we put "name must be null when visiting list elements" in the
contract.

However, I believe decent error messages will require a non-null name.

I'd specify "may be null" instead of "must be null", and drop the
assertion, because one, it's what the code actually needs, and two,
it'll be slightly less churn when we fix the error messages.

>              qlist_append_obj(qobject_to_qlist(cur), value);
>              break;
>          default:
> @@ -120,7 +119,7 @@ static void qmp_output_start_struct(Visitor *v, const char *name, void **obj,
>  static void qmp_output_end_struct(Visitor *v, Error **errp)
>  {
>      QmpOutputVisitor *qov = to_qov(v);
> -    qmp_output_pop(qov);
> +    qmp_output_pop(qov, QTYPE_QDICT);

Leave qmp_output_pop() unchanged, and do

    value = qmp_output_pop(qov);
    assert(qobject_type(value) == QTYPE_QLIST);

Likewise for the second caller.

Just as simple, and the thing called pop actually pops.

>  }
>
>  static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
> @@ -151,7 +150,7 @@ static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp,
>  static void qmp_output_end_list(Visitor *v)
>  {
>      QmpOutputVisitor *qov = to_qov(v);
> -    qmp_output_pop(qov);
> +    qmp_output_pop(qov, QTYPE_QLIST);
>  }
>
>  static void qmp_output_type_int64(Visitor *v, const char *name, int64_t *obj,
> @@ -202,18 +201,22 @@ static void qmp_output_type_any(Visitor *v, const char *name, QObject **obj,
>      qmp_output_add_obj(qov, name, *obj);
>  }
>
> +static void qmp_output_type_null(Visitor *v, const char *name, Error **errp)
> +{
> +    QmpOutputVisitor *qov = to_qov(v);
> +    qmp_output_add_obj(qov, name, qnull());
> +}
> +
>  /* Finish building, and return the root object. Will not be NULL. */
>  QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
>  {
> -    /* FIXME: we should require that a visit occurred, and that it is
> -     * complete (no starts without a matching end) */
> -    QObject *obj = qov->root;
> -    if (obj) {
> -        qobject_incref(obj);
> -    } else {
> -        obj = qnull();
> -    }
> -    return obj;
> +    QObject *root;
> +
> +    assert(qov->root);              /* A visit must have occurred...  */
> +    assert(!qmp_output_last(qov));  /* ...with each start paired with end.  */

I figure QTAILQ_EMPTY(&qov->stack) would be more obvious.

Apropos QTAILQ: where I learned my trade, you got laughed out of the lab
for implementing such a stack with a dynamically allocated linked list.

> +    root = qov->root;
> +    qov->root = NULL;

This line arbitrarily restricts us to a single get.  Replace it by
qobject_incref(root), and qobject_decref(qmp_output_get_qobject(qov)) is
idempotent.  Idempotent is lovely.

> +    return root;
>  }
>
>  Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
> @@ -221,7 +224,7 @@ Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
>      return &v->visitor;
>  }
>
> -void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
> +void qmp_output_visitor_reset(QmpOutputVisitor *v)
>  {
>      QStackEntry *e, *tmp;
>
> @@ -231,6 +234,12 @@ void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
>      }
>
>      qobject_decref(v->root);
> +    v->root = NULL;
> +}
> +
> +void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
> +{
> +    qmp_output_visitor_reset(v);
>      g_free(v);
>  }
>
> @@ -252,6 +261,7 @@ QmpOutputVisitor *qmp_output_visitor_new(void)
>      v->visitor.type_str = qmp_output_type_str;
>      v->visitor.type_number = qmp_output_type_number;
>      v->visitor.type_any = qmp_output_type_any;
> +    v->visitor.type_null = qmp_output_type_null;
>
>      QTAILQ_INIT(&v->stack);
>
> diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
> index 26dc752..74d0ac4 100644
> --- a/tests/test-qmp-output-visitor.c
> +++ b/tests/test-qmp-output-visitor.c
> @@ -260,6 +260,7 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
>          visit_type_UserDefOne(data->ov, "unused", &pu, &err);
>          g_assert(err);
>          error_free(err);
> +        qmp_output_visitor_reset(data->qov);
>      }
>  }
>

That's the only spot that uses visitors for multiple roots?  I expected
worse...

> @@ -459,6 +460,7 @@ static void test_visitor_out_empty(TestOutputVisitorData *data,
>  {
>      QObject *arg;
>
> +    visit_type_null(data->ov, NULL, &error_abort);
>      arg = qmp_output_get_qobject(data->qov);
>      g_assert(qobject_type(arg) == QTYPE_QNULL);
>      /* Check that qnull reference counting is sane */

This isn't testing "empty" anymore.  Suggest to s/empty/null/.

  reply	other threads:[~2016-01-22 19:12 UTC|newest]

Thread overview: 128+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 01/37] qobject: Document more shortcomings in our number handling Eric Blake
2016-01-20  9:02   ` Markus Armbruster
2016-01-20 15:55     ` Eric Blake
2016-01-21  6:21       ` Markus Armbruster
2016-01-21 17:12         ` Eric Blake
2016-01-21 17:29         ` Daniel P. Berrange
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 02/37] qapi: Avoid use of misnamed DO_UPCAST() Eric Blake
2016-01-20 10:04   ` Markus Armbruster
2016-01-20 15:59     ` Eric Blake
2016-01-21  6:22       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 03/37] qapi: Drop dead dealloc visitor variable Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 04/37] hmp: Improve use of qapi visitor Eric Blake
2016-01-20 13:05   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 05/37] vl: " Eric Blake
2016-01-20 13:43   ` Markus Armbruster
2016-01-20 16:18     ` Eric Blake
2016-01-21  6:45       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 06/37] balloon: " Eric Blake
2016-01-20 14:09   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 07/37] qapi: Improve generated event " Eric Blake
2016-01-20 15:19   ` Markus Armbruster
2016-01-20 17:10     ` Eric Blake
2016-01-21  7:16       ` Markus Armbruster
2016-01-26 23:40       ` Eric Blake
2016-01-28 22:51     ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 08/37] qapi: Track all failures between visit_start/stop Eric Blake
2016-01-20 16:03   ` Markus Armbruster
2016-01-20 17:15     ` Eric Blake
2016-01-21  7:19       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 09/37] qapi: Prefer type_int64 over type_int in visitors Eric Blake
2016-01-20 17:07   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 10/37] qapi: Make all visitors supply uint64 callbacks Eric Blake
2016-01-20 17:29   ` Markus Armbruster
2016-01-20 18:10     ` Eric Blake
2016-01-21  8:56       ` Markus Armbruster
2016-01-21 17:22         ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 11/37] qapi: Consolidate visitor small integer callbacks Eric Blake
2016-01-20 17:34   ` Markus Armbruster
2016-01-20 18:17     ` Eric Blake
2016-01-21  9:05       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 12/37] qapi: Don't cast Enum* to int* Eric Blake
2016-01-20 18:08   ` Markus Armbruster
2016-01-20 19:58     ` Eric Blake
2016-01-21  9:08       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 13/37] qom: Use typedef for Visitor Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 14/37] qapi: Swap visit_* arguments for consistent 'name' placement Eric Blake
2016-01-20 18:28   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 15/37] qom: Swap 'name' next to visitor in ObjectPropertyAccessor Eric Blake
2016-01-20 18:49   ` Markus Armbruster
2016-01-20 20:54     ` Eric Blake
2016-01-21  9:18       ` Markus Armbruster
2016-01-21  9:46         ` Kevin Wolf
2016-01-21 10:04           ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 16/37] qapi: Swap 'name' in visit_* callbacks to match public API Eric Blake
2016-01-20 18:55   ` Markus Armbruster
2016-01-20 21:01     ` Eric Blake
2016-01-21  9:19       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 17/37] qapi: Drop unused 'kind' for struct/enum visit Eric Blake
2016-01-20 18:59   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 18/37] qapi: Drop unused error argument for list and implicit struct Eric Blake
2016-01-20 19:03   ` Markus Armbruster
2016-01-20 21:58     ` Eric Blake
2016-01-21  9:47       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 19/37] qmp: Fix reference-counting of qnull on empty output visit Eric Blake
2016-01-21 10:27   ` Markus Armbruster
2016-01-21 13:19     ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 20/37] qmp: Don't abuse stack to track qmp-output root Eric Blake
2016-01-21 13:58   ` Markus Armbruster
2016-01-29  3:06     ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 21/37] qapi: Document visitor interfaces, add assertions Eric Blake
2016-01-21 20:08   ` Markus Armbruster
2016-01-22  0:30     ` Eric Blake
2016-01-22 12:18       ` Markus Armbruster
2016-02-10  0:23         ` Eric Blake
2016-02-10  7:38           ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 22/37] qapi: Add visit_type_null() visitor Eric Blake
2016-01-22 17:00   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 23/37] qmp: Support explicit null during input visit Eric Blake
2016-01-22 17:12   ` Markus Armbruster
2016-02-01 23:52     ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 24/37] qmp: Tighten output visitor rules Eric Blake
2016-01-22 19:11   ` Markus Armbruster [this message]
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 25/37] spapr_drc: Expose 'null' in qom-get when there is no fdt Eric Blake
2016-01-22 19:15   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 26/37] qapi: Simplify excess input reporting in input visitors Eric Blake
2016-01-22 19:24   ` Markus Armbruster
2016-01-22 19:37     ` Eric Blake
2016-01-25  9:27       ` Markus Armbruster
2016-01-25 10:43         ` Laszlo Ersek
2016-01-27 13:54   ` [Qemu-devel] [PATCH 0/3] qapi-visit: Unify struct and union visit Markus Armbruster
2016-01-27 13:54     ` [Qemu-devel] [PATCH 1/3] qapi-visit: Simplify how we visit common union members Markus Armbruster
2016-01-27 21:48       ` Eric Blake
2016-01-27 13:54     ` [Qemu-devel] [PATCH 2/3] qapi-visit: Clean up code generated around visit_end_union() Markus Armbruster
2016-01-27 14:02       ` Eric Blake
2016-01-27 14:46         ` Markus Armbruster
2016-01-27 13:54     ` [Qemu-devel] [PATCH 3/3] qapi-visit: Unify struct and union visit Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 27/37] qapi: Add type.is_empty() helper Eric Blake
2016-01-25 14:15   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 28/37] qapi: Fix command with named empty argument type Eric Blake
2016-01-25 15:03   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 29/37] qapi: Eliminate empty visit_type_FOO_fields Eric Blake
2016-01-25 17:04   ` Markus Armbruster
2016-02-17  4:57     ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 30/37] qapi: Canonicalize missing object to :empty Eric Blake
2016-01-25 19:04   ` Markus Armbruster
2016-01-26 16:29     ` Markus Armbruster
2016-01-27  8:00       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 31/37] qapi-visit: Unify struct and union visit Eric Blake
2016-01-27 14:12   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 32/37] qapi: Rework deallocation of partial struct Eric Blake
2016-01-27 16:41   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 33/37] qapi: Split visit_end_struct() into pieces Eric Blake
2016-01-27 17:20   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 34/37] qapi: Simplify semantics of visit_next_list() Eric Blake
2016-01-28 13:37   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 35/37] qapi: Change visit_type_FOO() to no longer return partial objects Eric Blake
2016-01-28 15:24   ` Markus Armbruster
2016-01-28 17:05     ` Eric Blake
2016-01-29 12:03       ` Markus Armbruster
2016-01-29 15:13         ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 36/37] RFC: qapi: Adjust layout of FooList types Eric Blake
2016-01-28 15:34   ` Markus Armbruster
2016-01-28 17:23     ` Eric Blake
2016-01-29  8:19       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 37/37] qapi: Update docs to match recent generator changes Eric Blake
2016-01-28 15:37   ` Markus Armbruster
2016-01-28 17:56 ` [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Markus Armbruster

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87d1stfmyu.fsf@blackfin.pond.sub.org \
    --to=armbru@redhat.com \
    --cc=eblake@redhat.com \
    --cc=marcandre.lureau@redhat.com \
    --cc=mdroth@linux.vnet.ibm.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.