qemu-devel
[Top][All Lists]
Advanced

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

Re: [Qemu-devel] [PATCH 2/2] qobject: modify qobject_ref() to assert on


From: Marc-André Lureau
Subject: Re: [Qemu-devel] [PATCH 2/2] qobject: modify qobject_ref() to assert on NULL
Date: Fri, 24 Aug 2018 10:43:44 +0200

Hi
On Fri, Aug 24, 2018 at 10:12 AM Markus Armbruster <address@hidden> wrote:
>
> Marc-André Lureau <address@hidden> writes:
>
> > While it may be convenient to accept NULL value in
> > qobject_unref() (for similar reasons as free() accepts NULL), it is
> > not such a good idea for qobject_ref(): now assert() on NULL.
>
> Why is it not such a good idea?
>
> Is it unsafe?  Unclean?  Ugly?
>
> If unsafe, can you point to actual problems, present or past?
>
> If you consider it unclean or ugly, can you point to established
> convention?

In general, a function/method shouldn't accept NULL as argument, as it
is usually a programmer mistake.

free() / unref() are exceptions, for convenience.

fwiw, g_object_ref/unref() do not accept NULL. However,
g_clear_object() was introduced later to simply clearing an object
reference (unref and set to NULL, checks NULL).

>
> > Some code relied on that behaviour, but it's best to be explicit that
> > NULL is accepted.  We have to rely on testing, and manual inspection
> > of qobject_ref() usage:
> >
> > * block.c:
> >  - bdrv_refresh_filename(): guarded
> >  - append_open_options(): it depends if qdict values could be NULL,
> >    handled for extra safety, might be unnecessary
> >
> > * block/blkdebug.c:
> >  - blkdebug_refresh_filename(): depends if qdict values could be NULL,
> >    full_open_options could be NULL apparently, handled
> >
> > * block/blkverify.c: guarded
> >
> > * block/{null,nvme}.c: guarded, previous qdict_del() (actually
> >   qdict_find()) guarantee non-NULL)
> >
> > * block/quorum.c: full_open_options could be NULL, handled for extra
> >   safety, might be unnecessary
> >
> > * monitor: events have associated qdict, but may not have 'data' dict
> >   entry. Command 'id' is already guarded. A queued response is
> >   non-NULL.
> >
> > * qapi/qmp-dispatch.c: if "arguments" exists, it can't be NULL during
> >   json parsing
> >
> > * qapi/qobject-input-visitor.c: guarded by assert in visit_type_any()
> >
> > * qapi/qobject-output-visitor.c: guarded by assert() in visit_type_any()
> >   qobject_output_complete(): guarded by pre-condition assert()
> >
> > * qmp.c: guarded, error out before if NULL
> >
> > * qobject/q{list,dict}.c: can accept NULL values apparently, what's
> >   the reason? how are you supposed to handle that? no test?
> >
> >   Some code, such as qdict_flatten_qdict(), assume the value is always
> >   non-NULL for example.
> >
> > - tests/*: considered to be covered by make check, not critical
> >
> > - util/keyval.c: guarded, if (!elt[i]) before
> >
> > Signed-off-by: Marc-André Lureau <address@hidden>
> > Reviewed-by: Eric Blake <address@hidden>
>
> That's a lot of analysis to support a change...
>
> > ---
> >  include/qapi/qmp/qobject.h | 7 ++++---
> >  block.c                    | 6 ++++--
> >  block/blkdebug.c           | 3 ++-
> >  block/quorum.c             | 3 ++-
> >  monitor.c                  | 2 +-
> >  5 files changed, 13 insertions(+), 8 deletions(-)
> >
> > diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h
> > index fcfd549220..2fe5b42579 100644
> > --- a/include/qapi/qmp/qobject.h
> > +++ b/include/qapi/qmp/qobject.h
> > @@ -74,9 +74,8 @@ static inline void qobject_init(QObject *obj, QType type)
> >
> >  static inline void qobject_ref_impl(QObject *obj)
> >  {
> > -    if (obj) {
> > -        obj->base.refcnt++;
> > -    }
> > +    assert(obj);
> > +    obj->base.refcnt++;
> >  }
> >
> >  /**
> > @@ -103,6 +102,7 @@ static inline void qobject_unref_impl(QObject *obj)
> >
> >  /**
> >   * qobject_ref(): Increment QObject's reference count
> > + * @obj: a #QObject or child type instance (must not be NULL)
> >   *
> >   * Returns: the same @obj. The type of @obj will be propagated to the
> >   * return type.
> > @@ -116,6 +116,7 @@ static inline void qobject_unref_impl(QObject *obj)
> >  /**
> >   * qobject_unref(): Decrement QObject's reference count, deallocate
> >   * when it reaches zero
> > + * @obj: a #QObject or child type instance (can be NULL)
> >   */
> >  #define qobject_unref(obj) qobject_unref_impl(QOBJECT(obj))
> >
> > diff --git a/block.c b/block.c
> > index 6161dbe3eb..f1e35c3c1e 100644
> > --- a/block.c
> > +++ b/block.c
> > @@ -5154,6 +5154,8 @@ static bool append_open_options(QDict *d, 
> > BlockDriverState *bs)
> >      for (entry = qdict_first(bs->options); entry;
> >           entry = qdict_next(bs->options, entry))
> >      {
> > +        QObject *val;
> > +
> >          /* Exclude node-name references to children */
> >          QLIST_FOREACH(child, &bs->children, next) {
> >              if (!strcmp(entry->key, child->name)) {
> > @@ -5174,8 +5176,8 @@ static bool append_open_options(QDict *d, 
> > BlockDriverState *bs)
> >              continue;
> >          }
> >
> > -        qdict_put_obj(d, qdict_entry_key(entry),
> > -                      qobject_ref(qdict_entry_value(entry)));
> > +        val = qdict_entry_value(entry);
> > +        qdict_put_obj(d, qdict_entry_key(entry), val ? qobject_ref(val) : 
> > NULL);
> >          found_any = true;
> >      }
> >
> > diff --git a/block/blkdebug.c b/block/blkdebug.c
> > index 0759452925..062263f7e1 100644
> > --- a/block/blkdebug.c
> > +++ b/block/blkdebug.c
> > @@ -846,7 +846,8 @@ static void blkdebug_refresh_filename(BlockDriverState 
> > *bs, QDict *options)
> >      opts = qdict_new();
> >      qdict_put_str(opts, "driver", "blkdebug");
> >
> > -    qdict_put(opts, "image", qobject_ref(bs->file->bs->full_open_options));
> > +    qdict_put(opts, "image", bs->file->bs->full_open_options ?
> > +              qobject_ref(bs->file->bs->full_open_options) : NULL);
> >
> >      for (e = qdict_first(options); e; e = qdict_next(options, e)) {
> >          if (strcmp(qdict_entry_key(e), "x-image")) {
> > diff --git a/block/quorum.c b/block/quorum.c
> > index 9152da8c58..96cd094ede 100644
> > --- a/block/quorum.c
> > +++ b/block/quorum.c
> > @@ -1089,7 +1089,8 @@ static void quorum_refresh_filename(BlockDriverState 
> > *bs, QDict *options)
> >      children = qlist_new();
> >      for (i = 0; i < s->num_children; i++) {
> >          qlist_append(children,
> > -                     qobject_ref(s->children[i]->bs->full_open_options));
> > +                     s->children[i]->bs->full_open_options ?
> > +                     qobject_ref(s->children[i]->bs->full_open_options) : 
> > NULL);
> >      }
> >
> >      opts = qdict_new();
> > diff --git a/monitor.c b/monitor.c
> > index eab2dc7b7b..be4bcd82a0 100644
> > --- a/monitor.c
> > +++ b/monitor.c
> > @@ -675,7 +675,7 @@ monitor_qapi_event_queue_no_reenter(QAPIEvent event, 
> > QDict *qdict)
> >
> >              evstate = g_new(MonitorQAPIEventState, 1);
> >              evstate->event = event;
> > -            evstate->data = qobject_ref(data);
> > +            evstate->data = data ? qobject_ref(data) : NULL;
> >              evstate->qdict = NULL;
> >              evstate->timer = timer_new_ns(monitor_get_event_clock(),
> >                                            monitor_qapi_event_handler,
>
> ... that only complicates code.  By not much, granted.  My problem with
> it is that the commit message doesn't convince me it's an improvement.
>


-- 
Marc-André Lureau



reply via email to

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