[asterisk-commits] rmudgett: branch group/bridge_construction r382215 - in /team/group/bridge_co...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Feb 27 15:27:04 CST 2013


Author: rmudgett
Date: Wed Feb 27 15:26:59 2013
New Revision: 382215

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=382215
Log:
Bridge API enhancements: Implement threading model for bridge management thread.

Merge work accomplishing this from team/rmudgett/bridge_phase.

* Always start the central bridge thread.  Change bridge_thread() to just
wait for something to happen if there is no bridge thread loop callback.

* Move the smart bridge operation into the central bridge thread.

* Add bridge merge inhibit options.  This is intended to prevent local
channel optimization and attended transfers from merging bridges in an
inappropriate fashion or time.

* Refactor code to extract ast_bridge_channel_pull(),
ast_bridge_channel_push(), and bridge_array_grow().  As a result
bridge_array_add(), bridge_channel_join(), and ast_bridge_merge() are
simpler.

* Rename struct ast_bridge_technology.poke to poke_channel.

* Rename struct ast_bridge.num to num_channels.

* Make bridge technology destroy, leave, and poke_channel callbacks return
void.

* Rename several functions and make semi-public.
generic_thread_loop() -> ast_bridge_thread_generic()
bridge_poke() -> ast_bridge_poke()

* Rename bridge_channel_poke() to bridge_channel_poke_locked().

* Extract ast_bridge_channel_poke() and make semi-public.

* Always give debug message regardless of the technology having the
callback.

(issue ASTERISK-21052)
Reported by: Matt Jordan

Modified:
    team/group/bridge_construction/apps/app_confbridge.c
    team/group/bridge_construction/bridges/bridge_multiplexed.c
    team/group/bridge_construction/bridges/bridge_simple.c
    team/group/bridge_construction/bridges/bridge_softmix.c
    team/group/bridge_construction/include/asterisk/bridging.h
    team/group/bridge_construction/include/asterisk/bridging_features.h
    team/group/bridge_construction/include/asterisk/bridging_technology.h
    team/group/bridge_construction/main/bridging.c

Modified: team/group/bridge_construction/apps/app_confbridge.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/apps/app_confbridge.c?view=diff&rev=382215&r1=382214&r2=382215
==============================================================================
--- team/group/bridge_construction/apps/app_confbridge.c (original)
+++ team/group/bridge_construction/apps/app_confbridge.c Wed Feb 27 15:26:59 2013
@@ -1266,7 +1266,8 @@
 		conf_bridge_profile_copy(&conference_bridge->b_profile, &conference_bridge_user->b_profile);
 
 		/* Create an actual bridge that will do the audio mixing */
-		if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_MULTIMIX, 0))) {
+		if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_MULTIMIX,
+			AST_BRIDGE_FLAG_MASQUERADE_ONLY))) {
 			ao2_ref(conference_bridge, -1);
 			conference_bridge = NULL;
 			ao2_unlock(conference_bridges);

Modified: team/group/bridge_construction/bridges/bridge_multiplexed.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/bridges/bridge_multiplexed.c?view=diff&rev=382215&r1=382214&r2=382215
==============================================================================
--- team/group/bridge_construction/bridges/bridge_multiplexed.c (original)
+++ team/group/bridge_construction/bridges/bridge_multiplexed.c Wed Feb 27 15:26:59 2013
@@ -188,14 +188,14 @@
 }
 
 /*! \brief Destroy function which unreserves/unreferences/removes a multiplexed thread structure */
-static int multiplexed_bridge_destroy(struct ast_bridge *bridge)
+static void multiplexed_bridge_destroy(struct ast_bridge *bridge)
 {
 	struct multiplexed_thread *muxed_thread;
 	pthread_t thread;
 
 	muxed_thread = bridge->bridge_pvt;
 	if (!muxed_thread) {
-		return -1;
+		return;
 	}
 	bridge->bridge_pvt = NULL;
 
@@ -224,7 +224,6 @@
 	}
 
 	ao2_ref(muxed_thread, -1);
-	return 0;
 }
 
 /*! \brief Thread function that executes for multiplexed threads */
@@ -283,6 +282,7 @@
 				}
 			}
 			if (!stop && bridge) {
+/* BUGBUG need to update thread callid for each bridge trip. */
 				ast_bridge_handle_trip(bridge, NULL, winner, -1);
 				ao2_unlock(bridge);
 			}
@@ -403,7 +403,9 @@
 
 	ast_debug(1, "Adding channel '%s' to multiplexed thread '%p' for monitoring\n", ast_channel_name(bridge_channel->chan), muxed_thread);
 
-	multiplexed_chan_add(muxed_thread, bridge_channel->chan);
+	if (!bridge_channel->suspended) {
+		multiplexed_chan_add(muxed_thread, bridge_channel->chan);
+	}
 
 	/* If the second channel has not yet joined do not make things compatible */
 	if (c0 == c1) {
@@ -420,15 +422,15 @@
 }
 
 /*! \brief Leave function which actually removes the channel from the array */
-static int multiplexed_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+static void multiplexed_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
 	struct multiplexed_thread *muxed_thread = bridge->bridge_pvt;
 
 	ast_debug(1, "Removing channel '%s' from multiplexed thread '%p'\n", ast_channel_name(bridge_channel->chan), muxed_thread);
 
-	multiplexed_chan_remove(muxed_thread, bridge_channel->chan);
-
-	return 0;
+	if (!bridge_channel->suspended) {
+		multiplexed_chan_remove(muxed_thread, bridge_channel->chan);
+	}
 }
 
 /*! \brief Suspend function which means control of the channel is going elsewhere */
@@ -462,8 +464,9 @@
 	}
 
 	/* Find the channel we actually want to write to */
-	if (!(other = (AST_LIST_FIRST(&bridge->channels) == bridge_channel ? AST_LIST_LAST(&bridge->channels) : AST_LIST_FIRST(&bridge->channels)))) {
-		return AST_BRIDGE_WRITE_FAILED;
+	other = AST_LIST_FIRST(&bridge->channels);
+	if (other == bridge_channel) {
+		other = AST_LIST_LAST(&bridge->channels);
 	}
 
 	/* The bridging core takes care of freeing the passed in frame. */

Modified: team/group/bridge_construction/bridges/bridge_simple.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/bridges/bridge_simple.c?view=diff&rev=382215&r1=382214&r2=382215
==============================================================================
--- team/group/bridge_construction/bridges/bridge_simple.c (original)
+++ team/group/bridge_construction/bridges/bridge_simple.c Wed Feb 27 15:26:59 2013
@@ -76,8 +76,9 @@
 	}
 
 	/* Find the channel we actually want to write to */
-	if (!(other = (AST_LIST_FIRST(&bridge->channels) == bridge_channel ? AST_LIST_LAST(&bridge->channels) : AST_LIST_FIRST(&bridge->channels)))) {
-		return AST_BRIDGE_WRITE_FAILED;
+	other = AST_LIST_FIRST(&bridge->channels);
+	if (other == bridge_channel) {
+		other = AST_LIST_LAST(&bridge->channels);
 	}
 
 	/* The bridging core takes care of freeing the passed in frame. */
@@ -93,9 +94,10 @@
 
 static struct ast_bridge_technology simple_bridge = {
 	.name = "simple_bridge",
-	.capabilities = AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_THREAD,
+	.capabilities = AST_BRIDGE_CAPABILITY_1TO1MIX,
 	.preference = AST_BRIDGE_PREFERENCE_MEDIUM,
 	.join = simple_bridge_join,
+	.thread_loop = ast_bridge_thread_generic,
 	.write = simple_bridge_write,
 };
 

Modified: team/group/bridge_construction/bridges/bridge_softmix.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/bridges/bridge_softmix.c?view=diff&rev=382215&r1=382214&r2=382215
==============================================================================
--- team/group/bridge_construction/bridges/bridge_softmix.c (original)
+++ team/group/bridge_construction/bridges/bridge_softmix.c Wed Feb 27 15:26:59 2013
@@ -327,17 +327,16 @@
 }
 
 /*! \brief Function called when a bridge is destroyed */
-static int softmix_bridge_destroy(struct ast_bridge *bridge)
+static void softmix_bridge_destroy(struct ast_bridge *bridge)
 {
 	struct softmix_bridge_data *softmix_data;
 
 	softmix_data = bridge->bridge_pvt;
 	if (!softmix_data) {
-		return -1;
+		return;
 	}
 	ao2_ref(softmix_data, -1);
 	bridge->bridge_pvt = NULL;
-	return 0;
 }
 
 static void set_softmix_bridge_data(int rate, int interval, struct ast_bridge_channel *bridge_channel, int reset)
@@ -407,12 +406,12 @@
 }
 
 /*! \brief Function called when a channel leaves the bridge */
-static int softmix_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+static void softmix_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
 	struct softmix_channel *sc = bridge_channel->bridge_pvt;
 
 	if (!(bridge_channel->bridge_pvt)) {
-		return 0;
+		return;
 	}
 	bridge_channel->bridge_pvt = NULL;
 
@@ -427,8 +426,6 @@
 
 	/* Eep! drop ourselves */
 	ast_free(sc);
-
-	return 0;
 }
 
 /*!
@@ -598,7 +595,7 @@
 }
 
 /*! \brief Function called when the channel's thread is poked */
-static int softmix_bridge_poke(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+static void softmix_bridge_poke_channel(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
 	struct softmix_channel *sc = bridge_channel->bridge_pvt;
 
@@ -610,8 +607,6 @@
 	}
 
 	ast_mutex_unlock(&sc->lock);
-
-	return 0;
 }
 
 static void gather_softmix_stats(struct softmix_stats *stats,
@@ -772,11 +767,11 @@
 	ast_timer_set_rate(timer, (1000 / softmix_data->internal_mixing_interval));
 
 	/* Give the mixing array room to grow, memory is cheap but allocations are expensive. */
-	if (softmix_mixing_array_init(&mixing_array, bridge->num + 10)) {
+	if (softmix_mixing_array_init(&mixing_array, bridge->num_channels + 10)) {
 		goto softmix_cleanup;
 	}
 
-	while (!bridge->stop && !bridge->refresh && bridge->array_num) {
+	while (!bridge->interrupt && bridge->array_num) {
 		struct ast_bridge_channel *bridge_channel;
 		int timeout = -1;
 		enum ast_format_id cur_slin_id = ast_format_slin_by_rate(softmix_data->internal_rate);
@@ -793,8 +788,8 @@
 		}
 
 		/* Grow the mixing array buffer as participants are added. */
-		if (mixing_array.max_num_entries < bridge->num
-			&& softmix_mixing_array_grow(&mixing_array, bridge->num + 5)) {
+		if (mixing_array.max_num_entries < bridge->num_channels
+			&& softmix_mixing_array_grow(&mixing_array, bridge->num_channels + 5)) {
 			goto softmix_cleanup;
 		}
 
@@ -918,15 +913,15 @@
 
 static struct ast_bridge_technology softmix_bridge = {
 	.name = "softmix",
-	.capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX | AST_BRIDGE_CAPABILITY_THREAD | AST_BRIDGE_CAPABILITY_MULTITHREADED | AST_BRIDGE_CAPABILITY_OPTIMIZE | AST_BRIDGE_CAPABILITY_VIDEO,
+	.capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX | AST_BRIDGE_CAPABILITY_MULTITHREADED | AST_BRIDGE_CAPABILITY_OPTIMIZE | AST_BRIDGE_CAPABILITY_VIDEO,
 	.preference = AST_BRIDGE_PREFERENCE_LOW,
 	.create = softmix_bridge_create,
 	.destroy = softmix_bridge_destroy,
 	.join = softmix_bridge_join,
 	.leave = softmix_bridge_leave,
 	.write = softmix_bridge_write,
-	.thread = softmix_bridge_thread,
-	.poke = softmix_bridge_poke,
+	.thread_loop = softmix_bridge_thread,
+	.poke_channel = softmix_bridge_poke_channel,
 };
 
 static int unload_module(void)

Modified: team/group/bridge_construction/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/include/asterisk/bridging.h?view=diff&rev=382215&r1=382214&r2=382215
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging.h Wed Feb 27 15:26:59 2013
@@ -75,8 +75,6 @@
 	AST_BRIDGE_CAPABILITY_MULTIMIX = (1 << 3),
 	/*! Bridge should run using the multithreaded model */
 	AST_BRIDGE_CAPABILITY_MULTITHREADED = (1 << 4),
-	/*! Bridge should run a central bridge thread */
-	AST_BRIDGE_CAPABILITY_THREAD = (1 << 5),
 	/*! Bridge technology can do video mixing (or something along those lines) */
 	AST_BRIDGE_CAPABILITY_VIDEO = (1 << 6),
 	/*! Bridge technology can optimize things based on who is talking */
@@ -146,7 +144,11 @@
 	pthread_t thread;
 	/*! Additional file descriptors to look at */
 	int fds[4];
-	/*! Bit to indicate whether the channel is suspended from the bridge or not */
+	/*! TRUE if the channel is in a bridge. */
+	unsigned int in_bridge:1;
+	/*! TRUE if the channel just joined the bridge. */
+	unsigned int just_joined:1;
+	/*! TRUE if the channel is suspended from the bridge. */
 	unsigned int suspended:1;
 	/*! TRUE if the imparted channel must wait for an explicit depart from the bridge to reclaim the channel. */
 	unsigned int depart_wait:1;
@@ -220,9 +222,16 @@
 	/*! Condition, used if we want to wake up the bridge thread. */
 	ast_cond_t cond;
 	/*! Number of channels participating in the bridge */
-	int num;
+	int num_channels;
 	/*! The video mode this bridge is using */
 	struct ast_bridge_video_mode video_mode;
+	/*!
+	 * \brief Count of the active temporary requests to inhibit bridge merges.
+	 * Zero if merges are allowed.
+	 *
+	 * \note Temporary as in try again in a moment.
+	 */
+	unsigned int inhibit_merge;
 	/*! The internal sample rate this bridge is mixed at when multiple channels are being mixed.
 	 *  If this value is 0, the bridge technology may auto adjust the internal mixing rate. */
 	unsigned int internal_sample_rate;
@@ -234,8 +243,10 @@
 	unsigned int waiting:1;
 	/*! TRUE if the bridge thread should stop */
 	unsigned int stop:1;
-	/*! TRUE if the bridge thread should refresh itself */
-	unsigned int refresh:1;
+	/*! TRUE if the bridge was reconfigured. */
+	unsigned int reconfigured:1;
+	/*! TRUE if the bridge thread loop should break.  Reconfig, stop, action-queue. */
+	unsigned int interrupt:1;
 	/*! TRUE if the bridge has been dissolved.  Any channel that now tries to join is immediately ejected. */
 	unsigned int dissolved:1;
 	/*! Bridge flags to tweak behavior */
@@ -250,16 +261,18 @@
 	struct ast_bridge_features features;
 	/*! Array of channels that the bridge thread is currently handling */
 	struct ast_channel **array;
-	/*! Number of channels in the above array */
-	size_t array_num;
+	/*! Number of channels in the above array (Number of active channels) */
+	unsigned int array_num;
 	/*! Number of channels the array can handle */
-	size_t array_size;
+	unsigned int array_size;
 	/*! Call ID associated with the bridge */
 	struct ast_callid *callid;
 	/*! Linked list of channels participating in the bridge */
 	AST_LIST_HEAD_NOLOCK(, ast_bridge_channel) channels;
 	/*! Linked list of channels removed from the bridge and waiting to be departed. */
 	AST_LIST_HEAD_NOLOCK(, ast_bridge_channel) depart_wait;
+	/*! Queue of actions to perform on the bridge. */
+	AST_LIST_HEAD_NOLOCK(, ast_frame) action_queue;
 };
 
 /*!
@@ -480,8 +493,8 @@
 /*!
  * \brief Merge two bridges together
  *
- * \param bridge0 First bridge
- * \param bridge1 Second bridge
+ * \param bridge1 First bridge
+ * \param bridge2 Second bridge
  *
  * \retval 0 on success
  * \retval -1 on failure
@@ -489,16 +502,31 @@
  * Example usage:
  *
  * \code
- * ast_bridge_merge(bridge0, bridge1);
- * \endcode
- *
- * This merges the bridge pointed to by bridge1 with the bridge pointed to by bridge0.
- * In reality all of the channels in bridge1 are simply moved to bridge0.
- *
- * \note The second bridge specified is not destroyed when this operation is
- *       completed.
- */
-int ast_bridge_merge(struct ast_bridge *bridge0, struct ast_bridge *bridge1);
+ * ast_bridge_merge(bridge1, bridge2);
+ * \endcode
+ *
+ * This merges the bridge pointed to by bridge2 into the bridge
+ * pointed to by bridge1.  In reality all of the channels in
+ * bridge2 are moved to bridge1.
+ *
+ * \note The second bridge has no active channels in it when
+ * this operation is completed.  The caller must explicitly call
+ * ast_bridge_destroy().
+ */
+int ast_bridge_merge(struct ast_bridge *bridge1, struct ast_bridge *bridge2);
+
+/*!
+ * \brief Adjust the bridge merge inhibit request count.
+ * \since 12.0.0
+ *
+ * \param bridge What to operate on.
+ * \param request Inhibit request increment.
+ *     (Positive to add requests.  Negative to remove requests.)
+ *
+ * \return Nothing
+ */
+
+void ast_bridge_merge_inhibit(struct ast_bridge *bridge, int request);
 
 /*!
  * \brief Suspend a channel temporarily from a bridge
@@ -577,6 +605,21 @@
  *       request the channel exit the bridge.
  */
 void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state);
+
+/*!
+ * \brief Put an action onto the specified bridge.
+ * \since 12.0.0
+ *
+ * \param bridge What to queue the action on.
+ * \param action What to do.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ *
+ * \note This API call is meant for internal bridging operations.
+ * \note BUGBUG This may get moved.
+ */
+int ast_bridge_queue_action(struct ast_bridge *bridge, struct ast_frame *action);
 
 /*!
  * \brief Put an action onto the specified bridge_channel.

Modified: team/group/bridge_construction/include/asterisk/bridging_features.h
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/include/asterisk/bridging_features.h?view=diff&rev=382215&r1=382214&r2=382215
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging_features.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging_features.h Wed Feb 27 15:26:59 2013
@@ -36,6 +36,12 @@
 	AST_BRIDGE_FLAG_SMART = (1 << 1),
 	/*! This channel leaves the bridge if all participants have this flag set. */
 	AST_BRIDGE_FLAG_LONELY = (1 << 2),
+	/*! Bridge channels cannot be merged from this bridge. */
+	AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM = (1 << 3),
+	/*! Bridge channels cannot be merged to this bridge. */
+	AST_BRIDGE_FLAG_MERGE_INHIBIT_TO = (1 << 4),
+	/*! Bridge channels can be moved to another bridge only by masquerade (ConfBridge) */
+	AST_BRIDGE_FLAG_MASQUERADE_ONLY = (1 << 5),
 };
 
 /*! \brief Built in DTMF features */

Modified: team/group/bridge_construction/include/asterisk/bridging_technology.h
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/include/asterisk/bridging_technology.h?view=diff&rev=382215&r1=382214&r2=382215
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging_technology.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging_technology.h Wed Feb 27 15:26:59 2013
@@ -52,25 +52,25 @@
 	/*! Callback for when a bridge is being created */
 	int (*create)(struct ast_bridge *bridge);
 	/*! Callback for when a bridge is being destroyed */
-	int (*destroy)(struct ast_bridge *bridge);
+	void (*destroy)(struct ast_bridge *bridge);
 	/*! Callback for when a channel is being added to a bridge */
 	int (*join)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
 	/*! Callback for when a channel is leaving a bridge */
-	int (*leave)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
+	void (*leave)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
 	/*! Callback for when a channel is suspended from the bridge */
 	void (*suspend)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
 	/*! Callback for when a channel is unsuspended from the bridge */
 	void (*unsuspend)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
-	/*! Callback to see if a channel is compatible with the bridging technology */
-	int (*compatible)(struct ast_bridge_channel *bridge_channel);
+	/*! Callback to see if the bridge is compatible with the bridging technology */
+	int (*compatible)(struct ast_bridge *bridge);
 	/*! Callback for writing a frame into the bridging technology */
 	enum ast_bridge_write_result (*write)(struct ast_bridge *bridge, struct ast_bridge_channel *bridged_channel, struct ast_frame *frame);
 	/*! Callback for when a file descriptor trips */
 	int (*fd)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, int fd);
-	/*! Callback for replacement thread function */
-	int (*thread)(struct ast_bridge *bridge);
-	/*! Callback for poking a bridge thread */
-	int (*poke)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
+	/*! Callback for bridge thread loop */
+	int (*thread_loop)(struct ast_bridge *bridge);
+	/*! Callback for poking a bridge channel thread */
+	void (*poke_channel)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
 	/*! Formats that the bridge technology supports */
 	struct ast_format_cap *format_capabilities;
 	/*! TRUE if the bridge technology is currently suspended. */
@@ -126,6 +126,41 @@
 int ast_bridge_technology_unregister(struct ast_bridge_technology *technology);
 
 /*!
+ * \brief Generic bridge thread loop.
+ * \since 12.0.0
+ *
+ * \param bridge Handle this bridge thread's loop.
+ *
+ * \retval 0 on success.
+ * \retval non-zero on error.
+ */
+int ast_bridge_thread_generic(struct ast_bridge *bridge);
+
+/*!
+ * \brief Poke the bridge thread if it is not us.
+ * \since 12.0.0
+ *
+ * \param bridge What to poke.
+ *
+ * \note This function assumes the bridge is locked.
+ *
+ * \return Nothing
+ */
+void ast_bridge_poke(struct ast_bridge *bridge);
+
+/*!
+ * \brief Poke the bridge channel thread if it is not us.
+ * \since 12.0.0
+ *
+ * \param bridge_channel What to poke.
+ *
+ * \note This function assumes the bridge_channel is locked.
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_poke(struct ast_bridge_channel *bridge_channel);
+
+/*!
  * \brief Feed notification that a frame is waiting on a channel into the bridging core
  *
  * \param bridge The bridge that the notification should influence

Modified: team/group/bridge_construction/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/main/bridging.c?view=diff&rev=382215&r1=382214&r2=382215
==============================================================================
--- team/group/bridge_construction/main/bridging.c (original)
+++ team/group/bridge_construction/main/bridging.c Wed Feb 27 15:26:59 2013
@@ -64,6 +64,8 @@
 #define BRIDGE_ARRAY_GROW 32
 
 static void cleanup_video_mode(struct ast_bridge *bridge);
+static int bridge_make_compatible(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
+static int smart_bridge_operation(struct ast_bridge *bridge);
 
 /*! Default DTMF keys for built in features */
 static char builtin_features_dtmf[AST_BRIDGE_BUILTIN_END][MAXIMUM_DTMF_FEATURE_STRING];
@@ -76,10 +78,12 @@
 
 int __ast_bridge_technology_register(struct ast_bridge_technology *technology, struct ast_module *module)
 {
-	struct ast_bridge_technology *current = NULL;
+	struct ast_bridge_technology *current;
 
 	/* Perform a sanity check to make sure the bridge technology conforms to our needed requirements */
-	if (ast_strlen_zero(technology->name) || !technology->capabilities || !technology->write) {
+	if (ast_strlen_zero(technology->name)
+		|| !technology->capabilities
+		|| !technology->write) {
 		ast_log(LOG_WARNING, "Bridge technology %s failed registration sanity check.\n", technology->name);
 		return -1;
 	}
@@ -110,7 +114,7 @@
 
 int ast_bridge_technology_unregister(struct ast_bridge_technology *technology)
 {
-	struct ast_bridge_technology *current = NULL;
+	struct ast_bridge_technology *current;
 
 	AST_RWLIST_WRLOCK(&bridge_technologies);
 
@@ -129,11 +133,18 @@
 	return current ? 0 : -1;
 }
 
-static void bridge_channel_poke(struct ast_bridge_channel *bridge_channel)
+void ast_bridge_channel_poke(struct ast_bridge_channel *bridge_channel)
+{
+	if (!pthread_equal(pthread_self(), bridge_channel->thread)) {
+		pthread_kill(bridge_channel->thread, SIGURG);
+		ast_cond_signal(&bridge_channel->cond);
+	}
+}
+
+static void bridge_channel_poke_locked(struct ast_bridge_channel *bridge_channel)
 {
 	ao2_lock(bridge_channel);
-	pthread_kill(bridge_channel->thread, SIGURG);
-	ast_cond_signal(&bridge_channel->cond);
+	ast_bridge_channel_poke(bridge_channel);
 	ao2_unlock(bridge_channel);
 }
 
@@ -146,11 +157,7 @@
 	/* Change the state on the bridge channel */
 	bridge_channel->state = new_state;
 
-	/* Only poke the channel's thread if it is not us */
-	if (!pthread_equal(pthread_self(), bridge_channel->thread)) {
-		pthread_kill(bridge_channel->thread, SIGURG);
-		ast_cond_signal(&bridge_channel->cond);
-	}
+	ast_bridge_channel_poke(bridge_channel);
 }
 
 void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state)
@@ -160,7 +167,7 @@
 	ao2_unlock(bridge_channel);
 }
 
-int ast_bridge_channel_queue_action(struct ast_bridge_channel *bridge_channel, struct ast_frame *action)
+int ast_bridge_queue_action(struct ast_bridge *bridge, struct ast_frame *action)
 {
 	struct ast_frame *dup;
 
@@ -169,27 +176,37 @@
 		return -1;
 	}
 
+	ast_debug(1, "BUGBUG Queueing action type:%d sub:%d on bridge %p\n",
+		action->frametype, action->subclass.integer, bridge);
+
+	ao2_lock(bridge);
+	AST_LIST_INSERT_TAIL(&bridge->action_queue, dup, frame_list);
+	bridge->interrupt = 1;
+	ast_bridge_poke(bridge);
+	ao2_unlock(bridge);
+	return 0;
+}
+
+int ast_bridge_channel_queue_action(struct ast_bridge_channel *bridge_channel, struct ast_frame *action)
+{
+	struct ast_frame *dup;
+
+	dup = ast_frdup(action);
+	if (!dup) {
+		return -1;
+	}
+
 	ast_debug(1, "BUGBUG Queueing action type:%d sub:%d on bridge channel %p\n",
 		action->frametype, action->subclass.integer, bridge_channel);
 
 	ao2_lock(bridge_channel);
 	AST_LIST_INSERT_TAIL(&bridge_channel->action_queue, dup, frame_list);
-
-	/* Only poke the channel's thread if it is not us */
-	if (!pthread_equal(pthread_self(), bridge_channel->thread)) {
-		pthread_kill(bridge_channel->thread, SIGURG);
-		ast_cond_signal(&bridge_channel->cond);
-	}
+	ast_bridge_channel_poke(bridge_channel);
 	ao2_unlock(bridge_channel);
 	return 0;
 }
 
-/*!
- * \brief Helper function to poke the bridge thread
- *
- * \note This function assumes the bridge is locked.
- */
-static void bridge_poke(struct ast_bridge *bridge)
+void ast_bridge_poke(struct ast_bridge *bridge)
 {
 	/* Poke the thread just in case */
 	if (bridge->thread != AST_PTHREADT_NULL) {
@@ -212,7 +229,8 @@
 	pthread_t thread;
 
 	bridge->stop = 1;
-	bridge_poke(bridge);
+	bridge->interrupt = 1;
+	ast_bridge_poke(bridge);
 	thread = bridge->thread;
 	bridge->thread = AST_PTHREADT_NULL;
 	ao2_unlock(bridge);
@@ -222,6 +240,32 @@
 }
 
 /*!
+ * \internal
+ * \brief Grow the bridge array size.
+ * \since 12.0.0
+ *
+ * \param bridge Grow the array on this bridge.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int bridge_array_grow(struct ast_bridge *bridge)
+{
+	struct ast_channel **new_array;
+
+	ast_debug(1, "Growing bridge array on %p from %u to %u\n",
+		bridge, bridge->array_size, bridge->array_size + BRIDGE_ARRAY_GROW);
+	new_array = ast_realloc(bridge->array,
+		(bridge->array_size + BRIDGE_ARRAY_GROW) * sizeof(*bridge->array));
+	if (!new_array) {
+		return -1;
+	}
+	bridge->array = new_array;
+	bridge->array_size += BRIDGE_ARRAY_GROW;
+	return 0;
+}
+
+/*!
  * \brief Helper function to add a channel to the bridge array
  *
  * \note This function assumes the bridge is locked.
@@ -230,31 +274,25 @@
 {
 	/* We have to make sure the bridge thread is not using the bridge array before messing with it */
 	while (bridge->waiting) {
-		bridge_poke(bridge);
+		ast_bridge_poke(bridge);
 		sched_yield();
 	}
 
+	/* If this addition cannot be held by the array, grow it or quit. */
+	if (bridge->array_num == bridge->array_size
+		&& bridge_array_grow(bridge)) {
+		return;
+	}
+
 	bridge->array[bridge->array_num++] = chan;
 
-	ast_debug(1, "Added channel %s(%p) to bridge array on %p, new count is %d\n",
-		ast_channel_name(chan), chan, bridge, (int) bridge->array_num);
+	ast_debug(1, "Added channel %s to bridge array on %p, new count is %u\n",
+		ast_channel_name(chan), bridge, bridge->array_num);
 
 	/* If the next addition of a channel will exceed our array size grow it out */
 	if (bridge->array_num == bridge->array_size) {
-		struct ast_channel **new_array;
-
-		ast_debug(1, "Growing bridge array on %p from %d to %d\n",
-			bridge, (int) bridge->array_size, (int) bridge->array_size + BRIDGE_ARRAY_GROW);
-		new_array = ast_realloc(bridge->array,
-			(bridge->array_size + BRIDGE_ARRAY_GROW) * sizeof(*bridge->array));
-		if (!new_array) {
-			return;
-		}
-		bridge->array = new_array;
-		bridge->array_size += BRIDGE_ARRAY_GROW;
-	}
-
-	bridge_poke(bridge);
+		bridge_array_grow(bridge);
+	}
 }
 
 /*!
@@ -264,11 +302,11 @@
  */
 static void bridge_array_remove(struct ast_bridge *bridge, struct ast_channel *chan)
 {
-	int idx;
+	unsigned int idx;
 
 	/* We have to make sure the bridge thread is not using the bridge array before messing with it */
 	while (bridge->waiting) {
-		bridge_poke(bridge);
+		ast_bridge_poke(bridge);
 		sched_yield();
 	}
 
@@ -276,8 +314,8 @@
 		if (bridge->array[idx] == chan) {
 			--bridge->array_num;
 			bridge->array[idx] = bridge->array[bridge->array_num];
-			ast_debug(1, "Removed channel %p from bridge array on %p, new count is %d\n",
-				chan, bridge, (int) bridge->array_num);
+			ast_debug(1, "Removed channel %s from bridge array on %p, new count is %u\n",
+				ast_channel_name(chan), bridge, bridge->array_num);
 			break;
 		}
 	}
@@ -295,6 +333,130 @@
 	}
 
 	return bridge_channel;
+}
+
+/*!
+ * \internal
+ * \brief Pull the bridge channel out of its current bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel to pull.
+ *
+ * \note On entry, the bridge is already locked.
+ *
+ * \return Nothing
+ */
+static void ast_bridge_channel_pull(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge *bridge = bridge_channel->bridge;
+
+	ao2_lock(bridge_channel);
+	if (!bridge_channel->in_bridge) {
+		ao2_unlock(bridge_channel);
+		return;
+	}
+	bridge_channel->in_bridge = 0;
+	ao2_unlock(bridge_channel);
+
+	ast_debug(1, "Pulling bridge channel %p from bridge %p\n", bridge_channel, bridge);
+
+	if (!bridge_channel->just_joined) {
+		/* Tell the bridge technology we are leaving so they tear us down */
+		ast_debug(1, "Giving bridge technology %s notification that %p is leaving bridge %p\n",
+			bridge->technology->name, bridge_channel, bridge);
+		if (bridge->technology->leave) {
+			bridge->technology->leave(bridge, bridge_channel);
+		}
+	}
+
+	/* Remove channel from the bridge */
+	if (!bridge_channel->suspended) {
+		bridge_array_remove(bridge, bridge_channel->chan);
+	}
+	--bridge->num_channels;
+	AST_LIST_REMOVE(&bridge->channels, bridge_channel, entry);
+
+	/* Wake up the bridge to recognize the reconfiguration. */
+	bridge->reconfigured = 1;
+	bridge->interrupt = 1;
+	ast_bridge_poke(bridge);
+}
+
+/*!
+ * \internal
+ * \brief Push the bridge channel into its specified bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel to push.
+ *
+ * \note On entry, the bridge is already locked.
+ *
+ * \return Nothing
+ */
+static void ast_bridge_channel_push(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge *bridge = bridge_channel->bridge;
+	struct ast_channel *swap;
+
+	ao2_lock(bridge_channel);
+	ast_assert(!bridge_channel->in_bridge);
+
+	if (bridge->dissolved) {
+		/* Force out channel being pushed into a dissolved bridge. */
+		switch (bridge_channel->state) {
+		case AST_BRIDGE_CHANNEL_STATE_WAIT:
+			ast_bridge_change_state_nolock(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+			break;
+		default:
+			break;
+		}
+	}
+	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
+		/* Don't push a channel in the process of leaving. */
+		ao2_unlock(bridge_channel);
+		return;
+	}
+
+	bridge_channel->in_bridge = 1;
+	bridge_channel->just_joined = 1;
+	swap = bridge_channel->swap;
+	bridge_channel->swap = NULL;
+	ao2_unlock(bridge_channel);
+
+	if (swap) {
+		struct ast_bridge_channel *bridge_channel2;
+
+		bridge_channel2 = find_bridge_channel(bridge, swap);
+		if (bridge_channel2) {
+			ast_debug(1, "Swapping bridge channel %p out from bridge %p so bridge channel %p can slip in\n",
+				bridge_channel2, bridge, bridge_channel);
+			ao2_lock(bridge_channel2);
+			switch (bridge_channel2->state) {
+			case AST_BRIDGE_CHANNEL_STATE_WAIT:
+				ast_bridge_change_state_nolock(bridge_channel2, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+				break;
+			default:
+				break;
+			}
+			ao2_unlock(bridge_channel2);
+
+			ast_bridge_channel_pull(bridge_channel2);
+		}
+	}
+
+	ast_debug(1, "Pushing bridge channel %p into bridge %p\n", bridge_channel, bridge);
+
+	/* Add channel to the bridge */
+	AST_LIST_INSERT_TAIL(&bridge->channels, bridge_channel, entry);
+	++bridge->num_channels;
+	if (!bridge_channel->suspended) {
+		bridge_array_add(bridge, bridge_channel->chan);
+	}
+
+	/* Wake up the bridge to complete joining the bridge. */
+	bridge->reconfigured = 1;
+	bridge->interrupt = 1;
+	ast_bridge_poke(bridge);
 }
 
 /*!
@@ -527,17 +689,16 @@
 		return;
 	}
 
-	/* If all else fails just poke the bridge */
-	if (bridge->technology->poke && bridge_channel) {
-		bridge->technology->poke(bridge, bridge_channel);
+	/* If all else fails just poke the bridge channel */
+	if (bridge->technology->poke_channel && bridge_channel) {
+		bridge->technology->poke_channel(bridge, bridge_channel);
 		return;
 	}
 }
 
-/*! \brief Generic thread loop, TODO: Rethink this/improve it */
-static int generic_thread_loop(struct ast_bridge *bridge)
-{
-	if (bridge->stop || bridge->refresh || !bridge->array_num) {
+int ast_bridge_thread_generic(struct ast_bridge *bridge)
+{
+	if (bridge->interrupt || !bridge->array_num) {
 		return 0;
 	}
 	for (;;) {
@@ -554,17 +715,88 @@
 		/* Wait on the channels */
 		bridge->waiting = 1;
 		ao2_unlock(bridge);
-		winner = ast_waitfor_n(bridge->array, (int) bridge->array_num, &to);
+		winner = ast_waitfor_n(bridge->array, bridge->array_num, &to);
 		bridge->waiting = 0;
 		ao2_lock(bridge);
 
-		if (bridge->stop || bridge->refresh || !bridge->array_num) {
+		if (bridge->interrupt || !bridge->array_num) {
 			return 0;
 		}
 
 		/* Process whatever they did */
 		ast_bridge_handle_trip(bridge, NULL, winner, -1);
 	}
+}
+
+/*!
+ * \internal
+ * \brief Complete joining new channels to the bridge.
+ * \since 12.0.0
+ *
+ * \param bridge Check for new channels on this bridge.
+ *
+ * \note On entry, bridge is already locked.
+ *
+ * \return Nothing
+ */
+static void bridge_complete_join(struct ast_bridge *bridge)
+{
+	struct ast_bridge_channel *bridge_channel;
+
+	if (bridge->dissolved) {
+		/*
+		 * No sense in completing the join on channels for a dissolved
+		 * bridge.  They are just going to be removed soon anyway.
+		 * However, we do have reason to abort here because the bridge
+		 * technology may not be able to handle the number of channels
+		 * still in the bridge.
+		 */
+		return;
+	}
+
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		if (!bridge_channel->just_joined) {
+			continue;
+		}
+
+		/* Make the channel compatible with the bridge */
+		bridge_make_compatible(bridge, bridge_channel);
+
+		/* Tell the bridge technology we are joining so they set us up */
+		ast_debug(1, "Giving bridge technology %s notification that %p is joining bridge %p\n",
+			bridge->technology->name, bridge_channel, bridge);
+		if (bridge->technology->join
+			&& bridge->technology->join(bridge, bridge_channel)) {
+			ast_debug(1, "Bridge technology %s failed to join %p to bridge %p\n",
+				bridge->technology->name, bridge_channel, bridge);
+		}
+
+		/*
+		 * Poke the bridge channel, this will cause it to wake up and
+		 * execute the proper threading model for the bridge.
+		 */
+		ao2_lock(bridge_channel);
+		bridge_channel->just_joined = 0;
+		ast_bridge_channel_poke(bridge_channel);
+		ao2_unlock(bridge_channel);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Handle bridge action frame.
+ * \since 12.0.0
+ *
+ * \param bridge What to execute the action on.
+ * \param action What to do.
+ *
+ * \note This function assumes the bridge is locked.
+ *
+ * \return Nothing
+ */
+static void bridge_action_bridge(struct ast_bridge *bridge, struct ast_frame *action)
+{
+	/*! \todo BUGBUG bridge_action() not written */
 }
 
 /*!
@@ -577,34 +809,55 @@
 static void *bridge_thread(void *data)
 {
 	struct ast_bridge *bridge = data;
+	struct ast_frame *action;
 	int res = 0;
-
-	ast_debug(1, "Started bridge thread for %p\n", bridge);
-
-	ao2_lock(bridge);
 
 	if (bridge->callid) {
 		ast_callid_threadassoc_add(bridge->callid);
 	}
 
+	ast_debug(1, "Started bridge thread for %p\n", bridge);
+
+	ao2_lock(bridge);
+
 	/* Loop around until we are told to stop */
 	while (!bridge->stop) {
-		if (!bridge->array_num) {
-			/* Wait for a channel to be added to the bridge. */
+		bridge->interrupt = 0;
+
+		if (bridge->reconfigured) {
+			bridge->reconfigured = 0;
+			if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_SMART)
+				&& smart_bridge_operation(bridge)) {
+				/* Smart bridge failed.  Dissolve the bridge. */
+				bridge_force_out_all(bridge);
+				break;
+			}
+			bridge_complete_join(bridge);
+		}
+
+		/* Run a pending bridge action. */
+		action = AST_LIST_REMOVE_HEAD(&bridge->action_queue, frame_list);
+		if (action) {
+			switch (action->frametype) {
+			case AST_FRAME_BRIDGE_ACTION:
+				bridge_action_bridge(bridge, action);
+				break;
+			default:
+				/* Unexpected deferred frame type.  Should never happen. */
+				ast_assert(0);
+				break;
+			}
+			ast_frfree(action);
+			continue;
+		}
+
+		if (!bridge->array_num || !bridge->technology->thread_loop) {
+			/* Wait for something to happen to the bridge. */
 			ast_cond_wait(&bridge->cond, ao2_object_get_lockaddr(bridge));
 			continue;
 		}
 
-		/* In case the refresh bit was set simply set it back to off */
-		bridge->refresh = 0;
-
-		/*
-		 * Execute the appropriate thread function.  If the technology
-		 * does not provide one we use the generic one.
-		 */
-		res = bridge->technology->thread
-			? bridge->technology->thread(bridge)
-			: generic_thread_loop(bridge);
+		res = bridge->technology->thread_loop(bridge);
 		if (res) {
 			/*
 			 * A bridge error occurred.  Sleep and try again later so we
@@ -626,20 +879,24 @@
 /*! \brief Helper function used to find the "best" bridge technology given a specified capabilities */
 static struct ast_bridge_technology *find_best_technology(uint32_t capabilities)
 {
-	struct ast_bridge_technology *current = NULL, *best = NULL;
+	struct ast_bridge_technology *current;
+	struct ast_bridge_technology *best = NULL;
 
 	AST_RWLIST_RDLOCK(&bridge_technologies);
 	AST_RWLIST_TRAVERSE(&bridge_technologies, current, entry) {
 		if (current->suspended) {
-			ast_debug(1, "Bridge technology %s is suspended. Skipping.\n", current->name);
+			ast_debug(1, "Bridge technology %s is suspended. Skipping.\n",
+				current->name);
 			continue;
 		}
 		if (!(current->capabilities & capabilities)) {
-			ast_debug(1, "Bridge technology %s does not have the capabilities we need.\n", current->name);
+			ast_debug(1, "Bridge technology %s does not have the capabilities we need.\n",
+				current->name);
 			continue;
 		}
 		if (best && best->preference < current->preference) {
-			ast_debug(1, "Bridge technology %s has preference %d while %s has preference %d. Skipping.\n", current->name, current->preference, best->name, best->preference);
+			ast_debug(1, "Bridge technology %s has preference %d while %s has preference %d. Skipping.\n",
+				current->name, current->preference, best->name, best->preference);
 			continue;
 		}
 		best = current;
@@ -659,6 +916,7 @@
 static void destroy_bridge(void *obj)
 {
 	struct ast_bridge *bridge = obj;
+	struct ast_frame *action;
 
 	ast_debug(1, "Actually destroying bridge %p, nobody wants it anymore\n", bridge);
 
@@ -676,13 +934,9 @@
 		bridge->callid = ast_callid_unref(bridge->callid);
 	}
 
-	/* Pass off the bridge to the technology to destroy if needed */

[... 738 lines stripped ...]



More information about the asterisk-commits mailing list