[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