[asterisk-commits] cdr.c: Add container to key off of Party B channel names. (asterisk[master])

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Oct 17 10:44:35 CDT 2017


Jenkins2 has submitted this change and it was merged. ( https://gerrit.asterisk.org/6786 )

Change subject: cdr.c: Add container to key off of Party B channel names.
......................................................................

cdr.c: Add container to key off of Party B channel names.

The CDR performance gets worse the further it gets behind in processing
stasis messages.  One of the reasons is because of a n*m loop used when
processing Party B information.

* Added a new CDR container that is keyed to Party B so we don't need such
a large loop when processing Party B information.

NOTE: To reduce the size of the patch I deferred to another patch the
renaming of the Party A active_cdrs_by_channel container to
active_cdrs_master and renaming the container's hash and cmp functions
appropriately.

ASTERISK-27335

Change-Id: I0bf66e8868f8adaa4b5dcf9e682e34951c350249
---
M main/cdr.c
1 file changed, 298 insertions(+), 131 deletions(-)

Approvals:
  Joshua Colp: Looks good to me, but someone else must approve
  George Joseph: Looks good to me, approved
  Jenkins2: Approved for Submit



diff --git a/main/cdr.c b/main/cdr.c
index def3871..2037e83 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -351,6 +351,9 @@
 /*! \brief A container of the active CDRs indexed by Party A channel id */
 static struct ao2_container *active_cdrs_by_channel;
 
+/*! \brief A container of all active CDRs indexed by Party B channel name */
+static struct ao2_container *active_cdrs_all;
+
 /*! \brief Message router for stasis messages regarding channel state */
 static struct stasis_message_router *stasis_router;
 
@@ -713,6 +716,7 @@
 		AST_STRING_FIELD(data);             /*!< The data for the last accepted application party A was in */
 		AST_STRING_FIELD(context);          /*!< The accepted context for Party A */
 		AST_STRING_FIELD(exten);            /*!< The accepted extension for Party A */
+		AST_STRING_FIELD(party_b_name);     /*!< Party B channel name. Cached here as it is the all CDRs container key */
 	);
 	struct cdr_object *next;                /*!< The next CDR object in the chain */
 	struct cdr_object *last;                /*!< The last CDR object in the chain */
@@ -846,6 +850,110 @@
         break;
     }
     return cmp ? 0 : CMP_MATCH;
+}
+
+/*!
+ * \internal
+ * \brief Hash function for all CDR container indexed by Party B channel name.
+ */
+static int cdr_all_hash_fn(const void *obj, const int flags)
+{
+	const struct cdr_object *cdr;
+	const char *key;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_KEY:
+		key = obj;
+		break;
+	case OBJ_SEARCH_OBJECT:
+		cdr = obj;
+		key = cdr->party_b_name;
+		break;
+	default:
+		ast_assert(0);
+		return 0;
+	}
+	return ast_str_case_hash(key);
+}
+
+/*!
+ * \internal
+ * \brief Comparison function for all CDR container indexed by Party B channel name.
+ */
+static int cdr_all_cmp_fn(void *obj, void *arg, int flags)
+{
+    struct cdr_object *left = obj;
+    struct cdr_object *right = arg;
+    const char *right_key = arg;
+    int cmp;
+
+    switch (flags & OBJ_SEARCH_MASK) {
+    case OBJ_SEARCH_OBJECT:
+        right_key = right->party_b_name;
+        /* Fall through */
+    case OBJ_SEARCH_KEY:
+        cmp = strcasecmp(left->party_b_name, right_key);
+        break;
+    case OBJ_SEARCH_PARTIAL_KEY:
+        /*
+         * We could also use a partial key struct containing a length
+         * so strlen() does not get called for every comparison instead.
+         */
+        cmp = strncasecmp(left->party_b_name, right_key, strlen(right_key));
+        break;
+    default:
+        /* Sort can only work on something with a full or partial key. */
+        ast_assert(0);
+        cmp = 0;
+        break;
+    }
+    return cmp ? 0 : CMP_MATCH;
+}
+
+/*!
+ * \internal
+ * \brief Relink the CDR because Party B's snapshot changed.
+ * \since 13.19.0
+ *
+ * \return Nothing
+ */
+static void cdr_all_relink(struct cdr_object *cdr)
+{
+	ao2_lock(active_cdrs_all);
+	if (cdr->party_b.snapshot) {
+		if (strcasecmp(cdr->party_b_name, cdr->party_b.snapshot->name)) {
+			ao2_unlink_flags(active_cdrs_all, cdr, OBJ_NOLOCK);
+			ast_string_field_set(cdr, party_b_name, cdr->party_b.snapshot->name);
+			ao2_link_flags(active_cdrs_all, cdr, OBJ_NOLOCK);
+		}
+	} else {
+		ao2_unlink_flags(active_cdrs_all, cdr, OBJ_NOLOCK);
+		ast_string_field_set(cdr, party_b_name, "");
+	}
+	ao2_unlock(active_cdrs_all);
+}
+
+/*!
+ * \internal
+ * \brief Unlink the master CDR and chained records from the active_cdrs_all container.
+ * \since 13.19.0
+ *
+ * \return Nothing
+ */
+static void cdr_all_unlink(struct cdr_object *cdr)
+{
+	struct cdr_object *cur;
+	struct cdr_object *next;
+
+	ast_assert(cdr->is_root);
+
+	ao2_lock(active_cdrs_all);
+	for (cur = cdr->next; cur; cur = next) {
+		next = cur->next;
+		ao2_unlink_flags(active_cdrs_all, cur, OBJ_NOLOCK);
+		ast_string_field_set(cur, party_b_name, "");
+	}
+	ao2_unlock(active_cdrs_all);
 }
 
 /*!
@@ -1509,6 +1617,7 @@
 		CDR_DEBUG("%p - Updated Party A %s snapshot\n", cdr,
 			cdr->party_a.snapshot->name);
 		cdr_object_swap_snapshot(&cdr->party_b, peer);
+		cdr_all_relink(cdr);
 		CDR_DEBUG("%p - Updated Party B %s snapshot\n", cdr,
 			cdr->party_b.snapshot->name);
 
@@ -1556,6 +1665,7 @@
 		CDR_DEBUG("%p - Party A %s has new Party B %s\n",
 			cdr, cdr->party_a.snapshot->name, cand_cdr->party_a.snapshot->name);
 		cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_a);
+		cdr_all_relink(cdr);
 		if (!cand_cdr->party_b.snapshot) {
 			/* We just stole them - finalize their CDR. Note that this won't
 			 * transition their state, it just sets the end time and the
@@ -1576,6 +1686,7 @@
 		CDR_DEBUG("%p - Party A %s has new Party B %s\n",
 			cdr, cdr->party_a.snapshot->name, cand_cdr->party_b.snapshot->name);
 		cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_b);
+		cdr_all_relink(cdr);
 		return 0;
 	}
 
@@ -1728,8 +1839,6 @@
 
 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)
 {
-	struct ao2_iterator it_cdrs;
-	char *channel_id;
 	int success = 0;
 
 	ast_string_field_set(cdr, bridge, bridge->uniqueid);
@@ -1741,50 +1850,51 @@
 		return BRIDGE_ENTER_ONLY_PARTY;
 	}
 
-	for (it_cdrs = ao2_iterator_init(bridge->channels, 0);
-		!success && (channel_id = ao2_iterator_next(&it_cdrs));
-		ao2_ref(channel_id, -1)) {
-		struct cdr_object *cand_cdr_master;
-		struct cdr_object *cand_cdr;
+	/* If we don't have a Party B (originated channel), skip it */
+	if (cdr->party_b.snapshot) {
+		struct ao2_iterator it_cdrs;
+		char *channel_id;
 
-		cand_cdr_master = ao2_find(active_cdrs_by_channel, channel_id, OBJ_SEARCH_KEY);
-		if (!cand_cdr_master) {
-			continue;
+		for (it_cdrs = ao2_iterator_init(bridge->channels, 0);
+			!success && (channel_id = ao2_iterator_next(&it_cdrs));
+			ao2_ref(channel_id, -1)) {
+			struct cdr_object *cand_cdr_master;
+			struct cdr_object *cand_cdr;
+
+			cand_cdr_master = ao2_find(active_cdrs_by_channel, channel_id, OBJ_SEARCH_KEY);
+			if (!cand_cdr_master) {
+				continue;
+			}
+
+			ao2_lock(cand_cdr_master);
+			for (cand_cdr = cand_cdr_master; cand_cdr; cand_cdr = cand_cdr->next) {
+				/* Skip any records that are not in a bridge or in this bridge.
+				 * I'm not sure how that would happen, but it pays to be careful. */
+				if (cand_cdr->fn_table != &bridge_state_fn_table
+					|| strcmp(cdr->bridge, cand_cdr->bridge)) {
+					continue;
+				}
+
+				/* Skip any records that aren't our Party B */
+				if (strcasecmp(cdr->party_b.snapshot->name, cand_cdr->party_a.snapshot->name)) {
+					continue;
+				}
+				cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_a);
+				/* If they have a Party B, they joined up with someone else as their
+				 * Party A. Don't finalize them as they're active. Otherwise, we
+				 * have stolen them so they need to be finalized.
+				 */
+				if (!cand_cdr->party_b.snapshot) {
+					cdr_object_finalize(cand_cdr);
+				}
+				success = 1;
+				break;
+			}
+			ao2_unlock(cand_cdr_master);
+			ao2_cleanup(cand_cdr_master);
 		}
-
-		ao2_lock(cand_cdr_master);
-		for (cand_cdr = cand_cdr_master; cand_cdr; cand_cdr = cand_cdr->next) {
-			/* Skip any records that are not in a bridge or in this bridge.
-			 * I'm not sure how that would happen, but it pays to be careful. */
-			if (cand_cdr->fn_table != &bridge_state_fn_table ||
-					strcmp(cdr->bridge, cand_cdr->bridge)) {
-				continue;
-			}
-
-			/* If we don't have a Party B (originated channel), skip it */
-			if (!cdr->party_b.snapshot) {
-				continue;
-			}
-
-			/* Skip any records that aren't our Party B */
-			if (strcasecmp(cdr->party_b.snapshot->name, cand_cdr->party_a.snapshot->name)) {
-				continue;
-			}
-			cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_a);
-			/* If they have a Party B, they joined up with someone else as their
-			 * Party A. Don't finalize them as they're active. Otherwise, we
-			 * have stolen them so they need to be finalized.
-			 */
-			if (!cand_cdr->party_b.snapshot) {
-				cdr_object_finalize(cand_cdr);
-			}
-			success = 1;
-			break;
-		}
-		ao2_unlock(cand_cdr_master);
-		ao2_cleanup(cand_cdr_master);
+		ao2_iterator_destroy(&it_cdrs);
 	}
-	ao2_iterator_destroy(&it_cdrs);
 
 	/* We always transition state, even if we didn't get a peer */
 	cdr_object_transition_state(cdr, &bridge_state_fn_table);
@@ -2032,38 +2142,54 @@
 	ao2_cleanup(cdr);
 }
 
-static int cdr_object_finalize_party_b(void *obj, void *arg, int flags)
+static int cdr_object_finalize_party_b(void *obj, void *arg, void *data, int flags)
 {
 	struct cdr_object *cdr = obj;
-	struct ast_channel_snapshot *party_b = arg;
-	struct cdr_object *it_cdr;
 
-	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
-		if (it_cdr->party_b.snapshot
-			&& !strcasecmp(it_cdr->party_b.snapshot->name, party_b->name)) {
-			/* Don't transition to the finalized state - let the Party A do
-			 * that when its ready
-			 */
-			cdr_object_finalize(it_cdr);
-		}
+	if (!strcasecmp(cdr->party_b_name, arg)) {
+#ifdef AST_DEVMODE
+		struct ast_channel_snapshot *party_b = data;
+
+		/*
+		 * For sanity's sake we also assert the party_b snapshot
+		 * is consistent with the key.
+		 */
+		ast_assert(cdr->party_b.snapshot
+			&& !strcasecmp(cdr->party_b.snapshot->name, party_b->name));
+#endif
+
+		/* Don't transition to the finalized state - let the Party A do
+		 * that when its ready
+		 */
+		cdr_object_finalize(cdr);
 	}
 	return 0;
 }
 
-static int cdr_object_update_party_b(void *obj, void *arg, int flags)
+static int cdr_object_update_party_b(void *obj, void *arg, void *data, int flags)
 {
 	struct cdr_object *cdr = obj;
-	struct ast_channel_snapshot *party_b = arg;
-	struct cdr_object *it_cdr;
 
-	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
-		if (!it_cdr->fn_table->process_party_b) {
-			continue;
+	if (cdr->fn_table->process_party_b
+		&& !strcasecmp(cdr->party_b_name, arg)) {
+		struct ast_channel_snapshot *party_b = data;
+
+		/*
+		 * For sanity's sake we also check the party_b snapshot
+		 * for consistency with the key.  The callback needs and
+		 * asserts the snapshot to be this way.
+		 */
+		if (!cdr->party_b.snapshot
+			|| strcasecmp(cdr->party_b.snapshot->name, party_b->name)) {
+			ast_log(LOG_NOTICE,
+				"CDR for Party A %s(%s) has inconsistent Party B %s name.  Message can be ignored but this shouldn't happen.\n",
+				cdr->linkedid,
+				cdr->party_a.snapshot->name,
+				cdr->party_b_name);
+			return 0;
 		}
-		if (it_cdr->party_b.snapshot
-			&& !strcasecmp(it_cdr->party_b.snapshot->name, party_b->name)) {
-			it_cdr->fn_table->process_party_b(it_cdr, party_b);
-		}
+
+		cdr->fn_table->process_party_b(cdr, party_b);
 	}
 	return 0;
 }
@@ -2137,44 +2263,46 @@
 		name = new_snapshot ? new_snapshot->name : old_snapshot->name;
 		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", name);
 		ast_assert(0);
-	} else {
+	} else if (new_snapshot) {
+		int all_reject = 1;
+
 		ao2_lock(cdr);
-		if (new_snapshot) {
-			int all_reject = 1;
+		for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
+			if (!it_cdr->fn_table->process_party_a) {
+				continue;
+			}
+			all_reject &= it_cdr->fn_table->process_party_a(it_cdr, new_snapshot);
+		}
+		if (all_reject && check_new_cdr_needed(old_snapshot, new_snapshot)) {
+			/* We're not hung up and we have a new snapshot - we need a new CDR */
+			struct cdr_object *new_cdr;
 
-			for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
-				if (!it_cdr->fn_table->process_party_a) {
-					continue;
-				}
-				all_reject &= it_cdr->fn_table->process_party_a(it_cdr, new_snapshot);
+			new_cdr = cdr_object_create_and_append(cdr);
+			if (new_cdr) {
+				new_cdr->fn_table->process_party_a(new_cdr, new_snapshot);
 			}
-			if (all_reject && check_new_cdr_needed(old_snapshot, new_snapshot)) {
-				/* We're not hung up and we have a new snapshot - we need a new CDR */
-				struct cdr_object *new_cdr;
-
-				new_cdr = cdr_object_create_and_append(cdr);
-				if (new_cdr) {
-					new_cdr->fn_table->process_party_a(new_cdr, new_snapshot);
-				}
-			}
-		} else {
-			CDR_DEBUG("%p - Beginning finalize/dispatch for %s\n", cdr, old_snapshot->name);
-			for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
-				cdr_object_finalize(it_cdr);
-			}
-			cdr_object_dispatch(cdr);
-			ao2_unlink(active_cdrs_by_channel, cdr);
 		}
 		ao2_unlock(cdr);
+	} else {
+		ao2_lock(cdr);
+		CDR_DEBUG("%p - Beginning finalize/dispatch for %s\n", cdr, old_snapshot->name);
+		for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
+			cdr_object_finalize(it_cdr);
+		}
+		cdr_object_dispatch(cdr);
+		ao2_unlock(cdr);
+
+		cdr_all_unlink(cdr);
+		ao2_unlink(active_cdrs_by_channel, cdr);
 	}
 
 	/* Handle Party B */
 	if (new_snapshot) {
-		ao2_callback(active_cdrs_by_channel, OBJ_NODATA, cdr_object_update_party_b,
-			new_snapshot);
+		ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
+			cdr_object_update_party_b, (char *) new_snapshot->name, new_snapshot);
 	} else {
-		ao2_callback(active_cdrs_by_channel, OBJ_NODATA, cdr_object_finalize_party_b,
-			old_snapshot);
+		ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
+			cdr_object_finalize_party_b, (char *) old_snapshot->name, old_snapshot);
 	}
 
 	ao2_cleanup(cdr);
@@ -2186,29 +2314,25 @@
 };
 
 /*! \brief Callback used to notify CDRs of a Party B leaving the bridge */
-static int cdr_object_party_b_left_bridge_cb(void *obj, void *arg, int flags)
+static int cdr_object_party_b_left_bridge_cb(void *obj, void *arg, void *data, int flags)
 {
 	struct cdr_object *cdr = obj;
-	struct bridge_leave_data *leave_data = arg;
-	struct cdr_object *it_cdr;
+	struct bridge_leave_data *leave_data = data;
 
-	if (strcmp(cdr->bridge, leave_data->bridge->uniqueid)) {
-		return 0;
-	}
-	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
-		if (it_cdr->fn_table != &bridge_state_fn_table) {
-			continue;
-		}
-		if (!it_cdr->party_b.snapshot) {
-			continue;
-		}
-		if (strcasecmp(it_cdr->party_b.snapshot->name, leave_data->channel->name)) {
-			continue;
-		}
+	if (cdr->fn_table == &bridge_state_fn_table
+		&& !strcmp(cdr->bridge, leave_data->bridge->uniqueid)
+		&& !strcasecmp(cdr->party_b_name, arg)) {
+		/*
+		 * For sanity's sake we also assert the party_b snapshot
+		 * is consistent with the key.
+		 */
+		ast_assert(cdr->party_b.snapshot
+			&& !strcasecmp(cdr->party_b.snapshot->name, leave_data->channel->name));
+
 		/* It is our Party B, in our bridge. Set the end time and let the handler
 		 * transition our CDR appropriately when we leave the bridge.
 		 */
-		cdr_object_finalize(it_cdr);
+		cdr_object_finalize(cdr);
 	}
 	return 0;
 }
@@ -2284,8 +2408,8 @@
 	/* Party B */
 	if (left_bridge
 		&& strcmp(bridge->subclass, "parking")) {
-		ao2_callback(active_cdrs_by_channel, OBJ_NODATA,
-			cdr_object_party_b_left_bridge_cb,
+		ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
+			cdr_object_party_b_left_bridge_cb, (char *) leave_data.channel->name,
 			&leave_data);
 	}
 
@@ -2308,6 +2432,7 @@
 		return;
 	}
 	cdr_object_snapshot_copy(&new_cdr->party_b, party_b);
+	cdr_all_relink(new_cdr);
 	cdr_object_check_party_a_answer(new_cdr);
 	ast_string_field_set(new_cdr, bridge, cdr->bridge);
 	cdr_object_transition_state(new_cdr, &bridge_state_fn_table);
@@ -2366,6 +2491,7 @@
 				cand_cdr, cand_cdr->party_a.snapshot->name,
 				cdr->party_a.snapshot->name);
 			cdr_object_snapshot_copy(&cand_cdr->party_b, &cdr->party_a);
+			cdr_all_relink(cand_cdr);
 			/* It's possible that this joined at one point and was never chosen
 			 * as party A. Clear their end time, as it would be set in such a
 			 * case.
@@ -3262,21 +3388,24 @@
 };
 
 /*! \brief Callback used to update the userfield on Party B on all CDRs */
-static int cdr_object_update_party_b_userfield_cb(void *obj, void *arg, int flags)
+static int cdr_object_update_party_b_userfield_cb(void *obj, void *arg, void *data, int flags)
 {
 	struct cdr_object *cdr = obj;
-	struct party_b_userfield_update *info = arg;
-	struct cdr_object *it_cdr;
 
-	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
-		if (it_cdr->fn_table == &finalized_state_fn_table && it_cdr->next != NULL) {
-			continue;
-		}
-		if (it_cdr->party_b.snapshot
-			&& !strcasecmp(it_cdr->party_b.snapshot->name, info->channel_name)) {
-			strcpy(it_cdr->party_b.userfield, info->userfield);
-		}
+	if ((cdr->fn_table != &finalized_state_fn_table || !cdr->next)
+		&& !strcasecmp(cdr->party_b_name, arg)) {
+		struct party_b_userfield_update *info = data;
+
+		/*
+		 * For sanity's sake we also assert the party_b snapshot
+		 * is consistent with the key.
+		 */
+		ast_assert(cdr->party_b.snapshot
+			&& !strcasecmp(cdr->party_b.snapshot->name, info->channel_name));
+
+		strcpy(cdr->party_b.userfield, info->userfield);
 	}
+
 	return 0;
 }
 
@@ -3284,8 +3413,8 @@
 {
 	struct cdr_object *cdr;
 	struct party_b_userfield_update party_b_info = {
-			.channel_name = channel_name,
-			.userfield = userfield,
+		.channel_name = channel_name,
+		.userfield = userfield,
 	};
 	struct cdr_object *it_cdr;
 
@@ -3303,9 +3432,9 @@
 	}
 
 	/* Handle Party B */
-	ao2_callback(active_cdrs_by_channel, OBJ_NODATA,
-			cdr_object_update_party_b_userfield_cb,
-			&party_b_info);
+	ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
+		cdr_object_update_party_b_userfield_cb, (char *) party_b_info.channel_name,
+		&party_b_info);
 
 	ao2_cleanup(cdr);
 }
@@ -3485,6 +3614,7 @@
 		if (cdr_obj->party_b.snapshot) {
 			new_cdr->party_b.snapshot = cdr_obj->party_b.snapshot;
 			ao2_ref(new_cdr->party_b.snapshot, +1);
+			cdr_all_relink(new_cdr);
 			strcpy(new_cdr->party_b.userfield, cdr_obj->party_b.userfield);
 			new_cdr->party_b.flags = cdr_obj->party_b.flags;
 			if (ast_test_flag(options, AST_CDR_FLAG_KEEP_VARS)) {
@@ -4074,7 +4204,9 @@
 	cdr_object_dispatch(cdr);
 	ao2_unlock(cdr);
 
-	return 0;
+	cdr_all_unlink(cdr);
+
+	return CMP_MATCH;
 }
 
 static void finalize_batch_mode(void)
@@ -4200,8 +4332,8 @@
 
 	STASIS_MESSAGE_TYPE_CLEANUP(cdr_sync_message_type);
 
-	ao2_callback(active_cdrs_by_channel, OBJ_NODATA, cdr_object_dispatch_all_cb,
-		NULL);
+	ao2_callback(active_cdrs_by_channel, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
+		cdr_object_dispatch_all_cb, NULL);
 	finalize_batch_mode();
 	ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
 	ast_sched_context_destroy(sched);
@@ -4213,8 +4345,12 @@
 	ao2_global_obj_release(module_configs);
 
 	ao2_container_unregister("cdrs_by_channel");
-	ao2_ref(active_cdrs_by_channel, -1);
+	ao2_cleanup(active_cdrs_by_channel);
 	active_cdrs_by_channel = NULL;
+
+	ao2_container_unregister("cdrs_all");
+	ao2_cleanup(active_cdrs_all);
+	active_cdrs_all = NULL;
 }
 
 static void cdr_enable_batch_mode(struct ast_cdr_config *config)
@@ -4259,6 +4395,30 @@
 		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>",
 				it_cdr->bridge);
 	}
+}
+
+/*!
+ * \internal
+ * \brief Print all CDR container object.
+ * \since 13.19.0
+ *
+ * \param v_obj A pointer to the object we want printed.
+ * \param where User data needed by prnt to determine where to put output.
+ * \param prnt Print output callback function to use.
+ *
+ * \return Nothing
+ */
+static void cdr_all_print_fn(void *v_obj, void *where, ao2_prnt_fn *prnt)
+{
+	struct cdr_object *cdr = v_obj;
+
+	if (!cdr) {
+		return;
+	}
+	prnt(where, "Party A: %s; Party B: %s; Bridge %s",
+		cdr->party_a.snapshot->name,
+		cdr->party_b.snapshot ? cdr->party_b.snapshot->name : "<unknown>",
+		cdr->bridge);
 }
 
 /*!
@@ -4327,6 +4487,13 @@
 	}
 	ao2_container_register("cdrs_by_channel", active_cdrs_by_channel, cdr_container_print_fn);
 
+	active_cdrs_all = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
+		NUM_CDR_BUCKETS, cdr_all_hash_fn, NULL, cdr_all_cmp_fn);
+	if (!active_cdrs_all) {
+		return -1;
+	}
+	ao2_container_register("cdrs_all", active_cdrs_all, cdr_all_print_fn);
+
 	sched = ast_sched_context_create();
 	if (!sched) {
 		ast_log(LOG_ERROR, "Unable to create schedule context.\n");

-- 
To view, visit https://gerrit.asterisk.org/6786
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: I0bf66e8868f8adaa4b5dcf9e682e34951c350249
Gerrit-Change-Number: 6786
Gerrit-PatchSet: 3
Gerrit-Owner: Richard Mudgett <rmudgett at digium.com>
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Jenkins2
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Kevin Harwell <kharwell at digium.com>
Gerrit-Reviewer: Richard Mudgett <rmudgett at digium.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-commits/attachments/20171017/5cbe46e3/attachment-0001.html>


More information about the asterisk-commits mailing list