qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [RFC PATCH] qapi: split visit_end_struct() into pieces


From: Markus Armbruster
Subject: Re: [Qemu-devel] [RFC PATCH] qapi: split visit_end_struct() into pieces
Date: Wed, 07 Oct 2015 14:00:37 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux)

Eric Blake <address@hidden> writes:

> We want to call the various visit_end_*() functions unconditionally,
> so that visitors can release resources tied up since the matching
> visit_start_*().  But we also have a requirement for detecting when
> an input visitor did not consume everything, so the code allowed
> visit_end_*() to set an error.  This led to some awkward coding,
> since safely setting the error, even if an earlier one had
> occurred, while still favoring the earliest error, requires either
> multiple error_propagate() calls (and an understanding that unlike
> error_setg(), it is safe to propagate onto an already-set error),
> or else ternaries such as foo(..., err ? NULL : &err) to ignore
> secondary errors if an earlier error is already known.
>
> Splitting the functionality allows slightly cleaner idioms. Now,
> clients must call visit_check_struct() if all earlier actions
> have been successful for one last chance at flagging excess
> input, while visit_end_struct() is unconditional and does not
> set any further errors.
>
> As it is an API change, everything must be changed in one commit
> (there is no way to break this into smaller pieces): both the
> generated code and other existing clients, as well as the various
> visitor backends.
>
> Signed-off-by: Eric Blake <address@hidden>
>
> ---
> TODO: There is some redundancy in the testsuite, with multiple
> files hand-writing their own visit_type_TestStruct() instead
> of letting the qapi generators create it.  Should this be
> consolidated as a separate patch?

If generating makes sense, it should be a separate patch.  Whether it
makes sense I can't say until I see the patch.

> Probably won't apply directly to any published commit, so much
> as me posting a part of my worktree early to get feedback on
> whether the idea even makes sense, given how large the results
> end up being.
>
> v6: new patch, based on discussion of v5 7/46:
>
> On 09/28/2015 03:14 AM, Markus Armbruster wrote:
>
>>> One other potential alternative:  What if we split visit_end_struct()
>>> into two visitor functions, one that checks for success, and the other
>>> that is called unconditionally to clean up resources.
>
>> I think this split could help with writing safe code: in
>> visit_check_struct() you can rely on "no error so far", as usual.  In
>> visit_end_struct(), you can't, but it should be a pure cleanup function,
>> where that's quite normal.
>>
>> Looks like we're getting drawn into visitor contract territory again.
>>
>
> ---
>  hmp.c                              |  3 ++-
>  hw/virtio/virtio-balloon.c         | 14 ++++++++------
>  include/qapi/visitor-impl.h        |  8 +++++---
>  include/qapi/visitor.h             | 24 ++++++++++++++++++------
>  qapi/opts-visitor.c                | 17 +++++++++++++++--
>  qapi/qapi-dealloc-visitor.c        |  6 +++---
>  qapi/qapi-visit-core.c             | 19 +++++++++++++------
>  qapi/qmp-input-visitor.c           | 36 +++++++++++++++++++++++++-----------
>  qapi/qmp-output-visitor.c          |  4 ++--
>  qapi/string-input-visitor.c        |  2 +-
>  qapi/string-output-visitor.c       |  2 +-
>  qom/object.c                       |  5 ++---
>  scripts/qapi-event.py              |  5 +++--
>  scripts/qapi-visit.py              | 24 ++++++++++++------------
>  tests/test-qmp-input-strict.c      |  9 +++++----
>  tests/test-qmp-input-visitor.c     |  9 +++++----
>  tests/test-qmp-output-visitor.c    | 23 ++++++++++++++++-------
>  tests/test-visitor-serialization.c |  9 +++++----
>  vl.c                               | 12 +++++++-----
>  19 files changed, 148 insertions(+), 83 deletions(-)
>
> diff --git a/hmp.c b/hmp.c
> index 5048eee..b7a42dc 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -1658,8 +1658,9 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
>
>      object_add(type, id, pdict, opts_get_visitor(ov), &err);
>
> +    visit_check_struct(opts_get_visitor(ov), &err_end);
>  out_end:
> -    visit_end_struct(opts_get_visitor(ov), &err_end);
> +    visit_end_struct(opts_get_visitor(ov));
>      if (!err && err_end) {
>          qmp_object_del(id, NULL);
>      }

Preexisting: calling object_add() before visit_end_struct() is awkward.
Can we simplify things now we have separate visit_check_struct() and
visit_end_struct()?  Call visit_check_struct() before object_add(),
bypass object_add() on error, avoiding the need to undo it with
qmp_object_del().

> diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
> index c419b17..48867c4 100644
> --- a/hw/virtio/virtio-balloon.c
> +++ b/hw/virtio/virtio-balloon.c
> @@ -132,14 +132,16 @@ static void balloon_stats_get_all(Object *obj, struct 
> Visitor *v,
       visit_start_struct(v, NULL, NULL, "stats", 0, &err);
       if (err) {
           goto out_end;
       }
       for (i = 0; !err && i < VIRTIO_BALLOON_S_NR; i++) {
>          visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i],
>                           &err);

Preexisting: if visit_type_int64() fails, we need to break the loop.
Separate fix.

I wonder whether this is a common mistake.  Perhaps I can search for it
with Coccinelle.

>      }
> -    error_propagate(errp, err);
> -    err = NULL;
> -    visit_end_struct(v, &err);
> +    if (!err) {
> +        visit_check_struct(v, &err);
> +    }
> +    visit_end_struct(v);
>
>  out_end:
> -    error_propagate(errp, err);
> -    err = NULL;
> -    visit_end_struct(v, &err);
> +    if (!err) {
> +        visit_check_struct(v, &err);
> +    }
> +    visit_end_struct(v);
>  out:
>      error_propagate(errp, err);
>  }

This is the obvious mechanical change.

Loop fix and visit_end_struct() split squashed together could look like
this (not even compile-tested):

    for (i = 0; !err && i < VIRTIO_BALLOON_S_NR; i++) {
        visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i],
                         &err);
        if (err) {
            goto out_inner_end;
        }
    }

    visit_check_struct(v, &err);
out_inner_end:
    visit_end_struct(v);

    if (!err) {
        visit_check_struct(v, &err);
    }
out_end:
    visit_end_struct(v);

out:
    error_propagate(errp, err);

> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index 090b19b..c53d4d3 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -28,21 +28,23 @@ struct Visitor
>       * currently visit structs). */
>      void (*start_struct)(Visitor *v, void **obj, const char *kind,
>                           const char *name, size_t size, Error **errp);
> +    /* May be NULL; most useful for input visitors. */
> +    void (*check_struct)(Visitor *v, Error **errp);
>      /* Must be provided if start_struct is present. */
> -    void (*end_struct)(Visitor *v, Error **errp);
> +    void (*end_struct)(Visitor *v);
>
>      /* May be NULL; most useful for input visitors. */
>      void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
>                                    Error **errp);
>      /* May be NULL */
> -    void (*end_implicit_struct)(Visitor *v, Error **errp);
> +    void (*end_implicit_struct)(Visitor *v);
>
>      /* Must be set */
>      void (*start_list)(Visitor *v, const char *name, Error **errp);
>      /* Must be set */
>      GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
>      /* Must be set */
> -    void (*end_list)(Visitor *v, Error **errp);
> +    void (*end_list)(Visitor *v);
>
>      /* Must be set, although the helpers input_type_enum() and
>       * output_type_enum() can be used.  */
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index 6534cca..708f55c 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -56,11 +56,19 @@ typedef struct GenericList
>  void visit_start_struct(Visitor *v, void **obj, const char *kind,
>                          const char *name, size_t size, Error **errp);
>  /**
> + * Check whether completing a struct is safe.

"Safe"?  We need to complete the struct visit with visit_end_struct()
regardless of what this function returns...

> + * Should be called prior to visit_end_struct() if all other intermediate
> + * visit steps were successful, to allow the caller one last chance to
> + * report errors such as remaining data that was not consumed by the visit.
> + */
> +void visit_check_struct(Visitor *v, Error **errp);
> +/**
>   * Complete a struct visit started earlier.
>   * Must be called after any successful use of visit_start_struct(),
> - * even if intermediate processing was skipped due to errors.
> + * even if intermediate processing was skipped due to errors, to allow
> + * the backend to release any resources.
>   */
> -void visit_end_struct(Visitor *v, Error **errp);
> +void visit_end_struct(Visitor *v);
>
>  /**
>   * Prepare to visit an implicit struct.
> @@ -76,9 +84,12 @@ void visit_start_implicit_struct(Visitor *v, void **obj, 
> size_t size,
>  /**
>   * Complete an implicit struct visit started earlier.
>   * Must be called after any successful use of visit_start_implicit_struct(),
> - * even if intermediate processing was skipped due to errors.
> + * even if intermediate processing was skipped due to errors.  Unlike
> + * visit_end_struct(), there is no need for a prior check call (since
> + * an implicit object is a subset of larger object, checking before ending
> + * the overall struct is sufficient).
>   */
> -void visit_end_implicit_struct(Visitor *v, Error **errp);
> +void visit_end_implicit_struct(Visitor *v);
>
>  /**
>   * Prepare to visit an array tied to an object key @name.
> @@ -101,9 +112,10 @@ GenericList *visit_next_list(Visitor *v, GenericList 
> **list, Error **errp);
>  /**
>   * Complete the list started earlier.
>   * Must be called after any successful use of visit_start_list(),
> - * even if intermediate processing was skipped due to errors.
> + * even if intermediate processing was skipped due to errors, to allow
> + * the backend to release any resources.
>   */
> -void visit_end_list(Visitor *v, Error **errp);
> +void visit_end_list(Visitor *v);
>
>  /**
>   * Check if an optional member @name of an object needs visiting.
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index cd10392..39dc431 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -158,7 +158,7 @@ ghr_true(gpointer ign_key, gpointer ign_value, gpointer 
> ign_user_data)
>
>
>  static void
> -opts_end_struct(Visitor *v, Error **errp)
> +opts_check_struct(Visitor *v, Error **errp)
>  {
>      OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
>      GQueue *any;

       if (--ov->depth > 0) {

Do we want to decrement ov->depth here?  We'll decrement it again in
opts_end_struct()...

           return;
       }

       /* we should have processed all (distinct) QemuOpt instances */
       any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL);
       if (any) {
           const QemuOpt *first;

> @@ -175,6 +175,18 @@ opts_end_struct(Visitor *v, Error **errp)
>          first = g_queue_peek_head(any);
>          error_setg(errp, QERR_INVALID_PARAMETER, first->name);
>      }
> +}
> +
> +
> +static void
> +opts_end_struct(Visitor *v)
> +{
> +    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
> +
> +    if (--ov->depth > 0) {
> +        return;
> +    }
> +
>      g_hash_table_destroy(ov->unprocessed_opts);
>      ov->unprocessed_opts = NULL;
>      if (ov->fake_id_opt) {
> @@ -263,7 +275,7 @@ opts_next_list(Visitor *v, GenericList **list, Error 
> **errp)
>
>
>  static void
> -opts_end_list(Visitor *v, Error **errp)
> +opts_end_list(Visitor *v)
>  {
>      OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
>
> @@ -506,6 +518,7 @@ opts_visitor_new(const QemuOpts *opts)
>      ov = g_malloc0(sizeof *ov);
>
>      ov->visitor.start_struct = &opts_start_struct;
> +    ov->visitor.check_struct = &opts_check_struct;
>      ov->visitor.end_struct   = &opts_end_struct;
>
>      ov->visitor.start_list = &opts_start_list;
> diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
> index 737deab..a2139dd 100644
> --- a/qapi/qapi-dealloc-visitor.c
> +++ b/qapi/qapi-dealloc-visitor.c
> @@ -67,7 +67,7 @@ static void qapi_dealloc_start_struct(Visitor *v, void 
> **obj, const char *kind,
>      qapi_dealloc_push(qov, obj);
>  }
>
> -static void qapi_dealloc_end_struct(Visitor *v, Error **errp)
> +static void qapi_dealloc_end_struct(Visitor *v)
>  {
>      QapiDeallocVisitor *qov = to_qov(v);
>      void **obj = qapi_dealloc_pop(qov);
> @@ -85,7 +85,7 @@ static void qapi_dealloc_start_implicit_struct(Visitor *v,
>      qapi_dealloc_push(qov, obj);
>  }
>
> -static void qapi_dealloc_end_implicit_struct(Visitor *v, Error **errp)
> +static void qapi_dealloc_end_implicit_struct(Visitor *v)
>  {
>      QapiDeallocVisitor *qov = to_qov(v);
>      void **obj = qapi_dealloc_pop(qov);
> @@ -121,7 +121,7 @@ static GenericList *qapi_dealloc_next_list(Visitor *v, 
> GenericList **listp,
>      return NULL;
>  }
>
> -static void qapi_dealloc_end_list(Visitor *v, Error **errp)
> +static void qapi_dealloc_end_list(Visitor *v)
>  {
>      QapiDeallocVisitor *qov = to_qov(v);
>      void *obj = qapi_dealloc_pop(qov);
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index cbf7780..82d54af 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -23,9 +23,16 @@ void visit_start_struct(Visitor *v, void **obj, const char 
> *kind,
>      v->start_struct(v, obj, kind, name, size, errp);
>  }
>
> -void visit_end_struct(Visitor *v, Error **errp)
> +void visit_check_struct(Visitor *v, Error **errp)
>  {
> -    v->end_struct(v, errp);
> +    if (v->check_struct) {
> +        v->check_struct(v, errp);
> +    }
> +}
> +
> +void visit_end_struct(Visitor *v)
> +{
> +    v->end_struct(v);
>  }
>
>  void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
> @@ -36,10 +43,10 @@ void visit_start_implicit_struct(Visitor *v, void **obj, 
> size_t size,
>      }
>  }
>
> -void visit_end_implicit_struct(Visitor *v, Error **errp)
> +void visit_end_implicit_struct(Visitor *v)
>  {
>      if (v->end_implicit_struct) {
> -        v->end_implicit_struct(v, errp);
> +        v->end_implicit_struct(v);
>      }
>  }
>
> @@ -53,9 +60,9 @@ GenericList *visit_next_list(Visitor *v, GenericList 
> **list, Error **errp)
>      return v->next_list(v, list, errp);
>  }
>
> -void visit_end_list(Visitor *v, Error **errp)
> +void visit_end_list(Visitor *v)
>  {
> -    v->end_list(v, errp);
> +    v->end_list(v);
>  }
>
>  bool visit_start_union(Visitor *v, bool data_present, Error **errp)
> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
> index 5310db5..f10f74f 100644
> --- a/qapi/qmp-input-visitor.c
> +++ b/qapi/qmp-input-visitor.c
> @@ -88,14 +88,14 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject 
> *obj, Error **errp)
>      qiv->nb_stack++;
>  }
>
> -/** Only for qmp_input_pop. */
> +/** Only for qmp_input_check. */

Drop the comment instead?

Aside: a loop would be more idiomatic C.  Leave higher order functions
to languages that are actually equipped for the job.

>  static gboolean always_true(gpointer key, gpointer val, gpointer user_pkey)
>  {
>      *(const char **)user_pkey = (const char *)key;
>      return TRUE;
>  }
>
> -static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
> +static void qmp_input_check(QmpInputVisitor *qiv, Error **errp)
>  {
>      assert(qiv->nb_stack > 0);
>
> @@ -107,6 +107,17 @@ static void qmp_input_pop(QmpInputVisitor *qiv, Error 
> **errp)
>                  g_hash_table_find(top_ht, always_true, &key);
>                  error_setg(errp, QERR_QMP_EXTRA_MEMBER, key);
>              }
> +        }
> +    }
> +}
> +
> +static void qmp_input_pop(QmpInputVisitor *qiv)
> +{
> +    assert(qiv->nb_stack > 0);
> +
> +    if (qiv->strict) {
> +        GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h;
> +        if (top_ht) {
>              g_hash_table_unref(top_ht);
>          }
>      }
> @@ -138,11 +149,18 @@ static void qmp_input_start_struct(Visitor *v, void 
> **obj, const char *kind,
>      }
>  }
>
> -static void qmp_input_end_struct(Visitor *v, Error **errp)
> +static void qmp_input_check_struct(Visitor *v, Error **errp)
>  {
>      QmpInputVisitor *qiv = to_qiv(v);
>
> -    qmp_input_pop(qiv, errp);
> +    qmp_input_check(qiv, errp);

qmp_input_check(to_qiv(v));

Alternatively, inline, as this is the only caller.

> +}
> +
> +static void qmp_input_end_struct(Visitor *v)
> +{
> +    QmpInputVisitor *qiv = to_qiv(v);
> +
> +    qmp_input_pop(qiv);

qmp_input_pop(to_qiv(v));

Since all callers pass to_qiv(v), we could move that into the callee.
Same for qmp_input_check() then.

>  }
>
>  static void qmp_input_start_implicit_struct(Visitor *v, void **obj,
> @@ -153,10 +171,6 @@ static void qmp_input_start_implicit_struct(Visitor *v, 
> void **obj,
>      }
>  }
>
> -static void qmp_input_end_implicit_struct(Visitor *v, Error **errp)
> -{
> -}
> -

Further evidence of confusion about the visitor contract.

>  static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
>  {
>      QmpInputVisitor *qiv = to_qiv(v);
> @@ -201,11 +215,11 @@ static GenericList *qmp_input_next_list(Visitor *v, 
> GenericList **list,
>      return entry;
>  }
>
> -static void qmp_input_end_list(Visitor *v, Error **errp)
> +static void qmp_input_end_list(Visitor *v)
>  {
>      QmpInputVisitor *qiv = to_qiv(v);
>
> -    qmp_input_pop(qiv, errp);
> +    qmp_input_pop(qiv);
>  }

Identical to qmp_input_end_struct() now.  If we move to_qiv() into
qmp_input_pop(), we can use it as callback directly, and drop the two
wrappers.

>
>  static void qmp_input_get_next_type(Visitor *v, qtype_code *type,
> @@ -332,9 +346,9 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
>      v = g_malloc0(sizeof(*v));
>
>      v->visitor.start_struct = qmp_input_start_struct;
> +    v->visitor.check_struct = qmp_input_check_struct;
>      v->visitor.end_struct = qmp_input_end_struct;
>      v->visitor.start_implicit_struct = qmp_input_start_implicit_struct;
> -    v->visitor.end_implicit_struct = qmp_input_end_implicit_struct;
>      v->visitor.start_list = qmp_input_start_list;
>      v->visitor.next_list = qmp_input_next_list;
>      v->visitor.end_list = qmp_input_end_list;
> diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> index f891e72..b9fba7b 100644
> --- a/qapi/qmp-output-visitor.c
> +++ b/qapi/qmp-output-visitor.c
> @@ -122,7 +122,7 @@ static void qmp_output_start_struct(Visitor *v, void 
> **obj, const char *kind,
>      qmp_output_push(qov, dict);
>  }
>
> -static void qmp_output_end_struct(Visitor *v, Error **errp)
> +static void qmp_output_end_struct(Visitor *v)
>  {
>      QmpOutputVisitor *qov = to_qov(v);
>      qmp_output_pop(qov);
> @@ -153,7 +153,7 @@ static GenericList *qmp_output_next_list(Visitor *v, 
> GenericList **listp,
>      return list ? list->next : NULL;
>  }
>
> -static void qmp_output_end_list(Visitor *v, Error **errp)
> +static void qmp_output_end_list(Visitor *v)
>  {
>      QmpOutputVisitor *qov = to_qov(v);
>      qmp_output_pop(qov);
> diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
> index bbd6a54..97e00c8 100644
> --- a/qapi/string-input-visitor.c
> +++ b/qapi/string-input-visitor.c
> @@ -173,7 +173,7 @@ next_list(Visitor *v, GenericList **list, Error **errp)
>  }
>
>  static void
> -end_list(Visitor *v, Error **errp)
> +end_list(Visitor *v)
>  {
>      StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
>      siv->head = true;
> diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
> index b86ce2c..b269f39 100644
> --- a/qapi/string-output-visitor.c
> +++ b/qapi/string-output-visitor.c
> @@ -290,7 +290,7 @@ next_list(Visitor *v, GenericList **list, Error **errp)
>  }
>
>  static void
> -end_list(Visitor *v, Error **errp)
> +end_list(Visitor *v)
>  {
>      StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
>
> diff --git a/qom/object.c b/qom/object.c
> index 11cd86b..6d92e01 100644
> --- a/qom/object.c
> +++ b/qom/object.c
> @@ -1844,10 +1844,9 @@ static void property_get_tm(Object *obj, Visitor *v, 
> void *opaque,
>      if (err) {
>          goto out_end;
>      }
> +    visit_check_struct(v, &err);
>  out_end:
> -    error_propagate(errp, err);
> -    err = NULL;
> -    visit_end_struct(v, errp);
> +    visit_end_struct(v);
>  out:
>      error_propagate(errp, err);
>

By now the improvement is obvious: the users of this interface get nicer
pretty consistently.

> diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
> index 720486f..250e753 100644
> --- a/scripts/qapi-event.py
> +++ b/scripts/qapi-event.py
> @@ -73,13 +73,14 @@ def gen_event_send(name, arg_type):
>          ret += gen_err_check()
>          ret += gen_visit_fields(arg_type.members, need_cast=True)
>          ret += mcgen('''
> -    visit_end_struct(v, &err);
> +    visit_check_struct(v, &err);
>      if (err) {
>          goto out;
>      }
> +    visit_end_struct(v);
>
>      obj = qmp_output_get_qobject(qov);
> -    g_assert(obj != NULL);
> +    g_assert(obj);

I prefer the more laconic form myself, but it's an unrelated change.

>
>      qdict_put_obj(qmp, "data", obj);
>  ''')
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 1ac5350..c5a32cf 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -52,7 +52,7 @@ static void visit_type_implicit_%(c_type)s(Visitor *v, 
> %(c_type)s **obj, Error *
>      visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err);
>      if (!err) {
>          visit_type_%(c_type)s_fields(v, obj, errp);
> -        visit_end_implicit_struct(v, &err);
> +        visit_end_implicit_struct(v);
>      }
>      error_propagate(errp, err);
>  }
> @@ -115,9 +115,12 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, 
> const char *name, Error
>      visit_start_struct(v, (void **)obj, "%(name)s", name, 
> sizeof(%(c_name)s), &err);
>      if (!err) {
>          if (*obj) {
> -            visit_type_%(c_name)s_fields(v, obj, errp);
> +            visit_type_%(c_name)s_fields(v, obj, &err);
>          }
> -        visit_end_struct(v, &err);
> +        if (!err) {
> +            visit_check_struct(v, &err);
> +        }
> +        visit_end_struct(v);
>      }
>      error_propagate(errp, err);
>  }
> @@ -147,9 +150,7 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, 
> const char *name, Error
>          visit_type_%(c_elt_type)s(v, &native_i->value, NULL, &err);
>      }
>
> -    error_propagate(errp, err);
> -    err = NULL;
> -    visit_end_list(v, &err);
> +    visit_end_list(v);
>  out:
>      error_propagate(errp, err);
>  }
> @@ -208,9 +209,7 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, 
> const char *name, Error
>                     "%(name)s");
>      }
>  out_obj:
> -    error_propagate(errp, err);
> -    err = NULL;
> -    visit_end_implicit_struct(v, &err);
> +    visit_end_implicit_struct(v);
>  out:
>      error_propagate(errp, err);
>  }
> @@ -297,13 +296,14 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s 
> **obj, const char *name, Error
>      default:
>          abort();
>      }
> +    if (!err) {
> +        visit_check_struct(v, &err);
> +    }
>  out_obj:
>      error_propagate(errp, err);
>      err = NULL;
>      visit_end_union(v, !!(*obj)->data, &err);

Should visit_end_union() be similarly split?  Or should its Error **
parameter be dropped?  As far as I can tell, no visitor implements this
method...

> -    error_propagate(errp, err);
> -    err = NULL;
> -    visit_end_struct(v, &err);
> +    visit_end_struct(v);
>  out:
>      error_propagate(errp, err);
>  }
> diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
> index 1c13c8f..da9dafd 100644
> --- a/tests/test-qmp-input-strict.c
> +++ b/tests/test-qmp-input-strict.c
> @@ -118,11 +118,12 @@ static void visit_type_TestStruct(Visitor *v, 
> TestStruct **obj,
>          goto out_end;
>      }
>      visit_type_str(v, &(*obj)->string, "string", &err);
> -
> +    if (err) {
> +        goto out_end;
> +    }
> +    visit_check_struct(v, &err);
>  out_end:
> -    error_propagate(errp, err);
> -    err = NULL;
> -    visit_end_struct(v, &err);
> +    visit_end_struct(v);
>  out:
>      error_propagate(errp, err);
>  }
> diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
> index ef5871b..38b1436 100644
> --- a/tests/test-qmp-input-visitor.c
> +++ b/tests/test-qmp-input-visitor.c
> @@ -200,11 +200,12 @@ static void visit_type_TestStruct(Visitor *v, 
> TestStruct **obj,
>          goto out_end;
>      }
>      visit_type_str(v, &(*obj)->string, "string", &err);
> -
> +    if (err) {
> +        goto out_end;
> +    }
> +    visit_check_struct(v, &err);

Here's you goto over visit_check_struct()...

>  out_end:
> -    error_propagate(errp, err);
> -    err = NULL;
> -    visit_end_struct(v, &err);
> +    visit_end_struct(v);
>  out:
>      error_propagate(errp, err);
>  }
> diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
> index 11996d0..f836588 100644
> --- a/tests/test-qmp-output-visitor.c
> +++ b/tests/test-qmp-output-visitor.c
> @@ -194,10 +194,11 @@ static void visit_type_TestStruct(Visitor *v, 
> TestStruct **obj,
>      }
>      visit_type_str(v, &(*obj)->string, "string", &err);
>
> +    if (!err) {
> +        visit_check_struct(v, &err);
> +    }

... but here, you guard it with an if.  Either way works, but I'd like
us to pick just one for the generators.

>  out_end:
> -    error_propagate(errp, err);
> -    err = NULL;
> -    visit_end_struct(v, &err);
> +    visit_end_struct(v);
>  out:
>      error_propagate(errp, err);
>  }
> @@ -329,15 +330,23 @@ static void visit_type_TestStructList(Visitor *v, 
> TestStructList **obj,
>                                        const char *name, Error **errp)
>  {
>      GenericList *i, **head = (GenericList **)obj;
> +    Error *err = NULL;
>
> -    visit_start_list(v, name, errp);
> +    visit_start_list(v, name, &err);
> +    if (err) {
> +        goto out;
> +    }
>
> -    for (*head = i = visit_next_list(v, head, errp); i; i = 
> visit_next_list(v, &i, errp)) {
> +    for (*head = i = visit_next_list(v, head, &err);
> +         !err && i;
> +         i = visit_next_list(v, &i, &err)) {
>          TestStructList *native_i = (TestStructList *)i;
> -        visit_type_TestStruct(v, &native_i->value, NULL, errp);
> +        visit_type_TestStruct(v, &native_i->value, NULL, &err);
>      }

Is this a silent bug fix?  Before your patch, the loop doesn't break on
error.

I'm not afraid of complex for expressions, but I think this one is
unnecessarily complex.

Why does visit_type_TestStruct() store to *head?  All that buys us is
less obviously correct list cleanup in test_visitor_out_list().

Testing err in the for expression works, but the admittedly more verbose

        visit_type_TestStruct(v, &native_i->value, NULL, &err);
        if (err) {
            break; // or goto wherever
        }

would be closer to common Error usage.  Matter of taste.

Aside: the caller creates identical list elements, which weakens the
test needlessly.

>
> -    visit_end_list(v, errp);
> +    visit_end_list(v);
> +out:
> +    error_propagate(errp, err);
>  }
>
>  static void test_visitor_out_list(TestOutputVisitorData *data,
> diff --git a/tests/test-visitor-serialization.c 
> b/tests/test-visitor-serialization.c
> index fa86cae..dd50d69 100644
> --- a/tests/test-visitor-serialization.c
> +++ b/tests/test-visitor-serialization.c
> @@ -212,11 +212,12 @@ static void visit_type_TestStruct(Visitor *v, 
> TestStruct **obj,
>          goto out_end;
>      }
>      visit_type_str(v, &(*obj)->string, "string", &err);
> -
> +    if (err) {
> +        goto out_end;
> +    }
> +    visit_check_struct(v, &err);
>  out_end:
> -    error_propagate(errp, err);
> -    err = NULL;
> -    visit_end_struct(v, &err);
> +    visit_end_struct(v);
>  out:
>      error_propagate(errp, err);
>  }
> diff --git a/vl.c b/vl.c
> index 8d1846c..8b944aa 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -2796,23 +2796,25 @@ static int object_create(void *opaque, QemuOpts 
> *opts, Error **errp)
>      qdict_del(pdict, "qom-type");
>      visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
>      if (err) {
> -        goto out;
> +        goto obj_out;
>      }
>      if (!type_predicate(type)) {
> -        goto out;
> +        goto obj_out;
>      }
>
>      qdict_del(pdict, "id");
>      visit_type_str(opts_get_visitor(ov), &id, "id", &err);
>      if (err) {
> -        goto out;
> +        goto obj_out;
>      }
>
>      object_add(type, id, pdict, opts_get_visitor(ov), &err);
>      if (err) {
> -        goto out;
> +        goto obj_out;
>      }
> -    visit_end_struct(opts_get_visitor(ov), &err);
> +    visit_check_struct(opts_get_visitor(ov), &err);
> +obj_out:
> +    visit_end_struct(opts_get_visitor(ov));
>      if (err) {
>          qmp_object_del(id, NULL);
>      }

Silent bug fix: we now call visit_end_struct() even on error.  Impact?
Separate patch?



reply via email to

[Prev in Thread] Current Thread [Next in Thread]