From 28f199fe7309cf664b12a18d7c8862d8116e99ea Mon Sep 17 00:00:00 2001
From: Jun Xiao <xiaojuntime@gmail.com>
Date: Mon, 23 Sep 2013 07:38:24 -0700
Subject: [PATCH 05/10] Add support for lattice element free

---
 .../libparistraceroute/algorithms/mda.c            |   16 +++++-
 .../libparistraceroute/algorithms/mda/data.c       |    4 +-
 .../libparistraceroute/algorithms/mda/interface.c  |   16 ++++++
 .../libparistraceroute/algorithms/mda/interface.h  |   17 ++++++
 libparistraceroute/libparistraceroute/lattice.c    |   56 +++++++++++++++-----
 libparistraceroute/libparistraceroute/lattice.h    |   13 +++--
 6 files changed, 101 insertions(+), 21 deletions(-)

diff --git a/libparistraceroute/libparistraceroute/algorithms/mda.c b/libparistraceroute/libparistraceroute/algorithms/mda.c
index 9ff2cd5..e15c8b2 100644
--- a/libparistraceroute/libparistraceroute/algorithms/mda.c
+++ b/libparistraceroute/libparistraceroute/algorithms/mda.c
@@ -443,6 +443,7 @@ static lattice_return_t mda_search_interface(lattice_elt_t * elt, void * data)
 static void mda_handler_init(pt_loop_t * loop, event_t * event, mda_data_t ** pdata, probe_t * skel, const mda_options_t * options)
 {
     mda_data_t * data;
+    mda_interface_t  * root_interface;
 
     /*
     // DEBUG
@@ -469,13 +470,17 @@ static void mda_handler_init(pt_loop_t * loop, event_t * event, mda_data_t ** pd
     // Create a dummy first hop, root of a lattice of discovered interfaces:
     // - not a tree since some interfaces might have several predecessors (diamonds)
     // - we assume the initial hop is not a load balancer
-    if (!lattice_add_element(data->lattice, NULL, mda_interface_create(NULL))) {
+    if (!(root_interface = mda_interface_create(NULL))) goto ERR_INTERFACE_CREATE;
+    if (!lattice_add_element(data->lattice, NULL, root_interface)) {
         goto ERR_LATTICE_ADD_ELEMENT;
     }
+    mda_interface_ref_add(root_interface);
 
     return;
 
 ERR_LATTICE_ADD_ELEMENT:
+    mda_interface_free(root_interface);
+ERR_INTERFACE_CREATE:
 ERR_EXTRACT_DST_IP:
     mda_data_free(data);
 ERR_MDA_DATA_CREATE:
@@ -552,8 +557,11 @@ static void mda_handler_reply(pt_loop_t * loop, event_t * event, mda_data_t * da
         source_interface = lattice_elt_get_data(source_elt);
 
         if (dest_elt) {
-            if (!lattice_connect(data->lattice, source_elt, dest_elt)) {
+            int c = lattice_connect(data->lattice, source_elt, dest_elt);
+            if (c == -1) {
                 goto ERR_LATTICE_CONNECT;
+            } else if (c == 0) {
+                mda_interface_ref_add(dest_interface);
             }
 
             // An interface has the minimum TTL possible, in case of multiple
@@ -572,8 +580,10 @@ static void mda_handler_reply(pt_loop_t * loop, event_t * event, mda_data_t * da
 
         } else {
             if (!lattice_add_element(data->lattice, source_elt, dest_interface)) {
+                mda_interface_free(dest_interface);
                 goto ERR_LATTICE_ADD_ELEMENT;
             }
+            mda_interface_ref_add(dest_interface);
         }
 
         source_interface->received++;
@@ -658,8 +668,10 @@ static void mda_handler_timeout(pt_loop_t *loop, event_t *event, mda_data_t * da
                 new_iface->num_stars = source_interface->num_stars + 1;
 
                 if (!lattice_add_element(data->lattice, source_elt, new_iface)) {
+                    mda_interface_free(new_iface);
                     goto ERROR;
                 }
+                mda_interface_ref_add(new_iface);
 
                 if (!mda_event_new_link(loop, source_interface, new_iface)) {
                     goto ERROR;
diff --git a/libparistraceroute/libparistraceroute/algorithms/mda/data.c b/libparistraceroute/libparistraceroute/algorithms/mda/data.c
index 00e4d08..fc605e6 100644
--- a/libparistraceroute/libparistraceroute/algorithms/mda/data.c
+++ b/libparistraceroute/libparistraceroute/algorithms/mda/data.c
@@ -24,7 +24,7 @@ mda_data_t * mda_data_create()
     return data;
 
 ERR_ADDRESS_CREATE:
-    lattice_free(data->lattice, (ELEMENT_FREE) mda_interface_free);
+    lattice_free(data->lattice, (bool (*)(void *))mda_interface_ref_free);
 ERR_LATTICE_CREATE:
     free(data);
 ERR_MALLOC:
@@ -34,7 +34,7 @@ ERR_MALLOC:
 void mda_data_free(mda_data_t * data)
 {
     if (data) {
-        lattice_free(data->lattice, (ELEMENT_FREE) mda_interface_free);
+        lattice_free(data->lattice, (bool (*)(void *))mda_interface_ref_free);
         address_free(data->dst_ip);
         free(data);
     }
diff --git a/libparistraceroute/libparistraceroute/algorithms/mda/interface.c b/libparistraceroute/libparistraceroute/algorithms/mda/interface.c
index 82654a8..ab54a63 100644
--- a/libparistraceroute/libparistraceroute/algorithms/mda/interface.c
+++ b/libparistraceroute/libparistraceroute/algorithms/mda/interface.c
@@ -25,6 +25,7 @@ mda_interface_t * mda_interface_create(const address_t * address)
     }
 
     mda_interface->type = MDA_LB_TYPE_UNKNOWN;
+    mda_interface->ref_count = 0;
     return mda_interface;
 
 ERR_FLOWS:
@@ -35,6 +36,21 @@ ERR_INTERFACE:
     return NULL;
 }
 
+bool mda_interface_ref_free(mda_interface_t * interface)
+{
+    if (--interface->ref_count == 0) {
+        mda_interface_free(interface);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void mda_interface_ref_add(mda_interface_t * interface)
+{
+    interface->ref_count++;
+}
+
 void mda_interface_free(mda_interface_t * interface)
 {
     if (interface) {
diff --git a/libparistraceroute/libparistraceroute/algorithms/mda/interface.h b/libparistraceroute/libparistraceroute/algorithms/mda/interface.h
index 41a4235..8d84c3f 100644
--- a/libparistraceroute/libparistraceroute/algorithms/mda/interface.h
+++ b/libparistraceroute/libparistraceroute/algorithms/mda/interface.h
@@ -29,6 +29,7 @@ typedef struct {
     bool           enumeration_done;
     mda_lb_type_t  type;             /**< Type of load balancer            */
     unsigned int   ttl;              /**< Time to live related to this hop */
+    unsigned int   ref_count;        /**< Reference count to this hop */
 } mda_interface_t;
 
 /**
@@ -49,6 +50,22 @@ mda_interface_t * mda_interface_create(const address_t * address);
 void mda_interface_free(mda_interface_t * interface);
 
 /**
+ * \brief Release reference count of a mda_interface_t.
+ * \param interface a preallocated mda_interface_t instance.
+ * \return true The interface resource is really freed.
+ *         false Just for reference count decrement.
+ */
+
+bool mda_interface_ref_free(mda_interface_t * interface);
+
+/**
+ * \brief Add reference count of a mda_interface_t by 1.
+ * \param interface a preallocated mda_interface_t instance.
+ */
+
+void mda_interface_ref_add(mda_interface_t * interface);
+
+/**
  * \brief Allocate and attach a new mda_flow_t instance to a given
  *    mda_interface_t instance.
  * \param flow_id The new flow id.
diff --git a/libparistraceroute/libparistraceroute/lattice.c b/libparistraceroute/libparistraceroute/lattice.c
index c00760e..9f34735 100644
--- a/libparistraceroute/libparistraceroute/lattice.c
+++ b/libparistraceroute/libparistraceroute/lattice.c
@@ -33,8 +33,12 @@ ERR_MALLOC:
     return NULL;
 }
 
-void lattice_elt_free(lattice_elt_t * elt) {
-    // TODO element_free
+void lattice_elt_free(lattice_elt_t * elt,
+                      bool (*lattice_element_free)(void *element)) {
+    // Retain the elt if user data is not ready to free.
+    if (lattice_element_free && !lattice_element_free(lattice_elt_get_data(elt))) {
+        return;
+    }
     dynarray_free(elt->siblings, NULL);
     dynarray_free(elt->next, NULL);
     free(elt);
@@ -69,12 +73,37 @@ ERR_MALLOC:
     return NULL;
 }
 
-void lattice_free(lattice_t * lattice, void (*lattice_element_free)(void *element))
+static void lattice_walk_dfs_rec_free(
+    lattice_elt_t * elt,
+    bool (*lattice_element_free)(void *element)
+) {
+    lattice_elt_t    * elt_iter;
+    unsigned int       i;
+
+    for (i = dynarray_get_size(elt->next); i > 0; i--) {
+        elt_iter = dynarray_get_ith_element(elt->next, i - 1);
+        lattice_walk_dfs_rec_free(elt_iter, lattice_element_free);
+        // Remove the "next" from current element to keep entries in
+        // next array always valid, because this element might be deferred
+        // to free for re-visiting from other paths.
+        dynarray_del_ith_element(elt->next, i - 1, NULL);
+    }
+    lattice_elt_free(elt, lattice_element_free);
+}
+
+void lattice_free(lattice_t * lattice, bool (*lattice_element_free)(void *element))
 {
-    /* TODO Free elements and data if needed ? */
-    //if (lattice_element_free)
-    //    lattice_element_free();
+    lattice_elt_t *  root;
+    size_t           i, num_roots;
 
+    // Process all roots
+    num_roots = dynarray_get_size(lattice->roots);
+    for (i = 0; i < num_roots; i++) {
+        root = dynarray_get_ith_element(lattice->roots, i);
+        lattice_walk_dfs_rec_free(root, lattice_element_free);
+    }
+
+    dynarray_free(lattice->roots, NULL);
     free(lattice);
 }
 
@@ -217,8 +246,7 @@ bool lattice_add_element(lattice_t * lattice, lattice_elt_t * predecessor, void
             goto ERR_DYNARRAY_PUSH_ELEMENT;
         }
     } else {
-        // This node has some a predecessor so connect the both nodes.
-        if (!lattice_connect(lattice, predecessor, elt)) {
+        if (lattice_connect(lattice, predecessor, elt) != 0) {
             goto ERR_LATTICE_CONNECT;
         }
     }
@@ -227,12 +255,13 @@ bool lattice_add_element(lattice_t * lattice, lattice_elt_t * predecessor, void
 
 ERR_LATTICE_CONNECT:
 ERR_DYNARRAY_PUSH_ELEMENT:
-    lattice_elt_free(elt);
+    // caller must take care of data recycle
+    lattice_elt_free(elt, NULL);
 ERR_LATTICE_ELT_CREATE:
     return false;
 }
 
-bool lattice_connect(lattice_t * lattice, lattice_elt_t * u, lattice_elt_t * v)
+int lattice_connect(lattice_t * lattice, lattice_elt_t * u, lattice_elt_t * v)
 {
     size_t          i, j, num_next, num_siblings;
     const void    * elt_data = lattice_elt_get_data(v),
@@ -247,7 +276,7 @@ bool lattice_connect(lattice_t * lattice, lattice_elt_t * u, lattice_elt_t * v)
         cur_data = lattice_elt_get_data(cur_elt);
         if ((lattice->cmp && (lattice->cmp(cur_data, elt_data) == 0))
         ||  (cur_data == elt_data)) {
-            return true;
+            return 1;
         }
     }
     
@@ -278,12 +307,13 @@ bool lattice_connect(lattice_t * lattice, lattice_elt_t * u, lattice_elt_t * v)
         goto ERR_DYNARRAY_PUSH_ELEMENT3;
     }
 
-    return true;
+    return 0;
 
 ERR_DYNARRAY_PUSH_ELEMENT3:
+// In case of following failure, garbage pointer in sibling doesn't matter
 ERR_DYNARRAY_PUSH_ELEMENT2:
 ERR_DYNARRAY_PUSH_ELEMENT1:
-    return false;
+    return -1;
 }
 
 static lattice_return_t lattice_element_dump(lattice_elt_t * elt, void * data) {
diff --git a/libparistraceroute/libparistraceroute/lattice.h b/libparistraceroute/libparistraceroute/lattice.h
index 57bab8d..761d535 100644
--- a/libparistraceroute/libparistraceroute/lattice.h
+++ b/libparistraceroute/libparistraceroute/lattice.h
@@ -38,9 +38,12 @@ lattice_elt_t * lattice_elt_create(void * data);
 /**
  * \brief Release a lattice_elt_t instance from the memory.
  * \param elt A lattice_elt_t instance.
+ * \param lattice_element_free Pointer to a function used to free up
+ *    element resources (can be NULL).
  */
 
-void lattice_elt_free(lattice_elt_t * elt);
+void lattice_elt_free(lattice_elt_t * elt,
+                      bool (*lattice_element_free)(void *element));
 
 void * lattice_elt_get_data(const lattice_elt_t * elt);
 
@@ -70,7 +73,7 @@ lattice_t * lattice_create();
  *    lattice_elt_t instance belonging to this lattice.
  */
 
-void lattice_free(lattice_t * lattice, void (*lattice_element_free)(void *element));
+void lattice_free(lattice_t * lattice, bool (*lattice_element_free)(void *element));
 
 /**
  * \brief Retrieve the number of successors of a given lattice node.
@@ -97,10 +100,12 @@ lattice_return_t lattice_walk(lattice_t * lattice, lattice_return_t (*visitor)(l
  * \brief Connect two nodes in the lattice.
  * \param u The first node.
  * \param v The second node.
- * \return true iif successful.
+ * \return -1 failure.
+ *         0  success.
+ *         1  connection is already existing.
  */
 
-bool lattice_connect(lattice_t * lattice, lattice_elt_t * u, lattice_elt_t * v);
+int lattice_connect(lattice_t * lattice, lattice_elt_t * u, lattice_elt_t * v);
 
 /**
  * \brief Add a new node in the lattice.
-- 
1.7.9.5

