<p>Richard Mudgett has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/6765">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">cdr.c: Add container to key off of Party B channel names.<br><br>The CDR performance gets worse the further it gets behind in processing<br>stasis messages.  One of the reasons is because of a n*m loop used when<br>processing Party B information.<br><br>* Added a new CDR container that is keyed to Party B so we don't need such<br>a large loop when processing Party B information.<br><br>NOTE: To reduce the size of the patch I deferred to another patch the<br>renaming of the Party A active_cdrs_by_channel container to<br>active_cdrs_master and renaming the container's hash and cmp functions<br>appropriately.<br><br>ASTERISK-27335<br><br>Change-Id: I0bf66e8868f8adaa4b5dcf9e682e34951c350249<br>---<br>M main/cdr.c<br>1 file changed, 298 insertions(+), 131 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/65/6765/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/main/cdr.c b/main/cdr.c<br>index 85c22fd9..4c94f6b 100644<br>--- a/main/cdr.c<br>+++ b/main/cdr.c<br>@@ -351,6 +351,9 @@<br> /*! \brief A container of the active CDRs indexed by Party A channel id */<br> static struct ao2_container *active_cdrs_by_channel;<br> <br>+/*! \brief A container of all active CDRs indexed by Party B channel name */<br>+static struct ao2_container *active_cdrs_all;<br>+<br> /*! \brief Message router for stasis messages regarding channel state */<br> static struct stasis_message_router *stasis_router;<br> <br>@@ -713,6 +716,7 @@<br>           AST_STRING_FIELD(data);             /*!< The data for the last accepted application party A was in */<br>              AST_STRING_FIELD(context);          /*!< The accepted context for Party A */<br>               AST_STRING_FIELD(exten);            /*!< The accepted extension for Party A */<br>+            AST_STRING_FIELD(party_b_name);     /*!< Party B channel name. Cached here as it is the all CDRs container key */<br>  );<br>    struct cdr_object *next;                /*!< The next CDR object in the chain */<br>   struct cdr_object *last;                /*!< The last CDR object in the chain */<br>@@ -846,6 +850,110 @@<br>         break;<br>     }<br>     return cmp ? 0 : CMP_MATCH;<br>+}<br>+<br>+/*!<br>+ * \internal<br>+ * \brief Hash function for all CDR container indexed by Party B channel name.<br>+ */<br>+static int cdr_all_hash_fn(const void *obj, const int flags)<br>+{<br>+  const struct cdr_object *cdr;<br>+        const char *key;<br>+<br>+  switch (flags & OBJ_SEARCH_MASK) {<br>+       case OBJ_SEARCH_KEY:<br>+         key = obj;<br>+           break;<br>+       case OBJ_SEARCH_OBJECT:<br>+              cdr = obj;<br>+           key = cdr->party_b_name;<br>+          break;<br>+       default:<br>+             ast_assert(0);<br>+               return 0;<br>+    }<br>+    return ast_str_case_hash(key);<br>+}<br>+<br>+/*!<br>+ * \internal<br>+ * \brief Comparison function for all CDR container indexed by Party B channel name.<br>+ */<br>+static int cdr_all_cmp_fn(void *obj, void *arg, int flags)<br>+{<br>+    struct cdr_object *left = obj;<br>+    struct cdr_object *right = arg;<br>+    const char *right_key = arg;<br>+    int cmp;<br>+<br>+    switch (flags & OBJ_SEARCH_MASK) {<br>+    case OBJ_SEARCH_OBJECT:<br>+        right_key = right->party_b_name;<br>+        /* Fall through */<br>+    case OBJ_SEARCH_KEY:<br>+        cmp = strcasecmp(left->party_b_name, right_key);<br>+        break;<br>+    case OBJ_SEARCH_PARTIAL_KEY:<br>+        /*<br>+         * We could also use a partial key struct containing a length<br>+         * so strlen() does not get called for every comparison instead.<br>+         */<br>+        cmp = strncasecmp(left->party_b_name, right_key, strlen(right_key));<br>+        break;<br>+    default:<br>+        /* Sort can only work on something with a full or partial key. */<br>+        ast_assert(0);<br>+        cmp = 0;<br>+        break;<br>+    }<br>+    return cmp ? 0 : CMP_MATCH;<br>+}<br>+<br>+/*!<br>+ * \internal<br>+ * \brief Relink the CDR because Party B's snapshot changed.<br>+ * \since 13.18.0<br>+ *<br>+ * \return Nothing<br>+ */<br>+static void cdr_all_relink(struct cdr_object *cdr)<br>+{<br>+   ao2_lock(active_cdrs_all);<br>+   if (cdr->party_b.snapshot) {<br>+              if (strcasecmp(cdr->party_b_name, cdr->party_b.snapshot->name)) {<br>+                   ao2_unlink_flags(active_cdrs_all, cdr, OBJ_NOLOCK);<br>+                  ast_string_field_set(cdr, party_b_name, cdr->party_b.snapshot->name);<br>+                  ao2_link_flags(active_cdrs_all, cdr, OBJ_NOLOCK);<br>+            }<br>+    } else {<br>+             ao2_unlink_flags(active_cdrs_all, cdr, OBJ_NOLOCK);<br>+          ast_string_field_set(cdr, party_b_name, "");<br>+       }<br>+    ao2_unlock(active_cdrs_all);<br>+}<br>+<br>+/*!<br>+ * \internal<br>+ * \brief Unlink the master CDR and chained records from the active_cdrs_all container.<br>+ * \since 13.18.0<br>+ *<br>+ * \return Nothing<br>+ */<br>+static void cdr_all_unlink(struct cdr_object *cdr)<br>+{<br>+      struct cdr_object *cur;<br>+      struct cdr_object *next;<br>+<br>+  ast_assert(cdr->is_root);<br>+<br>+      ao2_lock(active_cdrs_all);<br>+   for (cur = cdr->next; cur; cur = next) {<br>+          next = cur->next;<br>+         ao2_unlink_flags(active_cdrs_all, cur, OBJ_NOLOCK);<br>+          ast_string_field_set(cur, party_b_name, "");<br>+       }<br>+    ao2_unlock(active_cdrs_all);<br> }<br> <br> /*!<br>@@ -1523,6 +1631,7 @@<br>              CDR_DEBUG("%p - Updated Party A %s snapshot\n", cdr,<br>                        cdr->party_a.snapshot->name);<br>           cdr_object_swap_snapshot(&cdr->party_b, peer);<br>+                cdr_all_relink(cdr);<br>          CDR_DEBUG("%p - Updated Party B %s snapshot\n", cdr,<br>                        cdr->party_b.snapshot->name);<br> <br>@@ -1570,6 +1679,7 @@<br>                 CDR_DEBUG("%p - Party A %s has new Party B %s\n",<br>                   cdr, cdr->party_a.snapshot->name, cand_cdr->party_a.snapshot->name);<br>              cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_a);<br>+           cdr_all_relink(cdr);<br>          if (!cand_cdr->party_b.snapshot) {<br>                         /* We just stole them - finalize their CDR. Note that this won't<br>                   * transition their state, it just sets the end time and the<br>@@ -1590,6 +1700,7 @@<br>           CDR_DEBUG("%p - Party A %s has new Party B %s\n",<br>                   cdr, cdr->party_a.snapshot->name, cand_cdr->party_b.snapshot->name);<br>              cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_b);<br>+           cdr_all_relink(cdr);<br>          return 0;<br>     }<br> <br>@@ -1750,8 +1861,6 @@<br> <br> static enum process_bridge_enter_results dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)<br> {<br>-      struct ao2_iterator it_cdrs;<br>- char *channel_id;<br>     int success = 0;<br> <br>   ast_string_field_set(cdr, bridge, bridge->uniqueid);<br>@@ -1763,50 +1872,51 @@<br>              return BRIDGE_ENTER_ONLY_PARTY;<br>       }<br> <br>- for (it_cdrs = ao2_iterator_init(bridge->channels, 0);<br>-            !success && (channel_id = ao2_iterator_next(&it_cdrs));<br>-          ao2_ref(channel_id, -1)) {<br>-           struct cdr_object *cand_cdr_master;<br>-          struct cdr_object *cand_cdr;<br>+ /* If we don't have a Party B (originated channel), skip it */<br>+   if (cdr->party_b.snapshot) {<br>+              struct ao2_iterator it_cdrs;<br>+         char *channel_id;<br> <br>-         cand_cdr_master = ao2_find(active_cdrs_by_channel, channel_id, OBJ_SEARCH_KEY);<br>-              if (!cand_cdr_master) {<br>-                      continue;<br>+            for (it_cdrs = ao2_iterator_init(bridge->channels, 0);<br>+                    !success && (channel_id = ao2_iterator_next(&it_cdrs));<br>+                  ao2_ref(channel_id, -1)) {<br>+                   struct cdr_object *cand_cdr_master;<br>+                  struct cdr_object *cand_cdr;<br>+<br>+                      cand_cdr_master = ao2_find(active_cdrs_by_channel, channel_id, OBJ_SEARCH_KEY);<br>+                      if (!cand_cdr_master) {<br>+                              continue;<br>+                    }<br>+<br>+                 ao2_lock(cand_cdr_master);<br>+                   for (cand_cdr = cand_cdr_master; cand_cdr; cand_cdr = cand_cdr->next) {<br>+                           /* Skip any records that are not in a bridge or in this bridge.<br>+                               * I'm not sure how that would happen, but it pays to be careful. */<br>+                             if (cand_cdr->fn_table != &bridge_state_fn_table<br>+                                      || strcmp(cdr->bridge, cand_cdr->bridge)) {<br>+                                    continue;<br>+                            }<br>+<br>+                         /* Skip any records that aren't our Party B */<br>+                           if (strcasecmp(cdr->party_b.snapshot->name, cand_cdr->party_a.snapshot->name)) {<br>+                                 continue;<br>+                            }<br>+                            cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_a);<br>+                           /* If they have a Party B, they joined up with someone else as their<br>+                          * Party A. Don't finalize them as they're active. Otherwise, we<br>+                              * have stolen them so they need to be finalized.<br>+                             */<br>+                          if (!cand_cdr->party_b.snapshot) {<br>+                                        cdr_object_finalize(cand_cdr);<br>+                               }<br>+                            success = 1;<br>+                         break;<br>+                       }<br>+                    ao2_unlock(cand_cdr_master);<br>+                 ao2_cleanup(cand_cdr_master);<br>                 }<br>-<br>-         ao2_lock(cand_cdr_master);<br>-           for (cand_cdr = cand_cdr_master; cand_cdr; cand_cdr = cand_cdr->next) {<br>-                   /* Skip any records that are not in a bridge or in this bridge.<br>-                       * I'm not sure how that would happen, but it pays to be careful. */<br>-                     if (cand_cdr->fn_table != &bridge_state_fn_table ||<br>-                                   strcmp(cdr->bridge, cand_cdr->bridge)) {<br>-                               continue;<br>-                    }<br>-<br>-                 /* If we don't have a Party B (originated channel), skip it */<br>-                   if (!cdr->party_b.snapshot) {<br>-                             continue;<br>-                    }<br>-<br>-                 /* Skip any records that aren't our Party B */<br>-                   if (strcasecmp(cdr->party_b.snapshot->name, cand_cdr->party_a.snapshot->name)) {<br>-                         continue;<br>-                    }<br>-                    cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_a);<br>-                   /* If they have a Party B, they joined up with someone else as their<br>-                  * Party A. Don't finalize them as they're active. Otherwise, we<br>-                      * have stolen them so they need to be finalized.<br>-                     */<br>-                  if (!cand_cdr->party_b.snapshot) {<br>-                                cdr_object_finalize(cand_cdr);<br>-                       }<br>-                    success = 1;<br>-                 break;<br>-               }<br>-            ao2_unlock(cand_cdr_master);<br>-         ao2_cleanup(cand_cdr_master);<br>+                ao2_iterator_destroy(&it_cdrs);<br>   }<br>-    ao2_iterator_destroy(&it_cdrs);<br> <br>        /* We always transition state, even if we didn't get a peer */<br>    cdr_object_transition_state(cdr, &bridge_state_fn_table);<br>@@ -2047,38 +2157,54 @@<br>        ao2_cleanup(cdr);<br> }<br> <br>-static int cdr_object_finalize_party_b(void *obj, void *arg, int flags)<br>+static int cdr_object_finalize_party_b(void *obj, void *arg, void *data, int flags)<br> {<br>  struct cdr_object *cdr = obj;<br>-        struct ast_channel_snapshot *party_b = arg;<br>-  struct cdr_object *it_cdr;<br> <br>-        for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {<br>-               if (it_cdr->party_b.snapshot<br>-                      && !strcasecmp(it_cdr->party_b.snapshot->name, party_b->name)) {<br>-                    /* Don't transition to the finalized state - let the Party A do<br>-                   * that when its ready<br>-                        */<br>-                  cdr_object_finalize(it_cdr);<br>-         }<br>+    if (!strcasecmp(cdr->party_b_name, arg)) {<br>+#ifdef AST_DEVMODE<br>+           struct ast_channel_snapshot *party_b = data;<br>+<br>+              /*<br>+            * For sanity's sake we also assert the party_b snapshot<br>+          * is consistent with the key.<br>+                */<br>+          ast_assert(cdr->party_b.snapshot<br>+                  && !strcasecmp(cdr->party_b.snapshot->name, party_b->name));<br>+#endif<br>+<br>+            /* Don't transition to the finalized state - let the Party A do<br>+           * that when its ready<br>+                */<br>+          cdr_object_finalize(cdr);<br>     }<br>     return 0;<br> }<br> <br>-static int cdr_object_update_party_b(void *obj, void *arg, int flags)<br>+static int cdr_object_update_party_b(void *obj, void *arg, void *data, int flags)<br> {<br>      struct cdr_object *cdr = obj;<br>-        struct ast_channel_snapshot *party_b = arg;<br>-  struct cdr_object *it_cdr;<br> <br>-        for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {<br>-               if (!it_cdr->fn_table->process_party_b) {<br>-                      continue;<br>+    if (cdr->fn_table->process_party_b<br>+             && !strcasecmp(cdr->party_b_name, arg)) {<br>+         struct ast_channel_snapshot *party_b = data;<br>+<br>+              /*<br>+            * For sanity's sake we also check the party_b snapshot<br>+           * for consistency with the key.  The callback needs and<br>+              * asserts the snapshot to be this way.<br>+               */<br>+          if (!cdr->party_b.snapshot<br>+                        || strcasecmp(cdr->party_b.snapshot->name, party_b->name)) {<br>+                        ast_log(LOG_NOTICE,<br>+                          "CDR for Party A %s(%s) has inconsistent Party B %s name.  Message can be ignored but this shouldn't happen.\n",<br>+                               cdr->linkedid,<br>+                            cdr->party_a.snapshot->name,<br>+                           cdr->party_b_name);<br>+                       return 0;<br>             }<br>-            if (it_cdr->party_b.snapshot<br>-                      && !strcasecmp(it_cdr->party_b.snapshot->name, party_b->name)) {<br>-                    it_cdr->fn_table->process_party_b(it_cdr, party_b);<br>-            }<br>+<br>+         cdr->fn_table->process_party_b(cdr, party_b);<br>   }<br>     return 0;<br> }<br>@@ -2152,44 +2278,46 @@<br>                name = new_snapshot ? new_snapshot->name : old_snapshot->name;<br>          ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", name);<br>          ast_assert(0);<br>-       } else {<br>+     } else if (new_snapshot) {<br>+           int all_reject = 1;<br>+<br>                ao2_lock(cdr);<br>-               if (new_snapshot) {<br>-                  int all_reject = 1;<br>+          for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {<br>+                       if (!it_cdr->fn_table->process_party_a) {<br>+                              continue;<br>+                    }<br>+                    all_reject &= it_cdr->fn_table->process_party_a(it_cdr, new_snapshot);<br>+             }<br>+            if (all_reject && check_new_cdr_needed(old_snapshot, new_snapshot)) {<br>+                        /* We're not hung up and we have a new snapshot - we need a new CDR */<br>+                   struct cdr_object *new_cdr;<br> <br>-                       for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {<br>-                               if (!it_cdr->fn_table->process_party_a) {<br>-                                      continue;<br>-                            }<br>-                            all_reject &= it_cdr->fn_table->process_party_a(it_cdr, new_snapshot);<br>+                     new_cdr = cdr_object_create_and_append(cdr);<br>+                 if (new_cdr) {<br>+                               new_cdr->fn_table->process_party_a(new_cdr, new_snapshot);<br>                      }<br>-                    if (all_reject && check_new_cdr_needed(old_snapshot, new_snapshot)) {<br>-                                /* We're not hung up and we have a new snapshot - we need a new CDR */<br>-                           struct cdr_object *new_cdr;<br>-<br>-                               new_cdr = cdr_object_create_and_append(cdr);<br>-                         if (new_cdr) {<br>-                                       new_cdr->fn_table->process_party_a(new_cdr, new_snapshot);<br>-                             }<br>-                    }<br>-            } else {<br>-                     CDR_DEBUG("%p - Beginning finalize/dispatch for %s\n", cdr, old_snapshot->name);<br>-                        for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {<br>-                               cdr_object_finalize(it_cdr);<br>-                 }<br>-                    cdr_object_dispatch(cdr);<br>-                    ao2_unlink(active_cdrs_by_channel, cdr);<br>              }<br>             ao2_unlock(cdr);<br>+     } else {<br>+             ao2_lock(cdr);<br>+               CDR_DEBUG("%p - Beginning finalize/dispatch for %s\n", cdr, old_snapshot->name);<br>+                for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {<br>+                       cdr_object_finalize(it_cdr);<br>+         }<br>+            cdr_object_dispatch(cdr);<br>+            ao2_unlock(cdr);<br>+<br>+          cdr_all_unlink(cdr);<br>+         ao2_unlink(active_cdrs_by_channel, cdr);<br>      }<br> <br>  /* Handle Party B */<br>  if (new_snapshot) {<br>-          ao2_callback(active_cdrs_by_channel, OBJ_NODATA, cdr_object_update_party_b,<br>-                  new_snapshot);<br>+               ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,<br>+                       cdr_object_update_party_b, (char *) new_snapshot->name, new_snapshot);<br>     } else {<br>-             ao2_callback(active_cdrs_by_channel, OBJ_NODATA, cdr_object_finalize_party_b,<br>-                        old_snapshot);<br>+               ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,<br>+                       cdr_object_finalize_party_b, (char *) old_snapshot->name, old_snapshot);<br>   }<br> <br>  ao2_cleanup(cdr);<br>@@ -2201,29 +2329,25 @@<br> };<br> <br> /*! \brief Callback used to notify CDRs of a Party B leaving the bridge */<br>-static int cdr_object_party_b_left_bridge_cb(void *obj, void *arg, int flags)<br>+static int cdr_object_party_b_left_bridge_cb(void *obj, void *arg, void *data, int flags)<br> {<br>       struct cdr_object *cdr = obj;<br>-        struct bridge_leave_data *leave_data = arg;<br>-  struct cdr_object *it_cdr;<br>+   struct bridge_leave_data *leave_data = data;<br> <br>-      if (strcmp(cdr->bridge, leave_data->bridge->uniqueid)) {<br>-            return 0;<br>-    }<br>-    for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {<br>-               if (it_cdr->fn_table != &bridge_state_fn_table) {<br>-                     continue;<br>-            }<br>-            if (!it_cdr->party_b.snapshot) {<br>-                  continue;<br>-            }<br>-            if (strcasecmp(it_cdr->party_b.snapshot->name, leave_data->channel->name)) {<br>-                     continue;<br>-            }<br>+    if (cdr->fn_table == &bridge_state_fn_table<br>+           && !strcmp(cdr->bridge, leave_data->bridge->uniqueid)<br>+               && !strcasecmp(cdr->party_b_name, arg)) {<br>+         /*<br>+            * For sanity's sake we also assert the party_b snapshot<br>+          * is consistent with the key.<br>+                */<br>+          ast_assert(cdr->party_b.snapshot<br>+                  && !strcasecmp(cdr->party_b.snapshot->name, leave_data->channel->name));<br>+<br>               /* It is our Party B, in our bridge. Set the end time and let the handler<br>              * transition our CDR appropriately when we leave the bridge.<br>                  */<br>-          cdr_object_finalize(it_cdr);<br>+         cdr_object_finalize(cdr);<br>     }<br>     return 0;<br> }<br>@@ -2299,8 +2423,8 @@<br>  /* Party B */<br>         if (left_bridge<br>               && strcmp(bridge->subclass, "parking")) {<br>-               ao2_callback(active_cdrs_by_channel, OBJ_NODATA,<br>-                     cdr_object_party_b_left_bridge_cb,<br>+           ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,<br>+                       cdr_object_party_b_left_bridge_cb, (char *) leave_data.channel->name,<br>                      &leave_data);<br>     }<br> <br>@@ -2323,6 +2447,7 @@<br>           return;<br>       }<br>     cdr_object_snapshot_copy(&new_cdr->party_b, party_b);<br>+ cdr_all_relink(new_cdr);<br>      cdr_object_check_party_a_answer(new_cdr);<br>     ast_string_field_set(new_cdr, bridge, cdr->bridge);<br>        cdr_object_transition_state(new_cdr, &bridge_state_fn_table);<br>@@ -2381,6 +2506,7 @@<br>                              cand_cdr, cand_cdr->party_a.snapshot->name,<br>                             cdr->party_a.snapshot->name);<br>                   cdr_object_snapshot_copy(&cand_cdr->party_b, &cdr->party_a);<br>+                   cdr_all_relink(cand_cdr);<br>                     /* It's possible that this joined at one point and was never chosen<br>                        * as party A. Clear their end time, as it would be set in such a<br>                      * case.<br>@@ -3269,21 +3395,24 @@<br> };<br> <br> /*! \brief Callback used to update the userfield on Party B on all CDRs */<br>-static int cdr_object_update_party_b_userfield_cb(void *obj, void *arg, int flags)<br>+static int cdr_object_update_party_b_userfield_cb(void *obj, void *arg, void *data, int flags)<br> {<br>      struct cdr_object *cdr = obj;<br>-        struct party_b_userfield_update *info = arg;<br>- struct cdr_object *it_cdr;<br> <br>-        for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {<br>-               if (it_cdr->fn_table == &finalized_state_fn_table && it_cdr->next != NULL) {<br>-                       continue;<br>-            }<br>-            if (it_cdr->party_b.snapshot<br>-                      && !strcasecmp(it_cdr->party_b.snapshot->name, info->channel_name)) {<br>-                       strcpy(it_cdr->party_b.userfield, info->userfield);<br>-            }<br>+    if ((cdr->fn_table != &finalized_state_fn_table || !cdr->next)<br>+             && !strcasecmp(cdr->party_b_name, arg)) {<br>+         struct party_b_userfield_update *info = data;<br>+<br>+             /*<br>+            * For sanity's sake we also assert the party_b snapshot<br>+          * is consistent with the key.<br>+                */<br>+          ast_assert(cdr->party_b.snapshot<br>+                  && !strcasecmp(cdr->party_b.snapshot->name, info->channel_name));<br>+<br>+                strcpy(cdr->party_b.userfield, info->userfield);<br>        }<br>+<br>  return 0;<br> }<br> <br>@@ -3291,8 +3420,8 @@<br> {<br>   struct cdr_object *cdr;<br>       struct party_b_userfield_update party_b_info = {<br>-                     .channel_name = channel_name,<br>-                        .userfield = userfield,<br>+              .channel_name = channel_name,<br>+                .userfield = userfield,<br>       };<br>    struct cdr_object *it_cdr;<br> <br>@@ -3310,9 +3439,9 @@<br>  }<br> <br>  /* Handle Party B */<br>- ao2_callback(active_cdrs_by_channel, OBJ_NODATA,<br>-                     cdr_object_update_party_b_userfield_cb,<br>-                      &party_b_info);<br>+  ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,<br>+               cdr_object_update_party_b_userfield_cb, (char *) party_b_info.channel_name,<br>+          &party_b_info);<br> <br>        ao2_cleanup(cdr);<br> }<br>@@ -3485,6 +3614,7 @@<br>          if (cdr_obj->party_b.snapshot) {<br>                   new_cdr->party_b.snapshot = cdr_obj->party_b.snapshot;<br>                  ao2_ref(new_cdr->party_b.snapshot, +1);<br>+                   cdr_all_relink(new_cdr);<br>                      strcpy(new_cdr->party_b.userfield, cdr_obj->party_b.userfield);<br>                         new_cdr->party_b.flags = cdr_obj->party_b.flags;<br>                        if (ast_test_flag(options, AST_CDR_FLAG_KEEP_VARS)) {<br>@@ -4074,7 +4204,9 @@<br>  cdr_object_dispatch(cdr);<br>     ao2_unlock(cdr);<br> <br>-  return 0;<br>+    cdr_all_unlink(cdr);<br>+<br>+      return CMP_MATCH;<br> }<br> <br> static void finalize_batch_mode(void)<br>@@ -4200,8 +4332,8 @@<br> <br>    STASIS_MESSAGE_TYPE_CLEANUP(cdr_sync_message_type);<br> <br>-       ao2_callback(active_cdrs_by_channel, OBJ_NODATA, cdr_object_dispatch_all_cb,<br>-         NULL);<br>+       ao2_callback(active_cdrs_by_channel, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,<br>+         cdr_object_dispatch_all_cb, NULL);<br>    finalize_batch_mode();<br>        ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));<br>   ast_sched_context_destroy(sched);<br>@@ -4213,8 +4345,12 @@<br>     ao2_global_obj_release(module_configs);<br> <br>    ao2_container_unregister("cdrs_by_channel");<br>-       ao2_ref(active_cdrs_by_channel, -1);<br>+ ao2_cleanup(active_cdrs_by_channel);<br>  active_cdrs_by_channel = NULL;<br>+<br>+    ao2_container_unregister("cdrs_all");<br>+      ao2_cleanup(active_cdrs_all);<br>+        active_cdrs_all = NULL;<br> }<br> <br> static void cdr_enable_batch_mode(struct ast_cdr_config *config)<br>@@ -4259,6 +4395,30 @@<br>             prnt(where, "Party A: %s; Party B: %s; Bridge %s\n", it_cdr->party_a.snapshot->name, it_cdr->party_b.snapshot ? it_cdr->party_b.snapshot->name : "<unknown>",<br>                                it_cdr->bridge);<br>   }<br>+}<br>+<br>+/*!<br>+ * \internal<br>+ * \brief Print all CDR container object.<br>+ * \since 13.18.0<br>+ *<br>+ * \param v_obj A pointer to the object we want printed.<br>+ * \param where User data needed by prnt to determine where to put output.<br>+ * \param prnt Print output callback function to use.<br>+ *<br>+ * \return Nothing<br>+ */<br>+static void cdr_all_print_fn(void *v_obj, void *where, ao2_prnt_fn *prnt)<br>+{<br>+   struct cdr_object *cdr = v_obj;<br>+<br>+   if (!cdr) {<br>+          return;<br>+      }<br>+    prnt(where, "Party A: %s; Party B: %s; Bridge %s",<br>+         cdr->party_a.snapshot->name,<br>+           cdr->party_b.snapshot ? cdr->party_b.snapshot->name : "<unknown>",<br>+          cdr->bridge);<br> }<br> <br> /*!<br>@@ -4328,6 +4488,13 @@<br>         }<br>     ao2_container_register("cdrs_by_channel", active_cdrs_by_channel, cdr_container_print_fn);<br> <br>+      active_cdrs_all = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,<br>+              NUM_CDR_BUCKETS, cdr_all_hash_fn, NULL, cdr_all_cmp_fn);<br>+     if (!active_cdrs_all) {<br>+              return -1;<br>+   }<br>+    ao2_container_register("cdrs_all", active_cdrs_all, cdr_all_print_fn);<br>+<br>   sched = ast_sched_context_create();<br>   if (!sched) {<br>                 ast_log(LOG_ERROR, "Unable to create schedule context.\n");<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/6765">change 6765</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/6765"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 13 </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: I0bf66e8868f8adaa4b5dcf9e682e34951c350249 </div>
<div style="display:none"> Gerrit-Change-Number: 6765 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Richard Mudgett <rmudgett@digium.com> </div>