[libvirt] [PATCH 2/2] network: Introduce virNetworkObjListForEachCb

Erik Skultety eskultet at redhat.com
Thu Oct 12 12:48:22 UTC 2017


On Tue, Oct 10, 2017 at 04:20:06PM -0400, John Ferlan wrote:
> Rather than separate callbacks for the NumOfNetworks, GetNames, and
> Export functions, let's combine them all into one multi-purpose callback.
>
> Signed-off-by: John Ferlan <jferlan at redhat.com>
> ---

NACK to the idea of consolidating all of them into a single callback, making it
ambiguous, much harder to read and unmaintainable (not that we're about to
introduce more getters for which another combination of elements within
virNetworkObjForEachData would determine the action, but still...)

However, we could still improve the existing code base, see below.

>  src/conf/virnetworkobj.c | 153 +++++++++++++++++++----------------------------
>  1 file changed, 62 insertions(+), 91 deletions(-)
>
> diff --git a/src/conf/virnetworkobj.c b/src/conf/virnetworkobj.c
> index 8cd1b62c1c..40e3eb5ea0 100644
> --- a/src/conf/virnetworkobj.c
> +++ b/src/conf/virnetworkobj.c
> @@ -1309,47 +1309,61 @@ virNetworkMatch(virNetworkObjPtr obj,
>  #undef MATCH
>
>
> -struct virNetworkObjListData {
> +typedef bool (*virNetworkObjMatch)(virNetworkObjPtr obj, unsigned int flags);
> +struct _virNetworkObjForEachData {
>      virConnectPtr conn;
> -    virNetworkPtr *nets;
>      virNetworkObjListFilter filter;
> +    virNetworkObjMatch match;
>      unsigned int flags;
> -    int nnets;
>      bool error;
> +    bool checkActive;
> +    bool active;
> +    int nElems;
> +    int maxElems;
> +    char **const elems;
> +    virNetworkPtr *nets;
>  };
>
>  static int
> -virNetworkObjListPopulate(void *payload,
> -                          const void *name ATTRIBUTE_UNUSED,
> -                          void *opaque)
> +virNetworkObjListForEachCb(void *payload,
> +                           const void *name ATTRIBUTE_UNUSED,
> +                           void *opaque)
>  {
> -    struct virNetworkObjListData *data = opaque;
> +    struct _virNetworkObjForEachData *data = opaque;
>      virNetworkObjPtr obj = payload;
>      virNetworkPtr net = NULL;
>
>      if (data->error)
>          return 0;
>
> +    if (data->maxElems >= 0 && data->nElems == data->maxElems)
> +        return 0;
> +
>      virObjectLock(obj);
>
> -    if (data->filter &&
> -        !data->filter(data->conn, obj->def))
> +    if (data->filter && !data->filter(data->conn, obj->def))
>          goto cleanup;
>
> -    if (!virNetworkMatch(obj, data->flags))
> +    if (data->match && !virNetworkMatch(obj, data->flags))
>          goto cleanup;
>
> -    if (!data->nets) {
> -        data->nnets++;
> +    if (data->checkActive && data->active != virNetworkObjIsActive(obj))
>          goto cleanup;
> -    }
>
> -    if (!(net = virGetNetwork(data->conn, obj->def->name, obj->def->uuid))) {
> -        data->error = true;
> -        goto cleanup;
> +    if (data->elems) {

This is an example of the ambiguity I'm talking about, what are elems? it's a
generic term that could describe anything, even @nets, yet @nets can't be
@elems (right below this text), only @maxnames can, so what's the meaning of and
restrictions tied to @elems?

> +        if (VIR_STRDUP(data->elems[data->nElems], obj->def->name) < 0) {
> +            data->error = true;
> +            goto cleanup;
> +        }
> +    } else if (data->nets) {
> +        if (!(net = virGetNetwork(data->conn, obj->def->name, obj->def->uuid))) {
> +            data->error = true;
> +            goto cleanup;
> +        }
> +        data->nets[data->nElems] = net;
>      }
>
> -    data->nets[data->nnets++] = net;
> +    data->nElems++;
>
>   cleanup:
>      virObjectUnlock(obj);
> @@ -1365,31 +1379,36 @@ virNetworkObjListExport(virConnectPtr conn,
>                          unsigned int flags)
>  {
>      int ret = -1;
> -    struct virNetworkObjListData data = {
> -        .conn = conn, .nets = NULL, .filter = filter, .flags = flags,
> -        .nnets = 0, .error = false };
> +    struct _virNetworkObjForEachData data = {
> +        .conn = conn, .filter = filter, .match = virNetworkMatch,
> +        .flags = flags, .checkActive = false, .active = false, .error = false,
> +        .nElems = 0, .maxElems = -1, .elems = NULL, .nets = NULL };
>

^The structure isn't holding actual data, just a bunch of switches the
combination of which further determines the operation with the object and
that's where I see the problem.

>      virObjectRWLockRead(netobjs);
>      if (nets && VIR_ALLOC_N(data.nets, virHashSize(netobjs->objs) + 1) < 0)
>          goto cleanup;
>
> -    virHashForEach(netobjs->objs, virNetworkObjListPopulate, &data);
> +    if (data.nets)
> +        data.maxElems = virHashSize(netobjs->objs) + 1;

What's the purpose of ^this assignment, okay, I can see that it's supposed to
be an array boundary check, but we're holding the read lock to whole time, so
no writer can update the overall number of objects in the meantime, so both
with our without this assignment we're looping through the whole list, nothing
more, nothing less.

> +
> +    virHashForEach(netobjs->objs, virNetworkObjListForEachCb, &data);
>
>      if (data.error)
>          goto cleanup;
>
>      if (data.nets) {
>          /* trim the array to the final size */
> -        ignore_value(VIR_REALLOC_N(data.nets, data.nnets + 1));
> +        ignore_value(VIR_REALLOC_N(data.nets, data.nElems + 1));
>          *nets = data.nets;
>          data.nets = NULL;
>      }
>
> -    ret = data.nnets;
> +    ret = data.nElems;
> +
>   cleanup:
>      virObjectRWUnlock(netobjs);
> -    while (data.nets && data.nnets)
> -        virObjectUnref(data.nets[--data.nnets]);
> +    while (data.nets && data.nElems)
> +        virObjectUnref(data.nets[--data.nElems]);
>
>      VIR_FREE(data.nets);
>      return ret;
> @@ -1442,53 +1461,6 @@ virNetworkObjListForEach(virNetworkObjListPtr nets,
>  }
>
>
> -struct virNetworkObjListGetHelperData {
> -    virConnectPtr conn;
> -    virNetworkObjListFilter filter;
> -    char **names;
> -    int nnames;
> -    int maxnames;
> -    bool active;
> -    bool error;
> -};
> -
> -static int
> -virNetworkObjListGetHelper(void *payload,
> -                           const void *name ATTRIBUTE_UNUSED,
> -                           void *opaque)
> -{
> -    struct virNetworkObjListGetHelperData *data = opaque;
> -    virNetworkObjPtr obj = payload;
> -
> -    if (data->error)
> -        return 0;

Could this have actually ever been true?

> -
> -    if (data->maxnames >= 0 &&
> -        data->nnames == data->maxnames)
> -        return 0;
> -
> -    virObjectLock(obj);
> -

Starting here...
> -    if (data->filter &&
> -        !data->filter(data->conn, obj->def))
> -        goto cleanup;
> -
> -    if ((data->active && virNetworkObjIsActive(obj)) ||
> -        (!data->active && !virNetworkObjIsActive(obj))) {

and ending here, this could become a helper on its own, we reuse this pattern
among all the getters you're trying to change.

> -        if (data->names &&
> -            VIR_STRDUP(data->names[data->nnames], obj->def->name) < 0) {
> -            data->error = true;
> -            goto cleanup;
> -        }

This hunk should be part of a new virNetworkObjListGetName helper.

> -        data->nnames++;
> -    }
> -
> - cleanup:
> -    virObjectUnlock(obj);
> -    return 0;
> -}
> -
> -
>  int
>  virNetworkObjListGetNames(virNetworkObjListPtr nets,
>                            bool active,
> @@ -1497,26 +1469,24 @@ virNetworkObjListGetNames(virNetworkObjListPtr nets,
>                            virNetworkObjListFilter filter,
>                            virConnectPtr conn)
>  {
> -    int ret = -1;
> -
> -    struct virNetworkObjListGetHelperData data = {
> -        .conn = conn, .filter = filter, .names = names, .nnames = 0,
> -        .maxnames = maxnames, .active = active, .error = false};
> +    struct _virNetworkObjForEachData data = {
> +        .conn = conn, .filter = filter, .match = NULL,
> +        .flags = 0, .checkActive = true, .active = active, .error = false,
> +        .nElems = 0, .maxElems = maxnames, .elems = names, .nets = NULL };
>
>      virObjectRWLockRead(nets);
> -    virHashForEach(nets->objs, virNetworkObjListGetHelper, &data);
> +    virHashForEach(nets->objs, virNetworkObjListForEachCb, &data);
>      virObjectRWUnlock(nets);

We have virNetworkObjListForEach for ^this...

>
>      if (data.error)
> -        goto cleanup;
> +        goto error;

you could drop ^this then.

>
> -    ret = data.nnames;
> - cleanup:
> -    if (ret < 0) {
> -        while (data.nnames)
> -            VIR_FREE(data.names[--data.nnames]);
> -    }
> -    return ret;
> +    return data.nElems;
> +
> + error:
> +    while (data.nElems)
> +        VIR_FREE(data.elems[--data.nElems]);
> +    return -1;
>  }
>
>
> @@ -1526,15 +1496,16 @@ virNetworkObjListNumOfNetworks(virNetworkObjListPtr nets,
>                                 virNetworkObjListFilter filter,
>                                 virConnectPtr conn)
>  {
> -    struct virNetworkObjListGetHelperData data = {
> -        .conn = conn, .filter = filter, .names = NULL, .nnames = 0,
> -        .maxnames = -1, .active = active, .error = false};
> +    struct _virNetworkObjForEachData data = {
> +        .conn = conn, .filter = filter, .match = NULL,
> +        .flags = 0, .checkActive = true, .active = active, .error = false,
> +        .nElems = 0, .maxElems = -1, .elems = NULL, .nets = NULL };
>
>      virObjectRWLockRead(nets);
> -    virHashForEach(nets->objs, virNetworkObjListGetHelper, &data);
> +    virHashForEach(nets->objs, virNetworkObjListForEachCb, &data);
>      virObjectRWUnlock(nets);

Again, virNetworkObjListForEach can be used instead.

>
> -    return data.nnames;
> +    return data.nElems;
>  }

To sum it up (feel free to use different names), you could replace
virNetworkObjListGetHelper with virNetworkObjListFilterHelper
(or something similar) which would be responsible for all filtering, IOW
determines whether the obj should or should not be used as part of the current
action over the list. This filter helper would contain the 2 hunks I marked
above and would be called from all the *ForEachCallbacks.
Then you'd have *ListNumOfNetworks calling something like *ObjListGetCount
(again, first calling the filter helper). Similarly, *GetNames could be
adjusted in this fashion.
I admit that preparing a patch myself might have actually been easier in order
to express my idea more clearly :/.

Erik




More information about the libvir-list mailing list