[asterisk-commits] mmichelson: branch 12 r427848 - in /branches/12: apps/ include/asterisk/ main...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Nov 14 09:00:49 CST 2014


Author: mmichelson
Date: Fri Nov 14 09:00:45 2014
New Revision: 427848

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=427848
Log:
Fix race condition that could result in ARI transfer messages not being sent.

>From reviewboard:

"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?

The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."

The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.

Review: https://reviewboard.asterisk.org/r/4135


Modified:
    branches/12/apps/app_queue.c
    branches/12/include/asterisk/stasis_bridges.h
    branches/12/main/bridge.c
    branches/12/main/bridge_basic.c
    branches/12/main/cel.c
    branches/12/main/stasis_bridges.c
    branches/12/res/stasis/app.c

Modified: branches/12/apps/app_queue.c
URL: http://svnview.digium.com/svn/asterisk/branches/12/apps/app_queue.c?view=diff&rev=427848&r1=427847&r2=427848
==============================================================================
--- branches/12/apps/app_queue.c (original)
+++ branches/12/apps/app_queue.c Fri Nov 14 09:00:45 2014
@@ -5629,7 +5629,7 @@
 	ao2_lock(queue_data);
 
 	if (ast_strlen_zero(queue_data->bridge_uniqueid) ||
-			strcmp(queue_data->bridge_uniqueid, transfer_msg->to_transferee.bridge_snapshot->uniqueid)) {
+			strcmp(queue_data->bridge_uniqueid, transfer_msg->bridge->uniqueid)) {
 		ao2_unlock(queue_data);
 		return;
 	}

Modified: branches/12/include/asterisk/stasis_bridges.h
URL: http://svnview.digium.com/svn/asterisk/branches/12/include/asterisk/stasis_bridges.h?view=diff&rev=427848&r1=427847&r2=427848
==============================================================================
--- branches/12/include/asterisk/stasis_bridges.h (original)
+++ branches/12/include/asterisk/stasis_bridges.h Fri Nov 14 09:00:45 2014
@@ -269,14 +269,6 @@
 };
 
 /*!
- * \brief Pair showing a bridge and a specific channel belonging to the bridge
- */
-struct ast_bridge_channel_pair {
-	struct ast_bridge *bridge;
-	struct ast_channel *channel;
-};
-
-/*!
  * \since 12
  * \brief Message type for \ref ast_blind_transfer_message.
  *
@@ -292,8 +284,10 @@
 	enum ast_transfer_result result;
 	/*! True if the transfer was initiated by an external source (i.e. not DTMF-initiated) */
 	int is_external;
-	/*! Transferer and its bridge */
-	struct ast_bridge_channel_snapshot_pair to_transferee;
+	/*! The transferring channel */
+	struct ast_channel_snapshot *transferer;
+	/*! The bridge between the transferer and the transferee */
+	struct ast_bridge_snapshot *bridge;
 	/*! Destination context */
 	char context[AST_MAX_CONTEXT];
 	/*! Destination extension */
@@ -303,6 +297,21 @@
 	/*! The channel replacing the transferer when multiple parties are being transferred */
 	struct ast_channel_snapshot *replace_channel;
 };
+
+/*!
+ * \brief Create a blind transfer message to be published
+ *
+ * \param is_external Whether the blind transfer was initiated externally (e.g. via AMI or native protocol)
+ * \param transferer The transferer's channel that is bridged to the transferee
+ * \param bridge The bridge the transferer and transferee are in
+ * \param context The destination context for the blind transfer
+ * \param exten The destination extension for the blind transfer
+ *
+ * \retval NULL Failure to allocate or create snapshots
+ * \retval non-NULL The created blind transfer message
+ */
+struct ast_blind_transfer_message *ast_blind_transfer_message_create(int is_external,
+		struct ast_channel *transferer, const char *exten, const char *context);
 
 /*!
  * \brief Publish a blind transfer event
@@ -320,9 +329,7 @@
  *                        cannot reach across the bridge due to bridge flags, this is
  *                        the channel connecting their bridge to the destination.
  */
-void ast_bridge_publish_blind_transfer(int is_external, enum ast_transfer_result result,
-		struct ast_bridge_channel_pair *to_transferee, const char *context, const char *exten,
-		struct ast_channel *transferee_channel, struct ast_channel *replace_channel);
+void ast_bridge_publish_blind_transfer(struct ast_blind_transfer_message *transfer_message);
 
 enum ast_attended_transfer_dest_type {
 	/*! The transfer failed, so there is no appropriate final state */
@@ -372,145 +379,111 @@
 };
 
 /*!
+ * \brief Create an Attended transfer message to be published.
+ *
+ * The parameters to this function are the basic necessities in order to create the
+ * initial attended transfer message.
+ *
+ * The transferee and transfer_target parameters are optional. If not provided, then this
+ * function will attempt to determine who the transferee and transfer target are based on
+ * the input transferer channels and bridges. You typically will not need to provide an
+ * explicit transferee and transfer target channel unless your attended transfer is implemented
+ * in a strange way.
+ *
+ * \param is_external Non-zero if the transfer was initiated by a native channel driver protocol.
+ * \param to_transferee The transferer channel that is bridged to the transferee channel.
+ * \param transferee_bridge The bridge between the transferer and transferee. May be NULL.
+ * \param to_transfer_target The transferer channel that is bridged to the transfer target.
+ * \param target_bridge The bridge between the transferer and transfer target. May be NULL.
+ * \param transferee The channel that is being transferred. Optional.
+ * \param transfer_target The channel that is being transferred to. Optional.
+ *
+ * \retval NULL Failure to allocate or create snapshots
+ * \retval non-NULL The created attended transfer message
+ */
+struct ast_attended_transfer_message *ast_attended_transfer_message_create(
+		int is_external, struct ast_channel *to_transferee, struct ast_bridge *transferee_bridge,
+		struct ast_channel *to_transfer_target, struct ast_bridge *target_bridge,
+		struct ast_channel *transferee, struct ast_channel *transfer_target);
+
+/*!
+ * \brief Add details for a bridge merge to an attended transfer message.
+ *
+ * If the transfer is accomplished by a bridge merge (or swap optimization), then this should
+ * be called on the created attended transfer message to have the appropriate details added on.
+ *
+ * \param transfer_msg The transfer message to add details to
+ * \param final_bridge The bridge where the surviving parties reside
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_attended_transfer_message_add_merge(struct ast_attended_transfer_message *transfer_msg,
+		struct ast_bridge *final_bridge);
+
+/*!
+ * \brief Add details for an attended transfer that was resolved as a three-way call
+ *
+ * If the transfer results in a three-way call between the transferer, the transferee, and the
+ * transfer target, then this should be called in order to add appropriate details to the
+ * transfer message to be published.
+ *
+ * \param transfer_msg The message to add details to
+ * \param survivor_channel The transferer channel that exists in the three-way call
+ * \param survivor_bridge The bridge where the three-way call takes place.
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_attended_transfer_message_add_threeway(struct ast_attended_transfer_message *transfer_msg,
+		struct ast_channel *survivor_channel, struct ast_bridge *survivor_bridge);
+
+/*!
+ * \brief Add details for an attended transfer to an application
+ *
+ * If the transfer is sending one or more parties into an application, then this should be called
+ * to add appropriate details to the transfer message being published.
+ *
+ * \param transfer_msg The message to add details to
+ * \param app The name of the application that the parties are being transferred to
+ * \param replace_channel The local channel that is in the bridge and running the application
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_attended_transfer_message_add_app(struct ast_attended_transfer_message *transfer_msg,
+		const char *app, struct ast_channel *replace_channel);
+
+/*!
+ * \brief Add details for an attended transfer that has a link between bridges.
+ *
+ * An attended transfer may be accomplished by linking two bridges together with local channels.
+ * If this is how the transfer is to be completed, call this function in order to fill in details
+ * about the transfer.
+ *
+ * \param transfer_msg The message to add details to.
+ * \param locals An array of local channel halves that each are in one of the involved bridges.
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_attended_transfer_message_add_link(struct ast_attended_transfer_message *transfer_msg,
+		struct ast_channel *locals[2]);
+
+/*!
+ * \brief Publish an attended transfer
+ *
+ * \param transfer_msg The transfer message to publish
+ */
+void ast_bridge_publish_attended_transfer(struct ast_attended_transfer_message *transfer_msg);
+
+/*!
  * \since 12
  * \brief Message type for \ref ast_attended_transfer_message.
  *
  * \retval Message type for \ref ast_attended_transfer_message.
  */
 struct stasis_message_type *ast_attended_transfer_type(void);
-
-/*!
- * \since 12
- * \brief Publish an attended transfer failure
- *
- * Publish an \ref ast_attended_transfer_message with the dest_type set to
- * \c AST_ATTENDED_TRANSFER_DEST_FAIL.
- *
- * \pre Bridges involved are locked. Channels involved are not locked.
- *
- * \param is_external Indicates if the transfer was initiated externally
- * \param result The result of the transfer. Will always be a type of failure.
- * \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
- * \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
- * \param transferee_channel If a single channel is being transferred, this is it. If multiple parties are being transferred, this is NULL.
- * \param target_channel If a single channel is being transferred to, this is it. If multiple parties are being transferred to, this is NULL.
- */
-void ast_bridge_publish_attended_transfer_fail(int is_external, enum ast_transfer_result result,
-		struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-		struct ast_channel *transferee_channel, struct ast_channel *target_channel);
-
-/*!
- * \since 12
- * \brief Publish an attended transfer that results in two bridges becoming one.
- *
- * Publish an \ref ast_attended_transfer_message with the dest_type set to
- * \c AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE. This type of attended transfer results from
- * having two bridges involved and either
- *
- * \li Merging the two bridges together
- * \li Moving a channel from one bridge to the other, thus emptying a bridge
- *
- * In either case, two bridges enter, one leaves.
- *
- * \pre Bridges involved are locked. Channels involved are not locked.
- *
- * \param is_external Indicates if the transfer was initiated externally
- * \param result The result of the transfer.
- * \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
- * \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
- * \param final_bridge The bridge that the parties end up in. Will be a bridge from the transferee or target pair.
- * \param transferee_channel If a single channel is being transferred, this is it. If multiple parties are being transferred, this is NULL.
- * \param target_channel If a single channel is being transferred to, this is it. If multiple parties are being transferred to, this is NULL.
- */
-void ast_bridge_publish_attended_transfer_bridge_merge(int is_external, enum ast_transfer_result result,
-		struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-		struct ast_bridge *final_bridge, struct ast_channel *transferee_channel,
-		struct ast_channel *target_channel);
-
-/*!
- * \since 12
- * \brief Publish an attended transfer that results in a threeway call.
- *
- * Publish an \ref ast_attended_transfer_message with the dest_type set to
- * \c AST_ATTENDED_TRANSFER_DEST_THREEWAY. Like with \ref ast_bridge_publish_attended_transfer_bridge_merge,
- * this results from merging two bridges together. The difference is that a
- * transferer channel survives the bridge merge
- *
- * \pre Bridges involved are locked. Channels involved are not locked.
- *
- * \param is_external Indicates if the transfer was initiated externally
- * \param result The result of the transfer.
- * \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
- * \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
- * \param final_pair The bridge that the parties end up in, and the transferer channel that is in this bridge.
- * \param transferee_channel If a single channel is being transferred, this is it. If multiple parties are being transferred, this is NULL.
- * \param target_channel If a single channel is being transferred to, this is it. If multiple parties are being transferred to, this is NULL.
- */
-void ast_bridge_publish_attended_transfer_threeway(int is_external, enum ast_transfer_result result,
-		struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-		struct ast_bridge_channel_pair *final_pair, struct ast_channel *transferee_channel,
-		struct ast_channel *target_channel);
-
-/*!
- * \since 12
- * \brief Publish an attended transfer that results in an application being run
- *
- * Publish an \ref ast_attended_transfer_message with the dest_type set to
- * \c AST_ATTENDED_TRANSFER_DEST_APP. This occurs when an attended transfer
- * results in either:
- *
- * \li A transferee channel leaving a bridge to run an app
- * \li A bridge of transferees running an app (via a local channel)
- *
- * \pre Bridges involved are locked. Channels involved are not locked.
- *
- * \param is_external Indicates if the transfer was initiated externally
- * \param result The result of the transfer.
- * \param transferee The bridge between the transferer and transferees as well as the
- *        transferer channel from that bridge
- * \param target The bridge between the transferer and transfer targets as well as the
- *        transferer channel from that bridge
- * \param replace_channel The channel that will be replacing the transferee bridge
- *        transferer channel when a local channel is involved
- * \param dest_app The application that the channel or bridge is running upon transfer
- *        completion.
- * \param transferee_channel If a single channel is being transferred, this is it.
- *        If multiple parties are being transferred, this is NULL.
- * \param target_channel If a single channel is being transferred to, this is it.
- *        If multiple parties are being transferred to, this is NULL.
- */
-void ast_bridge_publish_attended_transfer_app(int is_external, enum ast_transfer_result result,
-		struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-		struct ast_channel *replace_channel, const char *dest_app,
-		struct ast_channel *transferee_channel, struct ast_channel *target_channel);
-
-/*!
- * \since 12
- * \brief Publish an attended transfer that results in two bridges linked by a local channel
- *
- * Publish an \ref ast_attended_transfer_message with the dest_type set to
- * \c AST_ATTENDED_TRANSFER_DEST_LINK. This occurs when two bridges are involved
- * in an attended transfer, but their properties do not allow for the bridges to
- * merge or to have channels moved off of the bridge. An example of this occurs when
- * attempting to transfer a ConfBridge to another bridge.
- *
- * When this type of transfer occurs, the two bridges continue to exist after the
- * transfer and a local channel is used to link the two bridges together.
- *
- * \pre Bridges involved are locked. Channels involved are not locked.
- *
- * \param is_external Indicates if the transfer was initiated externally
- * \param result The result of the transfer.
- * \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
- * \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
- * \param locals The local channels linking the bridges together.
- * \param transferee_channel If a single channel is being transferred, this is it. If multiple parties are being transferred, this is NULL.
- * \param target_channel If a single channel is being transferred to, this is it. If multiple parties are being transferred to, this is NULL.
- */
-void ast_bridge_publish_attended_transfer_link(int is_external, enum ast_transfer_result result,
-		struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-		struct ast_channel *locals[2], struct ast_channel *transferee_channel,
-		struct ast_channel *target_channel);
 
 /*!
  * \brief Returns the most recent snapshot for the bridge.

Modified: branches/12/main/bridge.c
URL: http://svnview.digium.com/svn/asterisk/branches/12/main/bridge.c?view=diff&rev=427848&r1=427847&r2=427848
==============================================================================
--- branches/12/main/bridge.c (original)
+++ branches/12/main/bridge.c Fri Nov 14 09:00:45 2014
@@ -3767,26 +3767,6 @@
 	return peer;
 }
 
-static void publish_blind_transfer_full(int is_external, enum ast_transfer_result result,
-		struct ast_channel *transferer, struct ast_bridge *bridge,
-		const char *context, const char *exten, struct ast_channel *transferee_channel,
-		struct ast_channel *replace_channel)
-{
-	struct ast_bridge_channel_pair pair;
-
-	pair.channel = transferer;
-	pair.bridge = bridge;
-
-	if (bridge) {
-		ast_bridge_lock(bridge);
-	}
-	ast_bridge_publish_blind_transfer(is_external, result, &pair, context, exten,
-		transferee_channel, replace_channel);
-	if (bridge) {
-		ast_bridge_unlock(bridge);
-	}
-}
-
 /*!
  * \internal
  * \brief Transfer an entire bridge to a specific destination.
@@ -3803,9 +3783,12 @@
  * \param transferer The channel performing a transfer
  * \param bridge The bridge where the transfer is being performed
  * \param exten The destination extension for the blind transfer
+ * \param context The destination context for the blind transfer
  * \param transferee The party being transferred if there is only one
- * \param context The destination context for the blind transfer
- * \param hook Framehook to attach to local channel
+ * \param new_channel_cb Callback to call on channel that is created to
+ *        facilitate the blind transfer.
+ * \param user_data_wrapper User-provided data needed in new_channel_cb
+ * \param transfer_message The Stasis publication for this transfer.
  *
  * \return The success or failure of the operation
  */
@@ -3813,7 +3796,8 @@
 		struct ast_channel *transferer, struct ast_bridge *bridge,
 		const char *exten, const char *context, struct ast_channel *transferee,
 		transfer_channel_cb new_channel_cb,
-		struct transfer_channel_data *user_data_wrapper)
+		struct transfer_channel_data *user_data_wrapper,
+		struct ast_blind_transfer_message *transfer_message)
 {
 	struct ast_channel *local;
 	char chan_name[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2];
@@ -3826,6 +3810,12 @@
 		return AST_BRIDGE_TRANSFER_FAIL;
 	}
 
+	transfer_message->replace_channel = ast_channel_snapshot_get_latest(ast_channel_uniqueid(local));
+	if (!transfer_message->replace_channel) {
+		ast_hangup(local);
+		return AST_BRIDGE_TRANSFER_FAIL;
+	}
+
 	pbx_builtin_setvar_helper(local, BLINDTRANSFER, ast_channel_name(transferer));
 
 	if (new_channel_cb) {
@@ -3836,32 +3826,15 @@
 		ast_hangup(local);
 		return AST_BRIDGE_TRANSFER_FAIL;
 	}
+
 	if (ast_bridge_impart(bridge, local, transferer, NULL,
 		AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
 		ast_hangup(local);
 		return AST_BRIDGE_TRANSFER_FAIL;
 	}
-	publish_blind_transfer_full(is_external, AST_BRIDGE_TRANSFER_SUCCESS, transferer, bridge,
-		context, exten, transferee, local);
+
 	return AST_BRIDGE_TRANSFER_SUCCESS;
 }
-
-/*!
- * \internal
- * \brief Base data to publish for stasis attended transfer messages
- */
-struct stasis_attended_transfer_publish_data {
-	/* The bridge between the transferer and transferee, and the transferer channel in this bridge */
-	struct ast_bridge_channel_pair to_transferee;
-	/* The bridge between the transferer and transfer target, and the transferer channel in this bridge */
-	struct ast_bridge_channel_pair to_transfer_target;
-	/* The Local;1 that will replace the transferee bridge transferer channel */
-	struct ast_channel *replace_channel;
-	/* The transferee channel. NULL if there is no transferee channel or if multiple parties are transferred */
-	struct ast_channel *transferee_channel;
-	/* The transfer target channel. NULL if there is no transfer target channel or if multiple parties are transferred */
-	struct ast_channel *target_channel;
-};
 
 /*!
  * \internal
@@ -3892,118 +3865,6 @@
 
 	ao2_iterator_destroy(&channel_iter);
 	return transferee;
-}
-
-
-static void stasis_publish_data_cleanup(struct stasis_attended_transfer_publish_data *publication)
-{
-	ast_channel_unref(publication->to_transferee.channel);
-	ast_channel_unref(publication->to_transfer_target.channel);
-	ast_channel_cleanup(publication->transferee_channel);
-	ast_channel_cleanup(publication->target_channel);
-	ao2_cleanup(publication->to_transferee.bridge);
-	ao2_cleanup(publication->to_transfer_target.bridge);
-	ao2_cleanup(publication->replace_channel);
-}
-
-/*!
- * \internal
- * \brief Set up base data for an attended transfer stasis publication
- *
- * \param to_transferee The original transferer channel, which may be bridged to a transferee
- * \param to_transferee_bridge The bridge that to_transferee is in.
- * \param to_transfer_target The second transferer channel, which may be bridged to a transfer target
- * \param to_target_bridge The bridge that to_transfer_target_is in.
- * \param[out] publication A structure to hold the other parameters
- */
-static void stasis_publish_data_init(struct ast_channel *to_transferee,
-		struct ast_bridge *to_transferee_bridge, struct ast_channel *to_transfer_target,
-		struct ast_bridge *to_target_bridge,
-		struct stasis_attended_transfer_publish_data *publication)
-{
-	memset(publication, 0, sizeof(*publication));
-	publication->to_transferee.channel = ast_channel_ref(to_transferee);
-	if (to_transferee_bridge) {
-		ao2_ref(to_transferee_bridge, +1);
-		publication->to_transferee.bridge = to_transferee_bridge;
-	}
-
-	publication->to_transfer_target.channel = ast_channel_ref(to_transfer_target);
-	if (to_target_bridge) {
-		ao2_ref(to_target_bridge, +1);
-		publication->to_transfer_target.bridge = to_target_bridge;
-	}
-
-	if (to_transferee_bridge) {
-		publication->transferee_channel = ast_bridge_peer(to_transferee_bridge, to_transferee);
-	}
-	if (to_target_bridge) {
-		publication->target_channel = ast_bridge_peer(to_target_bridge, to_transfer_target);
-	}
-}
-
-/*
- * \internal
- * \brief Publish a stasis attended transfer resulting in a bridge merge
- *
- * \param publication Base data about the attended transfer
- * \param final_bridge The surviving bridge of the attended transfer
- */
-static void publish_attended_transfer_bridge_merge(struct stasis_attended_transfer_publish_data *publication,
-		struct ast_bridge *final_bridge)
-{
-	ast_bridge_publish_attended_transfer_bridge_merge(1, AST_BRIDGE_TRANSFER_SUCCESS,
-			&publication->to_transferee, &publication->to_transfer_target, final_bridge,
-			publication->transferee_channel, publication->target_channel);
-}
-
-/*
- * \internal
- * \brief Publish a stasis attended transfer to an application
- *
- * \param publication Base data about the attended transfer
- * \param app The app that is running at the conclusion of the transfer
- */
-static void publish_attended_transfer_app(struct stasis_attended_transfer_publish_data *publication,
-		const char *app)
-{
-	ast_bridge_publish_attended_transfer_app(1, AST_BRIDGE_TRANSFER_SUCCESS,
-			&publication->to_transferee, &publication->to_transfer_target,
-			publication->replace_channel, app,
-			publication->transferee_channel, publication->target_channel);
-}
-
-/*
- * \internal
- * \brief Publish a stasis attended transfer showing a link between bridges
- *
- * \param publication Base data about the attended transfer
- * \param local_channel1 Local channel in the original bridge
- * \param local_channel2 Local channel in the second bridge
- */
-static void publish_attended_transfer_link(struct stasis_attended_transfer_publish_data *publication,
-		struct ast_channel *local_channel1, struct ast_channel *local_channel2)
-{
-	struct ast_channel *locals[2] = { local_channel1, local_channel2 };
-
-	ast_bridge_publish_attended_transfer_link(1, AST_BRIDGE_TRANSFER_SUCCESS,
-			&publication->to_transferee, &publication->to_transfer_target, locals,
-			publication->transferee_channel, publication->target_channel);
-}
-
-/*
- * \internal
- * \brief Publish a stasis attended transfer failure
- *
- * \param publication Base data about the attended transfer
- * \param result The transfer result
- */
-static void publish_attended_transfer_fail(struct stasis_attended_transfer_publish_data *publication,
-		enum ast_transfer_result result)
-{
-	ast_bridge_publish_attended_transfer_fail(1, result, &publication->to_transferee,
-			&publication->to_transfer_target, publication->transferee_channel,
-			publication->target_channel);
 }
 
 /*!
@@ -4030,7 +3891,7 @@
  */
 static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *chan1,
 		struct ast_channel *chan2, struct ast_bridge *bridge1, struct ast_bridge *bridge2,
-		struct stasis_attended_transfer_publish_data *publication)
+		struct ast_attended_transfer_message *transfer_msg)
 {
 	static const char *dest = "_attended at transfer/m";
 	struct ast_channel *local_chan;
@@ -4075,6 +3936,7 @@
 
 	if (bridge2) {
 		RAII_VAR(struct ast_channel *, local_chan2, NULL, ao2_cleanup);
+		struct ast_channel *locals[2];
 
 		ast_channel_lock(local_chan);
 		local_chan2 = ast_local_get_peer(local_chan);
@@ -4082,11 +3944,12 @@
 
 		ast_assert(local_chan2 != NULL);
 
-		publish_attended_transfer_link(publication,
-				local_chan, local_chan2);
+		locals[0] = local_chan;
+		locals[1] = local_chan2;
+
+		ast_attended_transfer_message_add_link(transfer_msg, locals);
 	} else {
-		publication->replace_channel = ao2_bump(local_chan);
-		publish_attended_transfer_app(publication, app);
+		ast_attended_transfer_message_add_app(transfer_msg, app, local_chan);
 	}
 
 	ao2_cleanup(local_chan);
@@ -4192,14 +4055,6 @@
 	return bridge;
 }
 
-static void publish_blind_transfer(int is_external, enum ast_transfer_result result,
-		struct ast_channel *transferer, struct ast_bridge *bridge,
-		const char *context, const char *exten, struct ast_channel *transferee_channel)
-{
-	publish_blind_transfer_full(is_external, result, transferer, bridge, context,
-		exten, transferee_channel, NULL);
-}
-
 enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
 		struct ast_channel *transferer, const char *exten, const char *context,
 		transfer_channel_cb new_channel_cb, void *user_data)
@@ -4209,9 +4064,20 @@
 	RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_channel *, transferee, NULL, ast_channel_cleanup);
 	RAII_VAR(struct transfer_channel_data *, user_data_wrapper, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_blind_transfer_message *, transfer_message, NULL, ao2_cleanup);
 	int do_bridge_transfer;
 	int transfer_prohibited;
 	enum ast_transfer_result transfer_result;
+
+	transfer_message = ast_blind_transfer_message_create(is_external, transferer, exten, context);
+	if (!transfer_message) {
+		/* Out of memory. Not even possible to publish a Stasis message about the
+		 * failure
+		 */
+		ast_log(LOG_ERROR, "Unable to allocate memory for blind transfer publication from %s\n",
+				ast_channel_name(transferer));
+		return AST_BRIDGE_TRANSFER_FAIL;
+	}
 
 	bridge = acquire_bridge(transferer);
 	if (!bridge) {
@@ -4219,7 +4085,22 @@
 		goto publish;
 	}
 
+	ast_bridge_lock(bridge);
+	transfer_message->bridge = ast_bridge_snapshot_create(bridge);
+	ast_bridge_unlock(bridge);
+	if (!transfer_message->bridge) {
+		transfer_result = AST_BRIDGE_TRANSFER_FAIL;
+		goto publish;
+	}
+
 	transferee = ast_bridge_peer(bridge, transferer);
+	if (transferee) {
+		transfer_message->transferee = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transferee));
+		if (!transfer_message->transferee) {
+			transfer_result = AST_BRIDGE_TRANSFER_FAIL;
+			goto publish;
+		}
+	}
 
 	ast_channel_lock(transferer);
 	bridge_channel = ast_channel_get_bridge_channel(transferer);
@@ -4275,12 +4156,8 @@
 	set_transfer_variables_all(transferer, channels, 0);
 
 	if (do_bridge_transfer) {
-		/* if blind_transfer_bridge succeeds, it publishes its own message */
 		transfer_result = blind_transfer_bridge(is_external, transferer, bridge,
-			exten, context, transferee, new_channel_cb, user_data_wrapper);
-		if (transfer_result == AST_BRIDGE_TRANSFER_SUCCESS)  {
-			return transfer_result;
-		}
+			exten, context, transferee, new_channel_cb, user_data_wrapper, transfer_message);
 		goto publish;
 	}
 
@@ -4301,7 +4178,8 @@
 	transfer_result = AST_BRIDGE_TRANSFER_SUCCESS;
 
 publish:
-	publish_blind_transfer(is_external, transfer_result, transferer, bridge, context, exten, transferee);
+	transfer_message->result = transfer_result;
+	ast_bridge_publish_blind_transfer(transfer_message);
 	return transfer_result;
 }
 
@@ -4368,7 +4246,7 @@
 		struct ast_channel *to_transfer_target,
 		struct ast_bridge_channel *to_target_bridge_channel,
 		struct ast_bridge *to_transferee_bridge, struct ast_bridge *to_target_bridge,
-		struct stasis_attended_transfer_publish_data *publication)
+		struct ast_attended_transfer_message *transfer_msg)
 {
 	struct ast_bridge_channel *kick_me[] = {
 			to_transferee_bridge_channel,
@@ -4414,20 +4292,16 @@
 		 */
 		if (to_transferee_bridge->inhibit_merge || to_transferee_bridge->dissolved ||
 				to_target_bridge->inhibit_merge || to_target_bridge->dissolved) {
-			res = AST_BRIDGE_TRANSFER_INVALID;
-			goto end;
-		}
-
-		/* Don't goto end here. attended_transfer_bridge will publish its own
-		 * stasis message if it succeeds
-		 */
+			return AST_BRIDGE_TRANSFER_INVALID;
+		}
+
 		return attended_transfer_bridge(to_transferee, to_transfer_target,
-			to_transferee_bridge, to_target_bridge, publication);
+			to_transferee_bridge, to_target_bridge, transfer_msg);
 	}
 
 end:
 	if (res == AST_BRIDGE_TRANSFER_SUCCESS) {
-		publish_attended_transfer_bridge_merge(publication, final_bridge);
+		ast_attended_transfer_message_add_merge(transfer_msg, final_bridge);
 	}
 
 	return res;
@@ -4442,6 +4316,7 @@
 	RAII_VAR(struct ast_bridge_channel *, to_target_bridge_channel, NULL, ao2_cleanup);
 	RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_channel *, transferee, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
 	struct ast_bridge *the_bridge = NULL;
 	struct ast_channel *chan_bridged;
 	struct ast_channel *chan_unbridged;
@@ -4449,13 +4324,17 @@
 	int do_bridge_transfer;
 	enum ast_transfer_result res;
 	const char *app = NULL;
-	struct stasis_attended_transfer_publish_data publication;
 
 	to_transferee_bridge = acquire_bridge(to_transferee);
 	to_target_bridge = acquire_bridge(to_transfer_target);
 
-	stasis_publish_data_init(to_transferee, to_transferee_bridge,
-			to_transfer_target, to_target_bridge, &publication);
+	transfer_msg = ast_attended_transfer_message_create(1, to_transferee, to_transferee_bridge,
+			to_transfer_target, to_target_bridge, NULL, NULL);
+	if (!transfer_msg) {
+		ast_log(LOG_ERROR, "Unable to create Stasis publication for attended transfer from %s\n",
+				ast_channel_name(to_transferee));
+		return AST_BRIDGE_TRANSFER_FAIL;
+	}
 
 	/* They can't both be unbridged, you silly goose! */
 	if (!to_transferee_bridge && !to_target_bridge) {
@@ -4520,7 +4399,7 @@
 		ast_bridge_lock_both(to_transferee_bridge, to_target_bridge);
 		res = two_bridge_attended_transfer(to_transferee, to_transferee_bridge_channel,
 				to_transfer_target, to_target_bridge_channel,
-				to_transferee_bridge, to_target_bridge, &publication);
+				to_transferee_bridge, to_target_bridge, transfer_msg);
 		ast_bridge_unlock(to_transferee_bridge);
 		ast_bridge_unlock(to_target_bridge);
 
@@ -4561,7 +4440,7 @@
 
 	if (do_bridge_transfer) {
 		ast_bridge_lock(the_bridge);
-		res = attended_transfer_bridge(chan_bridged, chan_unbridged, the_bridge, NULL, &publication);
+		res = attended_transfer_bridge(chan_bridged, chan_unbridged, the_bridge, NULL, transfer_msg);
 		ast_bridge_unlock(the_bridge);
 		goto end;
 	}
@@ -4580,32 +4459,12 @@
 
 	ast_bridge_remove(the_bridge, chan_bridged);
 
-	ast_bridge_lock(the_bridge);
-	publish_attended_transfer_app(&publication, app);
-	ast_bridge_unlock(the_bridge);
+	ast_attended_transfer_message_add_app(transfer_msg, app, NULL);
 	res = AST_BRIDGE_TRANSFER_SUCCESS;
 
 end:
-	/* All successful transfer paths have published an appropriate stasis message.
-	 * All failure paths have deferred publishing a stasis message until this point
-	 */
-	if (res != AST_BRIDGE_TRANSFER_SUCCESS) {
-		if (to_transferee_bridge && to_target_bridge) {
-			ast_bridge_lock_both(to_transferee_bridge, to_target_bridge);
-		} else if (the_bridge) {
-			ast_bridge_lock(the_bridge);
-		}
-
-		publish_attended_transfer_fail(&publication, res);
-
-		if (to_transferee_bridge && to_target_bridge) {
-			ast_bridge_unlock(to_transferee_bridge);
-			ast_bridge_unlock(to_target_bridge);
-		} else if (the_bridge) {
-			ast_bridge_unlock(the_bridge);
-		}
-	}
-	stasis_publish_data_cleanup(&publication);
+	transfer_msg->result = res;
+	ast_bridge_publish_attended_transfer(transfer_msg);
 	return res;
 }
 

Modified: branches/12/main/bridge_basic.c
URL: http://svnview.digium.com/svn/asterisk/branches/12/main/bridge_basic.c?view=diff&rev=427848&r1=427847&r2=427848
==============================================================================
--- branches/12/main/bridge_basic.c (original)
+++ branches/12/main/bridge_basic.c Fri Nov 14 09:00:45 2014
@@ -1605,33 +1605,21 @@
 static void publish_transfer_success(struct attended_transfer_properties *props,
 		struct ast_channel *transferee_channel, struct ast_channel *target_channel)
 {
-	struct ast_bridge_channel_pair transferee = {
-		.channel = props->transferer,
-		.bridge = props->transferee_bridge,
-	};
-	struct ast_bridge_channel_pair transfer_target = {
-		.channel = props->transferer,
-		.bridge = props->target_bridge,
-	};
-
-	if (transferee.bridge && transfer_target.bridge) {
-		ast_bridge_lock_both(transferee.bridge, transfer_target.bridge);
-	} else if (transferee.bridge) {
-		ast_bridge_lock(transferee.bridge);
-	} else if (transfer_target.bridge) {
-		ast_bridge_lock(transfer_target.bridge);
-	}
-
-	ast_bridge_publish_attended_transfer_bridge_merge(0, AST_BRIDGE_TRANSFER_SUCCESS,
-			&transferee, &transfer_target, props->transferee_bridge, transferee_channel,
-			target_channel);
-
-	if (transferee.bridge) {
-		ast_bridge_unlock(transferee.bridge);
-	}
-	if (transfer_target.bridge) {
-		ast_bridge_unlock(transfer_target.bridge);
-	}
+	struct ast_attended_transfer_message *transfer_msg;
+
+	transfer_msg = ast_attended_transfer_message_create(0, props->transferer,
+			props->transferee_bridge, props->transferer, props->target_bridge,
+			transferee_channel, target_channel);
+
+	if (!transfer_msg) {
+		ast_log(LOG_ERROR, "Unable to publish successful attended transfer from %s\n",
+				ast_channel_name(props->transferer));
+		return;
+	}
+
+	ast_attended_transfer_message_add_merge(transfer_msg, props->transferee_bridge);
+	ast_bridge_publish_attended_transfer(transfer_msg);
+	ao2_cleanup(transfer_msg);
 }
 
 /*!
@@ -1640,37 +1628,22 @@
 static void publish_transfer_threeway(struct attended_transfer_properties *props,
 		struct ast_channel *transferee_channel, struct ast_channel *target_channel)
 {
-	struct ast_bridge_channel_pair transferee = {
-		.channel = props->transferer,
-		.bridge = props->transferee_bridge,
-	};
-	struct ast_bridge_channel_pair transfer_target = {
-		.channel = props->transferer,
-		.bridge = props->target_bridge,
-	};
-	struct ast_bridge_channel_pair threeway = {
-		.channel = props->transferer,
-		.bridge = props->transferee_bridge,
-	};
-
-	if (transferee.bridge && transfer_target.bridge) {
-		ast_bridge_lock_both(transferee.bridge, transfer_target.bridge);
-	} else if (transferee.bridge) {
-		ast_bridge_lock(transferee.bridge);
-	} else if (transfer_target.bridge) {
-		ast_bridge_lock(transfer_target.bridge);
-	}
-
-	ast_bridge_publish_attended_transfer_threeway(0, AST_BRIDGE_TRANSFER_SUCCESS,
-			&transferee, &transfer_target, &threeway, transferee_channel,
-			target_channel);
-
-	if (transferee.bridge) {
-		ast_bridge_unlock(transferee.bridge);
-	}
-	if (transfer_target.bridge) {
-		ast_bridge_unlock(transfer_target.bridge);
-	}
+	struct ast_attended_transfer_message *transfer_msg;
+
+	transfer_msg = ast_attended_transfer_message_create(0, props->transferer,
+			props->transferee_bridge, props->transferer, props->target_bridge,
+			transferee_channel, target_channel);
+
+	if (!transfer_msg) {
+		ast_log(LOG_ERROR, "Unable to publish successful three-way transfer from %s\n",
+				ast_channel_name(props->transferer));
+		return;
+	}
+
+	ast_attended_transfer_message_add_threeway(transfer_msg, props->transferer,
+			props->transferee_bridge);
+	ast_bridge_publish_attended_transfer(transfer_msg);
+	ao2_cleanup(transfer_msg);
 }
 
 /*!
@@ -1678,38 +1651,21 @@
  */
 static void publish_transfer_fail(struct attended_transfer_properties *props)
 {

[... 511 lines stripped ...]



More information about the asterisk-commits mailing list