From: Markus Armbruster <armbru@redhat.com>
To: Fabiano Rosas <farosas@suse.de>
Cc: qemu-devel@nongnu.org, peterx@redhat.com, ppandit@redhat.com,
Michael Roth <michael.roth@amd.com>
Subject: Re: [PATCH v2 5/9] qapi: Add QAPI_MERGE
Date: Fri, 13 Feb 2026 15:57:54 +0100 [thread overview]
Message-ID: <871pio3d3h.fsf@pond.sub.org> (raw)
In-Reply-To: <20260202224101.20568-6-farosas@suse.de> (Fabiano Rosas's message of "Mon, 2 Feb 2026 19:40:57 -0300")
Fabiano Rosas <farosas@suse.de> writes:
> The migration subsystem currently has code to merge two objects of the
> same type. It does so by checking which fields are present in a source
> object and overwriting the corresponding fields on the destination
> object. This leads to a lot of open-coded lines such as:
>
> if (src->has_foobar) {
> dst->foobar = src->foobar;
> }
It does this to update configuration. Both the configuration and the
update is a struct MigrationParameters. To enable update, not just
complete overwrite, the struct's members are all optional. If update's
member is present, it replaces the configuration's member.
Note that this is a *shallow* operation. There's no recursion.
MigrationParameters is almost flat: two members are arrays. These
members are updated wholesale, i.e. an array is either not touched or
replaced completely. We don't look at array elements.
> This pattern could be replaced by a copy using visitors. Implement a
> macro that extracts elements from a source object using an output
> visitor and merges it with a destination object using an input
> visitor.
>
> Acked-by: Peter Xu <peterx@redhat.com>
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
> include/qapi/type-helpers.h | 40 +++++++++++++++++++++++++++++++++++++
> 1 file changed, 40 insertions(+)
>
> diff --git a/include/qapi/type-helpers.h b/include/qapi/type-helpers.h
> index fc8352cdec..f22d7b7139 100644
> --- a/include/qapi/type-helpers.h
> +++ b/include/qapi/type-helpers.h
> @@ -10,6 +10,9 @@
> */
>
> #include "qapi/qapi-types-common.h"
> +#include "qapi/qobject-input-visitor.h"
> +#include "qapi/qobject-output-visitor.h"
> +#include "qapi/dealloc-visitor.h"
>
> HumanReadableText *human_readable_text_from_str(GString *str);
>
> @@ -20,3 +23,40 @@ HumanReadableText *human_readable_text_from_str(GString *str);
> * cleanup.
> */
> char **strv_from_str_list(const strList *list);
> +
> +/*
> + * Merge @src over @dst by copying deep clones of the present members
> + * from @src to @dst. Non-present on @src are left untouched on
> + * @dst. Pointer members that are overwritten are also freed.
> + */
> +#define QAPI_MERGE(type, dst_, src_) \
The comment uses @dst and @src, the code uses dst_ and src_. Make them
consistent, please.
> + ({ \
> + QObject *out_ = NULL; \
> + Visitor *v_; \
> + /* read in from src */ \
> + v_ = qobject_output_visitor_new(&out_); \
> + visit_type_ ## type(v_, NULL, &src_, &error_abort); \
> + visit_complete(v_, &out_); \
> + visit_free(v_); \
This serializes @src_ to @out_.
> + /* \
> + * Free the memory for which the pointers will be overwritten \
> + * in the next step. \
> + */ \
> + v_ = qapi_dealloc_present_visitor_new(out_); \
> + visit_start_struct(v_, NULL, NULL, 0, &error_abort); \
> + visit_type_ ## type ## _members(v_, dst_, &error_abort); \
> + visit_end_struct(v_, NULL); \
> + visit_free(v_); \
This frees members of @dst_ that are about to be overwritten, using the
dealloc present visitor from the previous patch. Which I haven't
reviewed, yet.
> + /* \
> + * Write to dst but leave existing fields intact (except for \
> + * has_* which will be updated according to their presence in \
> + * src). \
> + */ \
> + v_ = qobject_input_visitor_new(out_); \
> + visit_start_struct(v_, NULL, NULL, 0, &error_abort); \
> + visit_type_ ## type ## _members(v_, dst_, &error_abort); \
> + visit_check_struct(v_, &error_abort); \
> + visit_end_struct(v_, NULL); \
> + visit_free(v_); \
This deserializes @out_ into @dst_, but in an unusual way. The usual
way would be like
visit_start_struct(v, name, (void **)&dst_, sizeof(type),
&error_abort);
visit_type_ ## type ## _members(v, dst_, &error_abort);
visit_check_struct(v, &error_abort);
visit_end_struct(v, (void **)&dst_);
This is code from generated visit_type_TYPE() with @obj replaced by
&dst_, @errp replaced by &error_abort, and handling now impossible
errors deleted.
There's a a small difference that doesn't actually matter:
visit_start_struct() argument @name. visit_start_struct() ignores this
argument when visiting the root of a tree (again, see the big comment in
qapi/visitor.h).
The difference that matters follows.
The usual way passes &dst_ to visit_start_struct(), visit_end_struct(),
and dst_ to visit_type_TYPE_members().
The other usual way passes NULL to all three. That's a virtual walk.
See also the big comment in qapi/visitor.h.
Your QAPI_MERGE() mixes the two. Hmm.
Input visitors create new objects / work on zero-initialized objects:
visit_type_TYPE_members() with an input visitor takes a TYPE *obj
pointing to zero-initialized memory.
QAPI_MERGE() does not zero-initialize the memory it passes to the input
visitor. It merely frees objects the input visitor is going to
overwrite.
Memory the input visitor doesn't overwrite remains as it is. As long as
the input visitor overwrite exactly all the member memory for each
member it actually visits, this results in a merge operation.
At this point, I have a queasy feeling in my stomach: we're upending
design assumptions. I can't point to anything wrong, but this is risky
business.
Another observation: visitors are deep, not shallow. What happens when
@src isn't flat? Say it contains an array member. The input visitor
will first replace @dst's array wholesale by @src's. Then it'll duly
recurse into the array, and replace each of its members by itself. I
think. Does that work? I hope. Is it weird and surprising? Yes!
> + qobject_unref(out_); \
> + })
Could we do this in a simpler and cleaner way?
In handwritten code, we'd simply walk source and destination
simultaneously. The visitor infrastructure doesn't let us do that; it
fundamentally walks a single object. Perhaps we could somehow cajole it
into letting us do it. Can't tell offhand.
We could have an input visitor that updates an existing object. No need
for a dealloc present visitor then. It should take care to avoid the
recursive update I called weird and surprising.
We could serialize both objects to be merged, shallowly merge the
resulting QDicts, deserialize. One more serialize, which is expensive,
but if two expensive expensive serialize / deserialize is fine, three
should be fine, too. No need for a dealloc present visitor then.
Thoughts?
next prev parent reply other threads:[~2026-02-13 14:58 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-02 22:40 [PATCH v2 0/9] qapi: Use visitors for migration parameters handling Fabiano Rosas
2026-02-02 22:40 ` [PATCH v2 1/9] migration/options.c: Don't export migrate_tls_opts_free Fabiano Rosas
2026-02-05 20:19 ` Peter Xu
2026-02-02 22:40 ` [PATCH v2 2/9] migration: Use QAPI_CLONE_MEMBERS in migrate_params_test_apply Fabiano Rosas
2026-02-05 20:19 ` Peter Xu
2026-02-02 22:40 ` [PATCH v2 3/9] migration: Use QAPI_CLONE_MEMBERS in migrate_params_apply Fabiano Rosas
2026-02-02 22:40 ` [PATCH v2 4/9] qapi: Implement qapi_dealloc_present_visitor Fabiano Rosas
2026-02-05 21:45 ` Peter Xu
2026-02-06 12:19 ` Fabiano Rosas
2026-02-06 17:38 ` Peter Xu
2026-02-02 22:40 ` [PATCH v2 5/9] qapi: Add QAPI_MERGE Fabiano Rosas
2026-02-13 14:57 ` Markus Armbruster [this message]
2026-02-02 22:40 ` [PATCH v2 6/9] migration/options: Use QAPI_MERGE in migrate_params_test_apply Fabiano Rosas
2026-02-02 22:40 ` [PATCH v2 7/9] migration/options: Open code migrate_params_apply Fabiano Rosas
2026-02-05 22:14 ` Peter Xu
2026-02-06 12:25 ` Fabiano Rosas
2026-02-02 22:41 ` [PATCH v2 8/9] migration/options: Stop freeing s->parameters members individually Fabiano Rosas
2026-02-05 22:16 ` Peter Xu
2026-02-06 12:29 ` Fabiano Rosas
2026-02-06 16:20 ` Peter Xu
2026-02-06 19:50 ` Fabiano Rosas
2026-02-09 15:37 ` Peter Xu
2026-02-02 22:41 ` [PATCH v2 9/9] migration: Use migrate_params_free during finalize Fabiano Rosas
2026-02-05 22:21 ` Peter Xu
2026-02-05 22:05 ` [PATCH v2 0/9] qapi: Use visitors for migration parameters handling Peter Xu
2026-02-06 12:34 ` Fabiano Rosas
2026-02-06 17:40 ` Peter Xu
2026-02-06 18:36 ` Fabiano Rosas
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=871pio3d3h.fsf@pond.sub.org \
--to=armbru@redhat.com \
--cc=farosas@suse.de \
--cc=michael.roth@amd.com \
--cc=peterx@redhat.com \
--cc=ppandit@redhat.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.