[asterisk-commits] dlee: branch group/performance r399936 - in /team/group/performance: ./ bridg...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Sep 27 11:20:12 CDT 2013


Author: dlee
Date: Fri Sep 27 11:20:03 2013
New Revision: 399936

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=399936
Log:
Multiple revisions 399897,399912,399924

........
  r399897 | kharwell | 2013-09-26 13:48:25 -0500 (Thu, 26 Sep 2013) | 15 lines
  
  pjsip: race condition in registrar
  
  While handling a registration request a race condition could occur if/when two+
  clients registered at the same time.  This happened when one request obtained a
  copy of the current contacts for an AOR and another request did the same before
  the first request updated.  Thus the second would update and overwrite the first
  (or vice-versa depending on which actually updated first).  In the case of it
  being the same contact two "add" events would be raised.
  
  pjsip registration handling is now serialized to alleviate this issue.
  
  (closes issue AST-1213)
  Reported by: John Bigelow
  Review: https://reviewboard.asterisk.org/r/2860/
........
  r399912 | kmoore | 2013-09-27 09:01:01 -0500 (Fri, 27 Sep 2013) | 10 lines
  
  Restore usefulness of the CEL Peer field
  
  This change makes the CEL peer field useful again for BRIDGE_ENTER and
  BRIDGE_EXIT events and fills the field with a comma-separated list of
  all channels in the bridge other than the channel that is entering or
  exiting the bridge.
  
  Review: https://reviewboard.asterisk.org/r/2840/
  (closes issue ASTERISK-22393)
........
  r399924 | mmichelson | 2013-09-27 09:29:12 -0500 (Fri, 27 Sep 2013) | 8 lines
  
  Fix refleaks of ast_rtp_instance structures.
  
  These refleaks were causing bridged calls not to close their RTP ports. Thus
  a call would leave open 4 ports (RTP for party A, RTCP for party A, RTP for party
  B, and RTCP for party B). This led to an eventual depletion of available RTP
  ports.
........

Merged revisions 399897,399912,399924 from http://svn.asterisk.org/svn/asterisk/branches/12

Modified:
    team/group/performance/   (props changed)
    team/group/performance/bridges/bridge_native_rtp.c
    team/group/performance/include/asterisk/cel.h
    team/group/performance/include/asterisk/res_pjsip.h
    team/group/performance/main/cel.c
    team/group/performance/res/res_pjsip.exports.in
    team/group/performance/res/res_pjsip/security_events.c
    team/group/performance/res/res_pjsip_registrar.c
    team/group/performance/tests/test_cel.c

Propchange: team/group/performance/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Fri Sep 27 11:20:03 2013
@@ -1,1 +1,1 @@
-/branches/12:1-399889 /team/dlee/performance:1-399887 /team/dlee/stasis-forward-optimization:1-399888 /team/dlee/taskprocessor-optimization:1-399654 /team/dlee/tp-local:1-399890
+/branches/12:1-399935 /team/dlee/performance:1-399887 /team/dlee/stasis-forward-optimization:1-399888 /team/dlee/taskprocessor-optimization:1-399654 /team/dlee/tp-local:1-399890

Modified: team/group/performance/bridges/bridge_native_rtp.c
URL: http://svnview.digium.com/svn/asterisk/team/group/performance/bridges/bridge_native_rtp.c?view=diff&rev=399936&r1=399935&r2=399936
==============================================================================
--- team/group/performance/bridges/bridge_native_rtp.c (original)
+++ team/group/performance/bridges/bridge_native_rtp.c Fri Sep 27 11:20:03 2013
@@ -118,8 +118,12 @@
 	struct ast_bridge_channel *c1 = AST_LIST_LAST(&bridge->channels);
 	enum ast_rtp_glue_result native_type;
 	struct ast_rtp_glue *glue0, *glue1;
-	struct ast_rtp_instance *instance0 = NULL, *instance1 = NULL, *vinstance0 = NULL;
-	struct ast_rtp_instance *vinstance1 = NULL, *tinstance0 = NULL, *tinstance1 = NULL;
+	RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_rtp_instance *, tinstance0, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_rtp_instance *, tinstance1, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_format_cap *, cap0, ast_format_cap_alloc_nolock(), ast_format_cap_destroy);
 	RAII_VAR(struct ast_format_cap *, cap1, ast_format_cap_alloc_nolock(), ast_format_cap_destroy);
 
@@ -185,7 +189,10 @@
 	struct ast_bridge_channel *c1 = AST_LIST_LAST(&bridge->channels);
 	enum ast_rtp_glue_result native_type;
 	struct ast_rtp_glue *glue0, *glue1 = NULL;
-	struct ast_rtp_instance *instance0 = NULL, *instance1 = NULL, *vinstance0 = NULL, *vinstance1 = NULL;
+	RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup);
 
 	if (c0 == c1) {
 		return;

Modified: team/group/performance/include/asterisk/cel.h
URL: http://svnview.digium.com/svn/asterisk/team/group/performance/include/asterisk/cel.h?view=diff&rev=399936&r1=399935&r2=399936
==============================================================================
--- team/group/performance/include/asterisk/cel.h (original)
+++ team/group/performance/include/asterisk/cel.h Fri Sep 27 11:20:03 2013
@@ -273,6 +273,7 @@
  * \param userdefevname Custom name for the call event. (optional)
  * \param extra An event-specific opaque JSON blob to be rendered and placed
  *        in the "CEL_EXTRA" information element of the call event. (optional)
+ * \param peer_str A list of comma-separated peer channel names. (optional)
  *
  * \since 12
  *
@@ -281,7 +282,7 @@
  */
 struct ast_event *ast_cel_create_event(struct ast_channel_snapshot *snapshot,
 		enum ast_cel_event_type event_type, const char *userdefevname,
-		struct ast_json *extra);
+		struct ast_json *extra, const char *peer_str);
 
 /*!
  * \brief CEL backend callback

Modified: team/group/performance/include/asterisk/res_pjsip.h
URL: http://svnview.digium.com/svn/asterisk/team/group/performance/include/asterisk/res_pjsip.h?view=diff&rev=399936&r1=399935&r2=399936
==============================================================================
--- team/group/performance/include/asterisk/res_pjsip.h (original)
+++ team/group/performance/include/asterisk/res_pjsip.h Fri Sep 27 11:20:03 2013
@@ -1478,6 +1478,24 @@
  */
 void ast_sip_report_auth_challenge_sent(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, pjsip_tx_data *tdata);
 
+/*!
+ * \brief Send a security event notification for when a request is not supported
+ *
+ * \param endpoint Pointer to the endpoint in use
+ * \param rdata Received message
+ * \param req_type the type of request
+ */
+void ast_sip_report_req_no_support(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata,
+				   const char* req_type);
+
+/*!
+ * \brief Send a security event notification for when a memory limit is hit.
+ *
+ * \param endpoint Pointer to the endpoint in use
+ * \param rdata Received message
+ */
+void ast_sip_report_mem_limit(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata);
+
 void ast_sip_initialize_global_headers(void);
 void ast_sip_destroy_global_headers(void);
 

Modified: team/group/performance/main/cel.c
URL: http://svnview.digium.com/svn/asterisk/team/group/performance/main/cel.c?view=diff&rev=399936&r1=399935&r2=399936
==============================================================================
--- team/group/performance/main/cel.c (original)
+++ team/group/performance/main/cel.c Fri Sep 27 11:20:03 2013
@@ -293,8 +293,8 @@
 	[AST_CEL_PARK_START]       = "PARK_START",
 	[AST_CEL_PARK_END]         = "PARK_END",
 	[AST_CEL_USER_DEFINED]     = "USER_DEFINED",
-	[AST_CEL_BRIDGE_ENTER]       = "BRIDGE_ENTER",
-	[AST_CEL_BRIDGE_EXIT]        = "BRIDGE_EXIT",
+	[AST_CEL_BRIDGE_ENTER]     = "BRIDGE_ENTER",
+	[AST_CEL_BRIDGE_EXIT]      = "BRIDGE_EXIT",
 	[AST_CEL_BLINDTRANSFER]    = "BLINDTRANSFER",
 	[AST_CEL_ATTENDEDTRANSFER] = "ATTENDEDTRANSFER",
 	[AST_CEL_PICKUP]           = "PICKUP",
@@ -574,7 +574,7 @@
 static int cel_linkedid_ref(const char *linkedid);
 struct ast_event *ast_cel_create_event(struct ast_channel_snapshot *snapshot,
 		enum ast_cel_event_type event_type, const char *userdefevname,
-		struct ast_json *extra)
+		struct ast_json *extra, const char *peer)
 {
 	struct timeval eventtime = ast_tvnow();
 	RAII_VAR(char *, extra_txt, NULL, ast_json_free);
@@ -603,7 +603,7 @@
 		AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, snapshot->linkedid,
 		AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, snapshot->userfield,
 		AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, S_OR(extra_txt, ""),
-		AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, "",
+		AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, S_OR(peer, ""),
 		AST_EVENT_IE_END);
 }
 
@@ -617,7 +617,7 @@
 
 static int cel_report_event(struct ast_channel_snapshot *snapshot,
 		enum ast_cel_event_type event_type, const char *userdefevname,
-		struct ast_json *extra)
+		struct ast_json *extra, const char *peer_str)
 {
 	struct ast_event *ev;
 	char *linkedid = ast_strdupa(snapshot->linkedid);
@@ -648,7 +648,7 @@
 		return 0;
 	}
 
-	ev = ast_cel_create_event(snapshot, event_type, userdefevname, extra);
+	ev = ast_cel_create_event(snapshot, event_type, userdefevname, extra, peer_str);
 	if (!ev) {
 		return -1;
 	}
@@ -681,7 +681,7 @@
 	 * before unreffing the channel we have a refcount of 3, we're done. Unlink and report. */
 	if (ao2_ref(lid, -1) == 3) {
 		ast_str_container_remove(linkedids, lid);
-		cel_report_event(snapshot, AST_CEL_LINKEDID_END, NULL, NULL);
+		cel_report_event(snapshot, AST_CEL_LINKEDID_END, NULL, NULL, NULL);
 	}
 	ao2_ref(lid, -1);
 }
@@ -917,13 +917,13 @@
 	int is_hungup, was_hungup;
 
 	if (!new_snapshot) {
-		cel_report_event(old_snapshot, AST_CEL_CHANNEL_END, NULL, NULL);
+		cel_report_event(old_snapshot, AST_CEL_CHANNEL_END, NULL, NULL, NULL);
 		check_retire_linkedid(old_snapshot);
 		return;
 	}
 
 	if (!old_snapshot) {
-		cel_report_event(new_snapshot, AST_CEL_CHANNEL_START, NULL, NULL);
+		cel_report_event(new_snapshot, AST_CEL_CHANNEL_START, NULL, NULL, NULL);
 		return;
 	}
 
@@ -941,12 +941,12 @@
 			"hangupcause", new_snapshot->hangupcause,
 			"hangupsource", new_snapshot->hangupsource,
 			"dialstatus", dialstatus);
-		cel_report_event(new_snapshot, AST_CEL_HANGUP, NULL, extra);
+		cel_report_event(new_snapshot, AST_CEL_HANGUP, NULL, extra, NULL);
 		return;
 	}
 
 	if (old_snapshot->state != new_snapshot->state && new_snapshot->state == AST_STATE_UP) {
-		cel_report_event(new_snapshot, AST_CEL_ANSWER, NULL, NULL);
+		cel_report_event(new_snapshot, AST_CEL_ANSWER, NULL, NULL, NULL);
 		return;
 	}
 }
@@ -979,12 +979,12 @@
 
 	/* old snapshot has an application, end it */
 	if (old_snapshot && !ast_strlen_zero(old_snapshot->appl)) {
-		cel_report_event(old_snapshot, AST_CEL_APP_END, NULL, NULL);
+		cel_report_event(old_snapshot, AST_CEL_APP_END, NULL, NULL, NULL);
 	}
 
 	/* new snapshot has an application, start it */
 	if (new_snapshot && !ast_strlen_zero(new_snapshot->appl)) {
-		cel_report_event(new_snapshot, AST_CEL_APP_START, NULL, NULL);
+		cel_report_event(new_snapshot, AST_CEL_APP_START, NULL, NULL, NULL);
 	}
 }
 
@@ -1029,6 +1029,45 @@
 	}
 }
 
+static struct ast_str *cel_generate_peer_str(
+	struct ast_bridge_snapshot *bridge,
+	struct ast_channel_snapshot *chan)
+{
+	struct ast_str *peer_str = ast_str_create(32);
+	struct ao2_iterator i;
+	char *current_chan = NULL;
+
+	if (!peer_str) {
+		return NULL;
+	}
+
+	for (i = ao2_iterator_init(bridge->channels, 0);
+		(current_chan = ao2_iterator_next(&i));
+		ao2_cleanup(current_chan)) {
+		RAII_VAR(struct ast_channel_snapshot *, current_snapshot,
+			NULL,
+			ao2_cleanup);
+
+		/* Don't add the channel for which this message is being generated */
+		if (!strcmp(current_chan, chan->uniqueid)) {
+			continue;
+		}
+
+		current_snapshot = ast_channel_snapshot_get_latest(current_chan);
+		if (!current_snapshot) {
+			continue;
+		}
+
+		ast_str_append(&peer_str, 0, "%s,", current_snapshot->name);
+	}
+	ao2_iterator_destroy(&i);
+
+	/* Rip off the trailing comma */
+	ast_str_truncate(peer_str, -1);
+
+	return peer_str;
+}
+
 static void cel_bridge_enter_cb(
 	void *data, struct stasis_subscription *sub,
 	struct stasis_message *message)
@@ -1037,6 +1076,7 @@
 	struct ast_bridge_snapshot *snapshot = blob->bridge;
 	struct ast_channel_snapshot *chan_snapshot = blob->channel;
 	RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
+	RAII_VAR(struct ast_str *, peer_str, NULL, ast_free);
 
 	if (cel_filter_channel_snapshot(chan_snapshot)) {
 		return;
@@ -1047,7 +1087,12 @@
 		return;
 	}
 
-	cel_report_event(chan_snapshot, AST_CEL_BRIDGE_ENTER, NULL, extra);
+	peer_str = cel_generate_peer_str(snapshot, chan_snapshot);
+	if (!peer_str) {
+		return;
+	}
+
+	cel_report_event(chan_snapshot, AST_CEL_BRIDGE_ENTER, NULL, extra, ast_str_buffer(peer_str));
 }
 
 static void cel_bridge_leave_cb(
@@ -1058,6 +1103,7 @@
 	struct ast_bridge_snapshot *snapshot = blob->bridge;
 	struct ast_channel_snapshot *chan_snapshot = blob->channel;
 	RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
+	RAII_VAR(struct ast_str *, peer_str, NULL, ast_free);
 
 	if (cel_filter_channel_snapshot(chan_snapshot)) {
 		return;
@@ -1068,7 +1114,12 @@
 		return;
 	}
 
-	cel_report_event(chan_snapshot, AST_CEL_BRIDGE_EXIT, NULL, extra);
+	peer_str = cel_generate_peer_str(snapshot, chan_snapshot);
+	if (!peer_str) {
+		return;
+	}
+
+	cel_report_event(chan_snapshot, AST_CEL_BRIDGE_EXIT, NULL, extra, ast_str_buffer(peer_str));
 }
 
 static void cel_parking_cb(
@@ -1085,7 +1136,7 @@
 			"parker_dial_string", parked_payload->parker_dial_string,
 			"parking_lot", parked_payload->parkinglot);
 		if (extra) {
-			cel_report_event(parked_payload->parkee, AST_CEL_PARK_START, NULL, extra);
+			cel_report_event(parked_payload->parkee, AST_CEL_PARK_START, NULL, extra, NULL);
 		}
 		return;
 	case PARKED_CALL_TIMEOUT:
@@ -1107,7 +1158,7 @@
 
 	extra = ast_json_pack("{s: s}", "reason", reason);
 	if (extra) {
-		cel_report_event(parked_payload->parkee, AST_CEL_PARK_END, NULL, extra);
+		cel_report_event(parked_payload->parkee, AST_CEL_PARK_END, NULL, extra, NULL);
 	}
 }
 
@@ -1138,7 +1189,7 @@
 
 		extra = ast_json_pack("{s: s}", "forward", get_blob_variable(blob, "forward"));
 		if (extra) {
-			cel_report_event(caller, AST_CEL_FORWARD, NULL, extra);
+			cel_report_event(caller, AST_CEL_FORWARD, NULL, extra, NULL);
 		}
 	}
 
@@ -1162,7 +1213,7 @@
 		{
 			const char *event = ast_json_string_get(ast_json_object_get(event_details, "event"));
 			struct ast_json *extra = ast_json_object_get(event_details, "extra");
-			cel_report_event(obj->snapshot, event_type, event, extra);
+			cel_report_event(obj->snapshot, event_type, event, extra, NULL);
 			break;
 		}
 	default:
@@ -1214,7 +1265,7 @@
 		"bridge_id", bridge_snapshot->uniqueid);
 
 	if (extra) {
-		cel_report_event(chan_snapshot, AST_CEL_BLINDTRANSFER, NULL, extra);
+		cel_report_event(chan_snapshot, AST_CEL_BLINDTRANSFER, NULL, extra, NULL);
 	}
 }
 
@@ -1267,7 +1318,7 @@
 		}
 		break;
 	}
-	cel_report_event(channel1, AST_CEL_ATTENDEDTRANSFER, NULL, extra);
+	cel_report_event(channel1, AST_CEL_ATTENDEDTRANSFER, NULL, extra, NULL);
 }
 
 static void cel_pickup_cb(
@@ -1288,7 +1339,7 @@
 		return;
 	}
 
-	cel_report_event(target, AST_CEL_PICKUP, NULL, extra);
+	cel_report_event(target, AST_CEL_PICKUP, NULL, extra, NULL);
 }
 
 static void cel_local_cb(
@@ -1309,7 +1360,7 @@
 		return;
 	}
 
-	cel_report_event(localone, AST_CEL_LOCAL_OPTIMIZE, NULL, extra);
+	cel_report_event(localone, AST_CEL_LOCAL_OPTIMIZE, NULL, extra, NULL);
 }
 
 static void destroy_subscriptions(void)

Modified: team/group/performance/res/res_pjsip.exports.in
URL: http://svnview.digium.com/svn/asterisk/team/group/performance/res/res_pjsip.exports.in?view=diff&rev=399936&r1=399935&r2=399936
==============================================================================
--- team/group/performance/res/res_pjsip.exports.in (original)
+++ team/group/performance/res/res_pjsip.exports.in Fri Sep 27 11:20:03 2013
@@ -61,6 +61,8 @@
 		LINKER_SYMBOL_PREFIXast_sip_report_auth_failed_challenge_response;
 		LINKER_SYMBOL_PREFIXast_sip_report_auth_success;
 		LINKER_SYMBOL_PREFIXast_sip_report_auth_challenge_sent;
+		LINKER_SYMBOL_PREFIXast_sip_report_req_no_support;
+		LINKER_SYMBOL_PREFIXast_sip_report_mem_limit;
 		LINKER_SYMBOL_PREFIXast_sip_initialize_global_headers;
 		LINKER_SYMBOL_PREFIXast_sip_destroy_global_headers;
 		LINKER_SYMBOL_PREFIXast_sip_add_global_request_header;

Modified: team/group/performance/res/res_pjsip/security_events.c
URL: http://svnview.digium.com/svn/asterisk/team/group/performance/res/res_pjsip/security_events.c?view=diff&rev=399936&r1=399935&r2=399936
==============================================================================
--- team/group/performance/res/res_pjsip/security_events.c (original)
+++ team/group/performance/res/res_pjsip/security_events.c Fri Sep 27 11:20:03 2013
@@ -232,3 +232,59 @@
 
 	ast_security_event_report(AST_SEC_EVT(&chal_sent));
 }
+
+void ast_sip_report_req_no_support(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata,
+				   const char* req_type)
+{
+	enum ast_transport transport = security_event_get_transport(rdata);
+	char call_id[pj_strlen(&rdata->msg_info.cid->id) + 1];
+	struct ast_sockaddr local, remote;
+
+	struct ast_security_event_req_no_support req_no_support_event = {
+		.common.event_type  = AST_SECURITY_EVENT_REQ_NO_SUPPORT,
+		.common.version     = AST_SECURITY_EVENT_REQ_NO_SUPPORT_VERSION,
+		.common.service     = "PJSIP",
+		.common.account_id  = ast_sorcery_object_get_id(endpoint),
+		.common.local_addr  = {
+			.addr       = &local,
+			.transport  = transport,
+		},
+		.common.remote_addr = {
+			.addr       = &remote,
+			.transport  = transport,
+		},
+		.common.session_id  = call_id,
+		.request_type       = req_type
+	};
+
+	security_event_populate(rdata, call_id, sizeof(call_id), &local, &remote);
+
+	ast_security_event_report(AST_SEC_EVT(&req_no_support_event));
+}
+
+void ast_sip_report_mem_limit(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
+{
+	enum ast_transport transport = security_event_get_transport(rdata);
+	char call_id[pj_strlen(&rdata->msg_info.cid->id) + 1];
+	struct ast_sockaddr local, remote;
+
+	struct ast_security_event_mem_limit mem_limit_event = {
+		.common.event_type  = AST_SECURITY_EVENT_MEM_LIMIT,
+		.common.version     = AST_SECURITY_EVENT_MEM_LIMIT_VERSION,
+		.common.service     = "PJSIP",
+		.common.account_id  = ast_sorcery_object_get_id(endpoint),
+		.common.local_addr  = {
+			.addr       = &local,
+			.transport  = transport,
+		},
+		.common.remote_addr = {
+			.addr       = &remote,
+			.transport  = transport,
+		},
+		.common.session_id  = call_id
+	};
+
+	security_event_populate(rdata, call_id, sizeof(call_id), &local, &remote);
+
+	ast_security_event_report(AST_SEC_EVT(&mem_limit_event));
+}

Modified: team/group/performance/res/res_pjsip_registrar.c
URL: http://svnview.digium.com/svn/asterisk/team/group/performance/res/res_pjsip_registrar.c?view=diff&rev=399936&r1=399935&r2=399936
==============================================================================
--- team/group/performance/res/res_pjsip_registrar.c (original)
+++ team/group/performance/res/res_pjsip_registrar.c Fri Sep 27 11:20:03 2013
@@ -30,6 +30,7 @@
 #include "asterisk/res_pjsip.h"
 #include "asterisk/module.h"
 #include "asterisk/test.h"
+#include "asterisk/taskprocessor.h"
 
 /*! \brief Internal function which returns the expiration time for a contact */
 static int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_contact_hdr *contact, const pjsip_rx_data *rdata)
@@ -188,122 +189,195 @@
 	ast_sip_add_header(tdata, "Date", date);
 }
 
-static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
-{
-	struct ast_sip_endpoint *endpoint = ast_pjsip_rdata_get_endpoint(rdata);
-	pjsip_sip_uri *uri;
-	char user_name[64], domain_name[64];
-	char *configured_aors, *aor_name;
-	RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
+#define SERIALIZER_BUCKETS 59
+
+static struct ao2_container *serializers;
+
+/*! \brief Serializer with associated aor key */
+struct serializer {
+	/* Serializer to distribute tasks to */
+	struct ast_taskprocessor *serializer;
+	/* The name of the aor to associate with the serializer */
+	char aor_name[0];
+};
+
+static void serializer_destroy(void *obj)
+{
+	struct serializer *ser = obj;
+
+	ast_taskprocessor_unreference(ser->serializer);
+}
+
+static struct serializer *serializer_create(const char *aor_name)
+{
+	size_t size = strlen(aor_name) + 1;
+	struct serializer *ser = ao2_alloc(
+		sizeof(*ser) + size, serializer_destroy);
+
+	if (!ser) {
+		return NULL;
+	}
+
+	if (!(ser->serializer = ast_sip_create_serializer())) {
+		ao2_ref(ser, -1);
+		return NULL;
+	}
+
+	strcpy(ser->aor_name, aor_name);
+	return ser;
+}
+
+static struct serializer *serializer_find_or_create(const char *aor_name)
+{
+	struct serializer *ser = ao2_find(serializers, aor_name, OBJ_SEARCH_KEY);
+
+	if (ser) {
+		return ser;
+	}
+
+	if (!(ser = serializer_create(aor_name))) {
+		return NULL;
+	}
+
+	ao2_link(serializers, ser);
+	return ser;
+}
+
+static int serializer_hash(const void *obj, const int flags)
+{
+	const struct serializer *object;
+	const char *key;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_KEY:
+		key = obj;
+		return ast_str_hash(key);
+	case OBJ_SEARCH_OBJECT:
+		object = obj;
+		return ast_str_hash(object->aor_name);
+	default:
+		/* Hash can only work on something with a full key. */
+		ast_assert(0);
+		return 0;
+	}
+}
+
+static int serializer_cmp(void *obj_left, void *obj_right, int flags)
+{
+	const struct serializer *object_left = obj_left;
+	const struct serializer *object_right = obj_right;
+	const char *right_key = obj_right;
+	int cmp;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_OBJECT:
+		right_key = object_right->aor_name;
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		cmp = strcmp(object_left->aor_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 = strncmp(object_left->aor_name, right_key, strlen(right_key));
+		break;
+	default:
+		cmp = 0;
+		break;
+	}
+
+	return cmp ? 0 : CMP_MATCH;
+}
+
+struct rx_task_data {
+	pjsip_rx_data *rdata;
+	struct ast_sip_endpoint *endpoint;
+	struct ast_sip_aor *aor;
+};
+
+static void rx_task_data_destroy(void *obj)
+{
+	struct rx_task_data *task_data = obj;
+
+	pjsip_rx_data_free_cloned(task_data->rdata);
+	ao2_cleanup(task_data->endpoint);
+	ao2_cleanup(task_data->aor);
+}
+
+static struct rx_task_data *rx_task_data_create(pjsip_rx_data *rdata,
+						struct ast_sip_endpoint *endpoint,
+						struct ast_sip_aor *aor)
+{
+	struct rx_task_data *task_data = ao2_alloc(
+		sizeof(*task_data), rx_task_data_destroy);
+
+	if (!task_data) {
+		return NULL;
+	}
+
+	pjsip_rx_data_clone(rdata, 0, &task_data->rdata);
+
+	task_data->endpoint = endpoint;
+	ao2_ref(task_data->endpoint, +1);
+
+	task_data->aor = aor;
+	ao2_ref(task_data->aor, +1);
+
+	return task_data;
+}
+
+static int rx_task(void *data)
+{
+	RAII_VAR(struct rx_task_data *, task_data, data, ao2_cleanup);
 	RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
+
 	int added = 0, updated = 0, deleted = 0;
 	pjsip_contact_hdr *contact_hdr = NULL;
 	struct registrar_contact_details details = { 0, };
 	pjsip_tx_data *tdata;
 	pjsip_response_addr addr;
-
-	if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) {
-		return PJ_FALSE;
-	}
-
-	if (ast_strlen_zero(endpoint->aors)) {
-		/* Short circuit early if the endpoint has no AORs configured on it, which means no registration possible */
-		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
-		ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_configured_aors");
-		ast_log(LOG_WARNING, "Endpoint '%s' has no configured AORs\n", ast_sorcery_object_get_id(endpoint));
-		return PJ_TRUE;
-	}
-
-	if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) {
-		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
-		ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_uri_in_to_received");
-		ast_log(LOG_WARNING, "Endpoint '%s' attempted to register to an AOR with a non-SIP URI\n", ast_sorcery_object_get_id(endpoint));
-		return PJ_TRUE;
-	}
-
-	uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);
-	ast_copy_pj_str(user_name, &uri->user, sizeof(user_name));
-	ast_copy_pj_str(domain_name, &uri->host, sizeof(domain_name));
-
-	configured_aors = ast_strdupa(endpoint->aors);
-
-	/* Iterate the configured AORs to see if the user or the user+domain match */
-	while ((aor_name = strsep(&configured_aors, ","))) {
-		char id[AST_UUID_STR_LEN];
-		RAII_VAR(struct ast_sip_domain_alias *, alias, NULL, ao2_cleanup);
-
-		snprintf(id, sizeof(id), "%s@%s", user_name, domain_name);
-		if (!strcmp(aor_name, id)) {
-			break;
-		}
-
-		if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) {
-			snprintf(id, sizeof(id), "%s@%s", user_name, alias->domain);
-			if (!strcmp(aor_name, id)) {
-				break;
-			}
-		}
-
-		if (!strcmp(aor_name, user_name)) {
-			break;
-		}
-	}
-
-	if (ast_strlen_zero(aor_name) || !(aor = ast_sip_location_retrieve_aor(aor_name))) {
-		/* The provided AOR name was not found (be it within the configuration or sorcery itself) */
-		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
-		ast_sip_report_failed_acl(endpoint, rdata, "registrar_requested_aor_not_found");
-		ast_log(LOG_WARNING, "AOR '%s' not found for endpoint '%s'\n", user_name, ast_sorcery_object_get_id(endpoint));
-		return PJ_TRUE;
-	}
-
-	if (!aor->max_contacts) {
-		/* Registration is not permitted for this AOR */
-		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
-		ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_registration_permitted");
-		ast_log(LOG_WARNING, "AOR '%s' has no configured max_contacts. Endpoint '%s' unable to register\n",
-				ast_sorcery_object_get_id(aor), ast_sorcery_object_get_id(endpoint));
-		return PJ_TRUE;
-	}
+	const char *aor_name = ast_sorcery_object_get_id(task_data->aor);
 
 	/* Retrieve the current contacts, we'll need to know whether to update or not */
-	contacts = ast_sip_location_retrieve_aor_contacts(aor);
+	contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor);
 
 	/* So we don't count static contacts against max_contacts we prune them out from the container */
 	ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL);
 
-	if (registrar_validate_contacts(rdata, contacts, aor, &added, &updated, &deleted)) {
+	if (registrar_validate_contacts(task_data->rdata, contacts, task_data->aor, &added, &updated, &deleted)) {
 		/* The provided Contact headers do not conform to the specification */
-		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL);
-		ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided");
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 400, NULL, NULL, NULL);
+		ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_invalid_contacts_provided");
 		ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n",
-				ast_sorcery_object_get_id(endpoint));
-		return PJ_TRUE;
-	}
-
-	if ((MAX(added - deleted, 0) + (!aor->remove_existing ? ao2_container_count(contacts) : 0)) > aor->max_contacts) {
+				ast_sorcery_object_get_id(task_data->endpoint));
+		return PJ_TRUE;
+	}
+
+	if ((MAX(added - deleted, 0) + (!task_data->aor->remove_existing ? ao2_container_count(contacts) : 0)) > task_data->aor->max_contacts) {
 		/* Enforce the maximum number of contacts */
-		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
-		ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts");
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 403, NULL, NULL, NULL);
+		ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_attempt_exceeds_maximum_configured_contacts");
 		ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %d\n",
-				ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor), aor->max_contacts);
+				ast_sorcery_object_get_id(task_data->endpoint), ast_sorcery_object_get_id(task_data->aor), task_data->aor->max_contacts);
 		return PJ_TRUE;
 	}
 
 	if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) {
-		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 500, NULL, NULL, NULL);
 		return PJ_TRUE;
 	}
 
 	/* Iterate each provided Contact header and add, update, or delete */
-	while ((contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) {
+	while ((contact_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) {
 		int expiration;
 		char contact_uri[PJSIP_MAX_URL_SIZE];
 		RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
 
 		if (contact_hdr->star) {
 			/* A star means to unregister everything, so do so for the possible contacts */
-			ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, aor_name);
+			ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name);
 			break;
 		}
 
@@ -312,7 +386,7 @@
 			continue;
 		}
 
-		expiration = registrar_get_expiration(aor, contact_hdr, rdata);
+		expiration = registrar_get_expiration(task_data->aor, contact_hdr, task_data->rdata);
 		details.uri = pjsip_uri_get_uri(contact_hdr->uri);
 		pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));
 
@@ -324,7 +398,7 @@
 				continue;
 			}
 
-			ast_sip_location_add_contact(aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)));
+			ast_sip_location_add_contact(task_data->aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)));
 			ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n",
 				contact_uri, aor_name, expiration);
 			ast_test_suite_event_notify("AOR_CONTACT_ADDED",
@@ -337,8 +411,8 @@
 		} else if (expiration) {
 			RAII_VAR(struct ast_sip_contact *, updated, ast_sorcery_copy(ast_sip_get_sorcery(), contact), ao2_cleanup);
 			updated->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1));
-			updated->qualify_frequency = aor->qualify_frequency;
-			updated->authenticate_qualify = aor->authenticate_qualify;
+			updated->qualify_frequency = task_data->aor->qualify_frequency;
+			updated->authenticate_qualify = task_data->aor->authenticate_qualify;
 
 			ast_sip_location_update_contact(updated);
 			ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n",
@@ -366,16 +440,16 @@
 	/* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER
 	 * do so
 	 */
-	if (aor->remove_existing) {
+	if (task_data->aor->remove_existing) {
 		ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL);
 	}
 
 	/* Update the contacts as things will probably have changed */
 	ao2_cleanup(contacts);
-	contacts = ast_sip_location_retrieve_aor_contacts(aor);
+	contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor);
 
 	/* Send a response containing all of the contacts (including static) that are present on this AOR */
-	if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 200, NULL, &tdata) != PJ_SUCCESS) {
+	if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), task_data->rdata, 200, NULL, &tdata) != PJ_SUCCESS) {
 		return PJ_TRUE;
 	}
 
@@ -384,12 +458,114 @@
 
 	ao2_callback(contacts, 0, registrar_add_contact, tdata);
 
-	if (pjsip_get_response_addr(tdata->pool, rdata, &addr) == PJ_SUCCESS) {
+	if (pjsip_get_response_addr(tdata->pool, task_data->rdata, &addr) == PJ_SUCCESS) {
 		pjsip_endpt_send_response(ast_sip_get_pjsip_endpoint(), &addr, tdata, NULL, NULL);
 	} else {
 		pjsip_tx_data_dec_ref(tdata);
 	}
 
+	return PJ_TRUE;
+}
+
+static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
+{
+	RAII_VAR(struct serializer *, ser, NULL, ao2_cleanup);
+	struct rx_task_data *task_data;
+
+	RAII_VAR(struct ast_sip_endpoint *, endpoint,
+		 ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
+	RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
+	pjsip_sip_uri *uri;
+	char user_name[64], domain_name[64];
+	char *configured_aors, *aor_name;
+
+	if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) {
+		return PJ_FALSE;
+	}
+
+	if (ast_strlen_zero(endpoint->aors)) {
+		/* Short circuit early if the endpoint has no AORs configured on it, which means no registration possible */
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
+		ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_configured_aors");
+		ast_log(LOG_WARNING, "Endpoint '%s' has no configured AORs\n", ast_sorcery_object_get_id(endpoint));
+		return PJ_TRUE;
+	}
+
+	if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) {
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
+		ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_uri_in_to_received");
+		ast_log(LOG_WARNING, "Endpoint '%s' attempted to register to an AOR with a non-SIP URI\n", ast_sorcery_object_get_id(endpoint));
+		return PJ_TRUE;
+	}
+
+	uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);
+	ast_copy_pj_str(user_name, &uri->user, sizeof(user_name));
+	ast_copy_pj_str(domain_name, &uri->host, sizeof(domain_name));
+
+	configured_aors = ast_strdupa(endpoint->aors);
+
+	/* Iterate the configured AORs to see if the user or the user+domain match */
+	while ((aor_name = strsep(&configured_aors, ","))) {
+		char id[AST_UUID_STR_LEN];
+		RAII_VAR(struct ast_sip_domain_alias *, alias, NULL, ao2_cleanup);
+
+		snprintf(id, sizeof(id), "%s@%s", user_name, domain_name);
+		if (!strcmp(aor_name, id)) {
+			break;
+		}
+
+		if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) {
+			snprintf(id, sizeof(id), "%s@%s", user_name, alias->domain);
+			if (!strcmp(aor_name, id)) {
+				break;
+			}
+		}
+
+		if (!strcmp(aor_name, user_name)) {
+			break;
+		}
+	}
+
+	if (ast_strlen_zero(aor_name) || !(aor = ast_sip_location_retrieve_aor(aor_name))) {
+		/* The provided AOR name was not found (be it within the configuration or sorcery itself) */
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
+		ast_sip_report_req_no_support(endpoint, rdata, "registrar_requested_aor_not_found");
+		ast_log(LOG_WARNING, "AOR '%s' not found for endpoint '%s'\n", user_name, ast_sorcery_object_get_id(endpoint));
+		return PJ_TRUE;
+	}
+
+	if (!aor->max_contacts) {
+		/* Registration is not permitted for this AOR */
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
+		ast_sip_report_req_no_support(endpoint, rdata, "registrar_attempt_without_registration_permitted");
+		ast_log(LOG_WARNING, "AOR '%s' has no configured max_contacts. Endpoint '%s' unable to register\n",
+				ast_sorcery_object_get_id(aor), ast_sorcery_object_get_id(endpoint));
+		return PJ_TRUE;
+	}
+
+	if (!(ser = serializer_find_or_create(aor_name))) {
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
+		ast_sip_report_mem_limit(endpoint, rdata);
+		ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not get serializer\n",
+			ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor));
+		return PJ_TRUE;
+	}
+
+	if (!(task_data = rx_task_data_create(rdata, endpoint, aor))) {
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
+		ast_sip_report_mem_limit(endpoint, rdata);
+		ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not create rx_task_data\n",
+			ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor));
+		return PJ_TRUE;
+	}
+
+	if (ast_sip_push_task(ser->serializer, rx_task, task_data)) {
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
+		ast_sip_report_mem_limit(endpoint, rdata);
+		ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not serialize task\n",
+			ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor));
+		ao2_ref(task_data, -1);
+	}
 	return PJ_TRUE;
 }
 
@@ -404,6 +580,11 @@
 {
 	const pj_str_t STR_REGISTER = { "REGISTER", 8 };
 
+	if (!(serializers = ao2_container_alloc(
+		      SERIALIZER_BUCKETS, serializer_hash, serializer_cmp))) {
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
 	if (ast_sip_register_service(&registrar_module)) {
 		return AST_MODULE_LOAD_DECLINE;
 	}
@@ -419,6 +600,8 @@
 static int unload_module(void)
 {
 	ast_sip_unregister_service(&registrar_module);
+
+	ao2_cleanup(serializers);
 	return 0;
 }
 

Modified: team/group/performance/tests/test_cel.c
URL: http://svnview.digium.com/svn/asterisk/team/group/performance/tests/test_cel.c?view=diff&rev=399936&r1=399935&r2=399936
==============================================================================
--- team/group/performance/tests/test_cel.c (original)
+++ team/group/performance/tests/test_cel.c Fri Sep 27 11:20:03 2013
@@ -91,13 +91,19 @@
 }
 
 #define APPEND_EVENT(chan, ev_type, userevent, extra) do { \
-	if (append_expected_event(chan, ev_type, userevent, extra)) { \
+	if (append_expected_event(chan, ev_type, userevent, extra, NULL)) { \
 		return AST_TEST_FAIL; \
 	} \
 	} while (0)
 
-#define APPEND_EVENT_SNAPSHOT(snapshot, ev_type, userevent, extra) do { \
-	if (append_expected_event_snapshot(snapshot, ev_type, userevent, extra)) { \
+#define APPEND_EVENT_PEER(chan, ev_type, userevent, extra, peer) do { \
+	if (append_expected_event(chan, ev_type, userevent, extra, peer)) { \
+		return AST_TEST_FAIL; \
+	} \
+	} while (0)
+
+#define APPEND_EVENT_SNAPSHOT(snapshot, ev_type, userevent, extra, peer) do { \
+	if (append_expected_event_snapshot(snapshot, ev_type, userevent, extra, peer)) { \
 		return AST_TEST_FAIL; \
 	} \
 	} while (0)
@@ -115,17 +121,31 @@
 	} while (0)
 
 #define BRIDGE_EXIT_EVENT(channel, bridge) do { \
+	RAII_VAR(struct ast_str *, peer_str, NULL, ast_free); \
+	stasis_topic_wait(ast_channel_topic_all()); \
+	stasis_topic_wait(ast_bridge_topic_all()); \
+	peer_str = test_cel_generate_peer_str(channel, bridge); \
+	ast_test_validate(test, peer_str != NULL); \
+	BRIDGE_EXIT_EVENT_PEER(channel, bridge, ast_str_buffer(peer_str)); \
+	} while (0)
+
+#define BRIDGE_EXIT_EVENT_PEER(channel, bridge, peer) do { \
 	RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref); \
 	extra = ast_json_pack("{s: s}", "bridge_id", bridge->uniqueid); \
 	ast_test_validate(test, extra != NULL); \
-	APPEND_EVENT(channel, AST_CEL_BRIDGE_EXIT, NULL, extra); \
+	APPEND_EVENT_PEER(channel, AST_CEL_BRIDGE_EXIT, NULL, extra, peer); \
 	} while (0)
 

[... 314 lines stripped ...]



More information about the asterisk-commits mailing list