[ovs-dev] smap: New macro SMAP_INIT1 for initializing immutable 1-member smaps.
diff mbox

Message ID 1441139751-8836-1-git-send-email-blp@nicira.com
State Accepted
Headers show

Commit Message

Ben Pfaff Sept. 1, 2015, 8:35 p.m. UTC
Reviewing the ovn-controller code I started to notice a common pattern:

    struct smap ext_ids = SMAP_INITIALIZER(&ext_ids);
    smap_add(&ext_ids, "ovn-patch-port", network);
    ovsrec_port_set_external_ids(port, &ext_ids);
    smap_destroy(&ext_ids);

This seemed like a bit too much code for something as simple as
initializing an smap with a single key-value pair.  This commit allows the
code to be reduced to just:

    const struct smap ids = SMAP_INIT1(&ids, "ovn-patch-port", network);
    ovsrec_port_set_external_ids(port, &ids);

This new form also eliminates multiple memory allocation and free
operations, but I doubt that has any real effect on performance;
the primary goal here is code readability.

Signed-off-by: Ben Pfaff <blp@nicira.com>
---
 lib/hmap.h                      |  6 ++++++
 lib/smap.h                      | 19 +++++++++++++++++++
 ovn/controller/encaps.c         |  6 ++----
 ovn/controller/ovn-controller.c | 10 +++-------
 ovn/northd/ovn-northd.c         | 19 ++++++++-----------
 utilities/ovs-vsctl.c           |  4 +---
 6 files changed, 39 insertions(+), 25 deletions(-)

Comments

Russell Bryant Sept. 2, 2015, 12:32 a.m. UTC | #1
On 09/01/2015 04:35 PM, Ben Pfaff wrote:
> Reviewing the ovn-controller code I started to notice a common pattern:
> 
>     struct smap ext_ids = SMAP_INITIALIZER(&ext_ids);
>     smap_add(&ext_ids, "ovn-patch-port", network);
>     ovsrec_port_set_external_ids(port, &ext_ids);
>     smap_destroy(&ext_ids);
> 
> This seemed like a bit too much code for something as simple as
> initializing an smap with a single key-value pair.  This commit allows the
> code to be reduced to just:
> 
>     const struct smap ids = SMAP_INIT1(&ids, "ovn-patch-port", network);
>     ovsrec_port_set_external_ids(port, &ids);
> 
> This new form also eliminates multiple memory allocation and free
> operations, but I doubt that has any real effect on performance;
> the primary goal here is code readability.

I like that the code is so much shorter. However, I'm not sure it's any
more readable to me. The main thing that bothers me is that
smap_destroy() is now only used *some* of the time, where it used to be
nice and consistent.

I wonder if there's something we could do with the macro name that would
make it more clear that it doesn't need to be destroyed.  SMAP_LOCAL_INIT1?

I'm just nit picking, though ... I think it's still an overall net benefit.

Acked-by: Russell Bryant <rbryant@redhat.com>
Ben Pfaff Sept. 8, 2015, 8:42 p.m. UTC | #2
On Tue, Sep 01, 2015 at 08:32:12PM -0400, Russell Bryant wrote:
> On 09/01/2015 04:35 PM, Ben Pfaff wrote:
> > Reviewing the ovn-controller code I started to notice a common pattern:
> > 
> >     struct smap ext_ids = SMAP_INITIALIZER(&ext_ids);
> >     smap_add(&ext_ids, "ovn-patch-port", network);
> >     ovsrec_port_set_external_ids(port, &ext_ids);
> >     smap_destroy(&ext_ids);
> > 
> > This seemed like a bit too much code for something as simple as
> > initializing an smap with a single key-value pair.  This commit allows the
> > code to be reduced to just:
> > 
> >     const struct smap ids = SMAP_INIT1(&ids, "ovn-patch-port", network);
> >     ovsrec_port_set_external_ids(port, &ids);
> > 
> > This new form also eliminates multiple memory allocation and free
> > operations, but I doubt that has any real effect on performance;
> > the primary goal here is code readability.
> 
> I like that the code is so much shorter. However, I'm not sure it's any
> more readable to me. The main thing that bothers me is that
> smap_destroy() is now only used *some* of the time, where it used to be
> nice and consistent.
> 
> I wonder if there's something we could do with the macro name that would
> make it more clear that it doesn't need to be destroyed.  SMAP_LOCAL_INIT1?

I had the same thoughts when I was writing this.  My first thought was
that it should be written so that it is impossible to assign it to a
non-"const" struct hmap.  However, I don't think that's actually
possible because it's always possible to convert a "const" literal to a
non-const one.  That is,
        int x = (const int) 123;
is valid, and so would be:
        struct hmap map = (const struct hmap) { ... };

Still, the intent here is that the hmap target should be const.  If that
is followed, then calling hmap_destroy() will produce a compiler error
message.  If that is not followed, then hmap_destroy() becomes a no-op
(because the initializer sets hmap.buckets to &hmap.one, and in that
case hmap_destroy() does nothing) but smap_destroy() should cause a
segfault due to freeing non-allocated storage, so I think the problem
would be found on the first test.

My first name for this was SMAP_CONST_INIT1, but it was too long in my
opinion; it forces lines elsewhere to be split.  Do you like
SMAP_CONST1?

> I'm just nit picking, though ... I think it's still an overall net benefit.
> 
> Acked-by: Russell Bryant <rbryant@redhat.com>

Thanks.  I'll apply this once the discussion seems to have concluded.
Russell Bryant Sept. 9, 2015, 12:52 a.m. UTC | #3
On 09/08/2015 04:42 PM, Ben Pfaff wrote:
> My first name for this was SMAP_CONST_INIT1, but it was too long in my
> opinion; it forces lines elsewhere to be split.  Do you like
> SMAP_CONST1?

SMAP_CONST1 sounds great.  It's still short, yet nicely informative.

Patch
diff mbox

diff --git a/lib/hmap.h b/lib/hmap.h
index cab567e..7c87c33 100644
--- a/lib/hmap.h
+++ b/lib/hmap.h
@@ -68,6 +68,12 @@  struct hmap {
 #define HMAP_INITIALIZER(HMAP) \
     { (struct hmap_node **const) &(HMAP)->one, NULL, 0, 0 }
 
+/* Initializer for an immutable struct hmap 'HMAP' that contains a single
+ * 'NODE'. */
+#define HMAP_INIT1(HMAP, NODE) {                                    \
+        CONST_CAST(struct hmap_node **, &(HMAP)->one), NODE, 0, 1 }
+#define HMAP_NODE_INIT(HASH) { HASH, NULL }
+
 /* Initialization. */
 void hmap_init(struct hmap *);
 void hmap_destroy(struct hmap *);
diff --git a/lib/smap.h b/lib/smap.h
index 846e384..cf9ed3e 100644
--- a/lib/smap.h
+++ b/lib/smap.h
@@ -39,6 +39,25 @@  struct smap_node {
 #define SMAP_FOR_EACH_SAFE(SMAP_NODE, NEXT, SMAP) \
     HMAP_FOR_EACH_SAFE (SMAP_NODE, NEXT, node, &(SMAP)->map)
 
+/* Initializer for an immutable struct smap 'SMAP' that contains a single
+ * 'KEY'-'VALUE' pair, e.g.
+ *
+ *     const struct smap smap = SMAP1_INIT1(&smap, "key", "value");
+ *
+ * An smap initialized this way must not be modified or destroyed.
+ *
+ * The 'KEY' argument is evaluated multiple times.
+ */
+#define SMAP_INIT1(SMAP, KEY, VALUE) {                                  \
+        HMAP_INIT1(&(SMAP)->map,                                        \
+                   (&(struct smap_node) SMAP_NODE_INIT(KEY, VALUE).node)) \
+            }
+#define SMAP_NODE_INIT(KEY, VALUE) {                \
+        HMAP_NODE_INIT(hash_string(KEY, 0)),        \
+                       CONST_CAST(char *, KEY),     \
+                       CONST_CAST(char *, VALUE) }
+
+
 void smap_init(struct smap *);
 void smap_destroy(struct smap *);
 
diff --git a/ovn/controller/encaps.c b/ovn/controller/encaps.c
index 070b741..d050e12 100644
--- a/ovn/controller/encaps.c
+++ b/ovn/controller/encaps.c
@@ -149,7 +149,6 @@  tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id,
     }
 
     /* No such port, so add one. */
-    struct smap external_ids = SMAP_INITIALIZER(&external_ids);
     struct smap options = SMAP_INITIALIZER(&options);
     struct ovsrec_port *port, **ports;
     struct ovsrec_interface *iface;
@@ -174,9 +173,8 @@  tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id,
     port = ovsrec_port_insert(tc->ovs_txn);
     ovsrec_port_set_name(port, port_name);
     ovsrec_port_set_interfaces(port, &iface, 1);
-    smap_add(&external_ids, "ovn-chassis-id", new_chassis_id);
-    ovsrec_port_set_external_ids(port, &external_ids);
-    smap_destroy(&external_ids);
+    const struct smap ids = SMAP_INIT1(&ids, "ovn-chassis-id", new_chassis_id);
+    ovsrec_port_set_external_ids(port, &ids);
 
     ports = xmalloc(sizeof *tc->br_int->ports * (tc->br_int->n_ports + 1));
     for (i = 0; i < tc->br_int->n_ports; i++) {
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 1608cc4..85ebd9e 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -160,19 +160,15 @@  create_patch_port(struct controller_ctx *ctx,
     iface = ovsrec_interface_insert(ctx->ovs_idl_txn);
     ovsrec_interface_set_name(iface, port_name);
     ovsrec_interface_set_type(iface, "patch");
-    struct smap options = SMAP_INITIALIZER(&options);
-    smap_add(&options, "peer", peer_port_name);
+    const struct smap options = SMAP_INIT1(&options, "peer", peer_port_name);
     ovsrec_interface_set_options(iface, &options);
-    smap_destroy(&options);
 
     struct ovsrec_port *port;
     port = ovsrec_port_insert(ctx->ovs_idl_txn);
     ovsrec_port_set_name(port, port_name);
     ovsrec_port_set_interfaces(port, &iface, 1);
-    struct smap ext_ids = SMAP_INITIALIZER(&ext_ids);
-    smap_add(&ext_ids, "ovn-patch-port", network);
-    ovsrec_port_set_external_ids(port, &ext_ids);
-    smap_destroy(&ext_ids);
+    const struct smap ids = SMAP_INIT1(&ids, "ovn-patch-port", network);
+    ovsrec_port_set_external_ids(port, &ids);
 
     struct ovsrec_port **ports;
     ports = xmalloc(sizeof *ports * (b1->n_ports + 1));
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index befe122..0869e69 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -309,12 +309,10 @@  build_datapaths(struct northd_context *ctx, struct hmap *datapaths)
 
             od->sb = sbrec_datapath_binding_insert(ctx->ovnsb_txn);
 
-            struct smap external_ids = SMAP_INITIALIZER(&external_ids);
             char uuid_s[UUID_LEN + 1];
             sprintf(uuid_s, UUID_FMT, UUID_ARGS(&od->nb->header_.uuid));
-            smap_add(&external_ids, "logical-switch", uuid_s);
-            sbrec_datapath_binding_set_external_ids(od->sb, &external_ids);
-            smap_destroy(&external_ids);
+            const struct smap ids = SMAP_INIT1(&ids, "logical-switch", uuid_s);
+            sbrec_datapath_binding_set_external_ids(od->sb, &ids);
 
             sbrec_datapath_binding_set_tunnel_key(od->sb, tunnel_key);
         }
@@ -888,13 +886,12 @@  build_lflows(struct northd_context *ctx, struct hmap *datapaths,
         sbrec_logical_flow_set_match(sbflow, lflow->match);
         sbrec_logical_flow_set_actions(sbflow, lflow->actions);
 
-        struct smap external_ids = SMAP_INITIALIZER(&external_ids);
-        smap_add(&external_ids, "stage-name",
-                 lflow->pipeline == P_IN ?
-                  ingress_stage_to_str(lflow->table_id) :
-                  egress_stage_to_str(lflow->table_id));
-        sbrec_logical_flow_set_external_ids(sbflow, &external_ids);
-        smap_destroy(&external_ids);
+        const struct smap ids = SMAP_INIT1(
+            &ids, "stage-name",
+            (lflow->pipeline == P_IN
+             ? ingress_stage_to_str(lflow->table_id)
+             : egress_stage_to_str(lflow->table_id)));
+        sbrec_logical_flow_set_external_ids(sbflow, &ids);
 
         ovn_lflow_destroy(&lflows, lflow);
     }
diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c
index e177060..d651da7 100644
--- a/utilities/ovs-vsctl.c
+++ b/utilities/ovs-vsctl.c
@@ -1091,10 +1091,8 @@  cmd_emer_reset(struct ctl_context *ctx)
         /* We only want to save the "hwaddr" key from other_config. */
         hwaddr = smap_get(&br->other_config, "hwaddr");
         if (hwaddr) {
-            struct smap smap = SMAP_INITIALIZER(&smap);
-            smap_add(&smap, "hwaddr", hwaddr);
+            const struct smap smap = SMAP_INIT1(&smap, "hwaddr", hwaddr);
             ovsrec_bridge_set_other_config(br, &smap);
-            smap_destroy(&smap);
         } else {
             ovsrec_bridge_set_other_config(br, NULL);
         }