qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "Daniel P. Berrangé" <berrange@redhat.com>
To: Peter Xu <peterx@redhat.com>
Cc: qemu-devel@nongnu.org, "Fabiano Rosas" <farosas@suse.de>,
	"Igor Mammedov" <imammedo@redhat.com>,
	"Juraj Marcin" <jmarcin@redhat.com>,
	"Michael S . Tsirkin" <mst@redhat.com>,
	"Dr . David Alan Gilbert" <dave@treblig.org>,
	"Cédric Le Goater" <clg@redhat.com>,
	"Markus Armbruster" <armbru@redhat.com>,
	"Eduardo Habkost" <eduardo@habkost.net>,
	"Alex Williamson" <alex.williamson@redhat.com>,
	"Paolo Bonzini" <pbonzini@redhat.com>,
	"Peter Maydell" <peter.maydell@linaro.org>
Subject: Re: [PATCH 1/4] qom: TYPE_SINGLETON interface
Date: Fri, 25 Oct 2024 10:51:21 +0100	[thread overview]
Message-ID: <ZxtqGQbd4Hq4APtm@redhat.com> (raw)
In-Reply-To: <20241024165627.1372621-2-peterx@redhat.com>

On Thu, Oct 24, 2024 at 12:56:24PM -0400, Peter Xu wrote:

Adding significant new functionality to QOM should really come
with a commit message explaining the rationale and the design
choices

> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
>  include/qom/object_interfaces.h | 47 +++++++++++++++++++++++++++++++++
>  qom/object.c                    |  3 +++
>  qom/object_interfaces.c         | 24 +++++++++++++++++
>  qom/qom-qmp-cmds.c              | 22 ++++++++++++---
>  system/qdev-monitor.c           |  7 +++++
>  5 files changed, 100 insertions(+), 3 deletions(-)

snip

> + * Singleton class describes the type of object classes that can only
> + * provide one instance for the whole lifecycle of QEMU.  It will fail the
> + * operation if one attemps to create more than one instance.
> + *
> + * One can fetch the single object using class's get_instance() callback if
> + * it was created before.  This can be useful for operations like QMP
> + * qom-list-properties, where dynamically creating an object might not be
> + * feasible.

snip

> +/**
> + * singleton_get_instance:
> + *
> + * @class: the class to fetch singleton instance
> + *
> + * Returns: the object* if the class is a singleton class and the singleton
> + *          object is created, NULL otherwise.
> + */
> +Object *singleton_get_instance(ObjectClass *class);

With this design, all code that uses a given type needs to know
whether or not it is intended to be a singleton. If some code
somewhere mistakenly calls 'object_new' instead of 'singleton_get_instance',
the singleton type  is no longer a singleton, except you handle this by
adding an assert in object_initialize_with_type.

This is still a bit of a loaded foot-gun IMHO, as we don't want random
code asserting.

> diff --git a/qom/object.c b/qom/object.c
> index 11424cf471..ded299ae1a 100644
> --- a/qom/object.c
> +++ b/qom/object.c
> @@ -553,6 +553,9 @@ static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type
>      g_assert(type->abstract == false);
>      g_assert(size >= type->instance_size);
>  
> +    /* Singleton class can only create one object */
> +    g_assert(!singleton_get_instance(type->class));
> +
>      memset(obj, 0, type->instance_size);
>      obj->class = type->class;
>      object_ref(obj);

> diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
> index e91a235347..ecc1cf781c 100644
> --- a/qom/qom-qmp-cmds.c
> +++ b/qom/qom-qmp-cmds.c
> @@ -126,6 +126,7 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
>      ObjectProperty *prop;
>      ObjectPropertyIterator iter;
>      ObjectPropertyInfoList *prop_list = NULL;
> +    bool create;
>  
>      klass = module_object_class_by_name(typename);
>      if (klass == NULL) {
> @@ -141,7 +142,15 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
>          return NULL;
>      }
>  
> -    obj = object_new(typename);
> +    /* Avoid creating multiple instances if the class is a singleton */
> +    create = !object_class_is_singleton(klass) ||
> +        !singleton_get_instance(klass);
> +
> +    if (create) {
> +        obj = object_new(typename);
> +    } else {
> +        obj = singleton_get_instance(klass);
> +    }

This is the first foot-gun example.

I really strongly dislike that the design pattern forces us to
create code like this, as we can never be confident we've
correctly identified all the places which may call object_new
on a singleton...

> @@ -172,7 +181,9 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
>          QAPI_LIST_PREPEND(prop_list, info);
>      }
>  
> -    object_unref(obj);
> +    if (create) {
> +        object_unref(obj);
> +    }

...and this just compounds the ugliness.

> @@ -199,7 +210,12 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename,
>          return NULL;
>      }
>  
> -    if (object_class_is_abstract(klass)) {
> +    /*
> +     * Abstract classes are not for instantiations, meanwhile avoid
> +     * creating temporary singleton objects because it can cause conflicts
> +     * if there's already one created.
> +     */

Another example of the foot-gun firing at random code

> +    if (object_class_is_abstract(klass) || object_class_is_singleton(klass)) {
>          object_class_property_iter_init(&iter, klass);
>      } else {
>          obj = object_new(typename);


With changes to QOM, I think it is generally informative to look at how
GLib has handled the problem, since the QOM design has heavily borrowed
from its GObject design.

In GObject, singletons are handled in a very differnt way. It has a
concept of a "constructor" function against the class, which is what is
responsible for allocating the object. By default the 'constructor' will
call g_new0, but a class which wishes to become a singleton will override
the 'constructor' function to allocate on first call, and return the
cached object on subsequent calls. This is illustrated here:

  https://gitlab.gnome.org/GNOME/glib/-/blob/main/gobject/gobject.h#L297

The key benefit of this is that everything can carry on calling
g_object_new() as before, as it will just "do the right thing"
in terms of allocation.

In QOM, we don't have a 'constructor' class function, we just directly
call g_malloc from object_new_with_type. This is because at the time,
we didn't see an immediate need for it. We could easily change that
though to introduce the concept of a 'constructor', which could
probably make singletons work without needing updates to existing code.



With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



  parent reply	other threads:[~2024-10-25  9:52 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-10-24 16:56 [PATCH 0/4] QOM: Singleton interface Peter Xu
2024-10-24 16:56 ` [PATCH 1/4] qom: TYPE_SINGLETON interface Peter Xu
2024-10-24 20:02   ` Philippe Mathieu-Daudé
2024-10-24 20:53     ` Peter Xu
2024-10-25 15:11       ` Philippe Mathieu-Daudé
2024-10-25 16:21         ` Peter Xu
2024-10-25  8:07   ` Markus Armbruster
2024-10-25 15:17     ` Peter Xu
2024-10-25  9:51   ` Daniel P. Berrangé [this message]
2024-10-25 16:17     ` Peter Xu
2024-10-25 16:22       ` Daniel P. Berrangé
2024-10-25 22:10         ` Peter Xu
2024-10-29  0:01           ` Peter Xu
2024-10-25 16:37     ` Peter Xu
2024-10-24 16:56 ` [PATCH 2/4] x86/iommu: Make x86-iommu a singleton object Peter Xu
2024-10-25  9:25   ` Markus Armbruster
2024-10-25 21:55     ` Peter Xu
2024-10-25 22:13       ` Peter Xu
2024-11-07 11:12         ` Markus Armbruster
2024-11-07 15:29           ` Peter Xu
2024-11-08  8:50             ` Markus Armbruster
2024-10-29 10:47   ` Daniel P. Berrangé
2024-10-29 14:32     ` Peter Xu
2024-10-24 16:56 ` [PATCH 3/4] migration: Make migration object " Peter Xu
2024-10-24 19:20   ` Fabiano Rosas
2024-10-24 16:56 ` [PATCH 4/4] migration: Reset current_migration properly Peter Xu
2024-10-24 19:34   ` Fabiano Rosas
2024-10-24 20:15     ` Peter Xu
2024-10-24 20:51       ` Fabiano Rosas
2024-10-25  7:38 ` [PATCH 0/4] QOM: Singleton interface Markus Armbruster
2024-10-25 15:01   ` Peter Xu
2024-10-29 10:42     ` Daniel P. Berrangé
2024-10-29 14:45       ` Peter Xu
2024-10-29 16:04         ` Daniel P. Berrangé
2024-10-29 17:05           ` Peter Xu
2024-10-29 17:17             ` Daniel P. Berrangé
2024-12-11  8:19             ` Markus Armbruster
2024-12-11 22:10               ` Peter Xu

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=ZxtqGQbd4Hq4APtm@redhat.com \
    --to=berrange@redhat.com \
    --cc=alex.williamson@redhat.com \
    --cc=armbru@redhat.com \
    --cc=clg@redhat.com \
    --cc=dave@treblig.org \
    --cc=eduardo@habkost.net \
    --cc=farosas@suse.de \
    --cc=imammedo@redhat.com \
    --cc=jmarcin@redhat.com \
    --cc=mst@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=peterx@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).