[asterisk-commits] file: branch file/bridging r115576 - in /team/file/bridging: include/asterisk...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu May 8 22:32:43 CDT 2008


Author: file
Date: Thu May  8 22:32:43 2008
New Revision: 115576

URL: http://svn.digium.com/view/asterisk?view=rev&rev=115576
Log:
Latest burst of work. This converts the bridging object over to astobj2. Thanks to this plus some logic changes the number of states has now been reduced and the bridge object itself is leaner. This is by no means complete and is in flux.

Modified:
    team/file/bridging/include/asterisk/bridging.h
    team/file/bridging/main/bridging.c

Modified: team/file/bridging/include/asterisk/bridging.h
URL: http://svn.digium.com/view/asterisk/team/file/bridging/include/asterisk/bridging.h?view=diff&rev=115576&r1=115575&r2=115576
==============================================================================
--- team/file/bridging/include/asterisk/bridging.h (original)
+++ team/file/bridging/include/asterisk/bridging.h Thu May  8 22:32:43 2008
@@ -51,11 +51,8 @@
 	AST_BRIDGE_CHANNEL_STATE_END,      /*! Bridged channel ended itself */
 	AST_BRIDGE_CHANNEL_STATE_HANGUP,   /*! Bridge requested that this channel be hungup, unless otherwise instructed */
 	AST_BRIDGE_CHANNEL_STATE_DEPART,   /*! Depart from the bridge */
-	AST_BRIDGE_CHANNEL_STATE_SWAP,     /*! Channel is being swapped out */
 	AST_BRIDGE_CHANNEL_STATE_FEATURE,  /*! Channel is currently executing a feature */
-	AST_BRIDGE_CHANNEL_STATE_MERGE,    /*! Channel is part of a bridge merge operation */
 	AST_BRIDGE_CHANNEL_STATE_DTMF,     /*! A DTMF stream is playing/to be played on this channel */
-	AST_BRIDGE_CHANNEL_STATE_SMART,    /*! Channel is part of a bridge undergoing a smart bridge operation */
 };
 
 /*! \brief Flags used for bridge features */
@@ -121,7 +118,9 @@
 struct ast_bridge_channel {
 	enum ast_bridge_channel_state state;     /*! Current bridged channel state */
 	struct ast_channel *chan;                /*! Asterisk channel participating in this bridge */
+	struct ast_channel *swap;                /*! Asterisk channel we are swapping with */
 	struct ast_bridge *bridge;               /*! Bridge this channel is in */
+	ast_mutex_t lock;                        /*! Lock, to protect this bridge channel structure */
 	ast_cond_t cond;                         /*! Condition, used if we want to wake up a thread waiting on the bridged channel */
 	void *bridge_pvt;                        /*! Private information unique to the bridge technology (not always needed) */
 	pthread_t thread;                        /*! Thread handling the bridged channel */
@@ -133,15 +132,12 @@
 };
 
 struct ast_bridge {
-	ast_mutex_t lock;                                    /*! Lock to protect the bridge */
 	int num;                                             /*! Number of channels involved in the bridge */
 	unsigned int rebuild:1;                              /*! Something outside wants us to rebuild the bridge data */
-	unsigned int smart:1;                                /*! Bridge is undergoing smart bridge operation */
 	struct ast_flags feature_flags;                      /*! Feature flags */
 	struct ast_bridge_technology *technology;            /*! Technology in use on the bridge */
 	void *bridge_pvt;                                    /*! Private information unique to the bridge technology (not always needed) */
 	pthread_t thread;                                    /*! Thread running the bridge */
-	ast_cond_t cond;                                     /*! Condition */
 	struct ast_bridge_features features;                 /*! Enabled features information */
 	AST_LIST_HEAD_NOLOCK(, ast_bridge_channel) channels; /*! Channels participating in this bridge */
 };

Modified: team/file/bridging/main/bridging.c
URL: http://svn.digium.com/view/asterisk/team/file/bridging/main/bridging.c?view=diff&rev=115576&r1=115575&r2=115576
==============================================================================
--- team/file/bridging/main/bridging.c (original)
+++ team/file/bridging/main/bridging.c Thu May  8 22:32:43 2008
@@ -39,6 +39,7 @@
 #include "asterisk/app.h"
 #include "asterisk/file.h"
 #include "asterisk/module.h"
+#include "asterisk/astobj2.h"
 
 static AST_RWLIST_HEAD_STATIC(bridge_technologies, ast_bridge_technology);
 
@@ -164,7 +165,7 @@
 }
 
 /*! \brief Internal function to handle when a channel or bridge needs servicing */
-static void bridge_handle_trip(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_channel *chan, int outfd)
+attribute_unused static void bridge_handle_trip(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_channel *chan, int outfd)
 {
 	/* If no bridge channel has been provided and the actual channel has been provided find it */
 	if (chan && !bridge_channel)
@@ -210,47 +211,47 @@
 	return;
 }
 
-/*! \brief Generic thread loop */
+/*! \brief Generic thread loop, TODO: Rethink this/improve it */
 static int generic_thread_loop(struct ast_bridge *bridge)
 {
-	struct ast_channel *cs[128] = {NULL, }, *winner = NULL;
+	struct ast_channel *cs[128] = { NULL, }, *winner = NULL;
 	int to = -1, count = 0;
 
+	ao2_lock(bridge);
+
 	while (bridge->thread != AST_PTHREADT_STOP) {
-		/* See if we need to rebuild the bridge array information */
+		/* If told to rebuild do so */
 		if (bridge->rebuild) {
 			struct ast_bridge_channel *bridge_channel = NULL;
 			int i = 0;
 
+			/* Go through looking for channels that we should monitor */
 			AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
-				if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT && !bridge_channel->suspended)
+				if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT && !bridge_channel->suspended) {
 					cs[i++] = bridge_channel->chan;
+				}
 			}
-			
-			ast_debug(1, "Rebuild of bridge array on %p went from %d to %d.\n", bridge, count, i);
-			count = i;
+
 			bridge->rebuild = 0;
+
+			/* If no channels exist simply stop the thread, it will get started at a later time */
+			if (!(count = i)) {
+				break;
+			}
 		} else if (count >= 2) {
-			int i = 0;
-			cs[127] = cs[0];
-			for (i = 0; i < (count-1); i++)
-				cs[i] = cs[i+1];
-			cs[count-1] = cs[127];
-		}
-
-		/* If channels are actually present simply wait on them, otherwise wait on the pthread condition to trip */
-		if (count) {
-			ast_mutex_unlock(&bridge->lock);
-			winner = ast_waitfor_n(cs, count, &to);
-			ast_mutex_lock(&bridge->lock);
-		} else {
-			ast_debug(1, "Entering pthread condition wait on %p since we have no channels\n", bridge);
-			ast_cond_wait(&bridge->cond, &bridge->lock);
-		}
-		
-		if (count)
-			bridge_handle_trip(bridge, NULL, winner, -1);
-	}
+			/* Move channels around for priority reasons */
+		}
+
+		/* Wait on the channels */
+		ao2_unlock(bridge);
+		winner = ast_waitfor_n(cs, count, &to);
+		ao2_lock(bridge);
+
+		/* Process whatever they did */
+		bridge_handle_trip(bridge, NULL, winner, -1);
+	}
+
+	ao2_unlock(bridge);
 
 	return 0;
 }
@@ -261,14 +262,7 @@
 	struct ast_bridge *bridge = data;
 	int res = 0;
 
-	ast_mutex_lock(&bridge->lock);
-
-	/* It should never be possible for us to get called without at least one channel in our bridge but just in case... */
-	if (AST_LIST_EMPTY(&bridge->channels)) {
-		ast_log(LOG_ERROR, "We aren't even going to try to start the bridge function for bridge %p, no channels are in it. Something has gone terribly wrong.\n", bridge);
-		ast_mutex_unlock(&bridge->lock);
-		return NULL;
-	}
+	ast_debug(1, "Started bridge thread for %p\n", bridge);
 
 	/* 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));
@@ -276,9 +270,11 @@
 	ast_debug(1, "Ending bridge thread for %p\n", bridge);
 
 	/* Indicate the bridge thread is no longer active */
+	ao2_lock(bridge);
 	bridge->thread = AST_PTHREADT_NULL;
-
-	ast_mutex_unlock(&bridge->lock);
+	ao2_unlock(bridge);
+
+	ao2_ref(bridge, -1);
 
 	return NULL;
 }
@@ -319,6 +315,31 @@
 	return best;
 }
 
+static void destroy_bridge(void *obj)
+{
+	struct ast_bridge *bridge = obj;
+
+	ast_debug(1, "Actually destroying bridge %p, nobody wants it anymore\n", bridge);
+
+	/* Pass off the bridge to the technology to destroy if needed */
+	if (bridge->technology->destroy) {
+		ast_debug(1, "Giving bridge technology %s the bridge structure %p to destroy\n", bridge->technology->name, bridge);
+		if (bridge->technology->destroy(bridge)) {
+			ast_debug(1, "Bridge technology %s failed to destroy bridge structure %p... trying our best\n", bridge->technology->name, bridge);
+		}
+	}
+
+	/* We are no longer using the bridge technology so decrement the module reference count on it */
+	if (bridge->technology->mod) {
+		ast_module_unref(bridge->technology->mod);
+	}
+
+	/* Last but not least clean up the features configuration */
+	ast_bridge_features_cleanup(&bridge->features);
+
+	return;
+}
+
 struct ast_bridge *ast_bridge_new(int capabilities, int flags)
 {
 	struct ast_bridge *bridge = NULL;
@@ -335,12 +356,8 @@
 	}
 
 	/* We have everything we need to create this bridge... so allocate the memory, link things together, and fire her up! */
-	if (!(bridge = ast_calloc(1, sizeof(*bridge))))
+	if (!(bridge = ao2_alloc(sizeof(*bridge), destroy_bridge)))
 		return NULL;
-
-	/* Initialize our bridge lock and condition */
-	ast_mutex_init(&bridge->lock);
-	ast_cond_init(&bridge->cond, NULL);
 
 	bridge->technology = bridge_technology;
 	bridge->thread = AST_PTHREADT_NULL;
@@ -361,62 +378,18 @@
 {
 	struct ast_bridge_channel *bridge_channel = NULL;
 
-	ast_mutex_lock(&bridge->lock);
-
-	/* Drop every bridged channel */
+	ao2_lock(bridge);
+
+	ast_debug(1, "Telling all channels in bridge %p to end and leave the party\n", bridge);
+
+	/* Drop every bridged channel, the last one will cause the bridge thread (if it exists) to exit */
 	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
 		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
 	}
 
-	/* If the thread is still running we have to nudge it to kill itself */
-	if (bridge->thread != AST_PTHREADT_NULL) {
-		pthread_t thread = bridge->thread;
-
-		/* Request that the bridge thread rebuild it's array, this will cause it to see no channels exist and the thread will exit */
-		ast_bridge_rebuild(bridge);
-		/* Change thread pointer to indicate it should stop */
-		bridge->thread = AST_PTHREADT_STOP;
-		/* Give up our lock so that the bridge thread can acquire it */
-		ast_debug(1, "Giving up bridge lock on %p and waiting for thread to exit\n", bridge);
-		ast_mutex_unlock(&bridge->lock);
-		/* Now we basically wait for the thread to realize it should discontinue itself */
-		pthread_join(thread, NULL);
-	} else {
-		/* Thread is not running - we don't have to worry about it */
-		ast_mutex_unlock(&bridge->lock);
-	}
-
-	ast_debug(1, "Waiting for bridge %p to calm down... it currently has %d channels in it\n", bridge, bridge->num);
-
-	/* As each bridged channel removes themselves from the bridge they decrement this value atomically as a last action, so once this value is 0 we can be certain no channel
-	 * is part of the bridge any longer.
-	 */
-	while (bridge->num)
-		usleep(1);
-
-	ast_debug(1, "Proceeding with bridge destruction of %p\n", bridge);
-
-	/* Pass off the bridge to the technology to destroy if needed */
-	if (bridge->technology->destroy) {
-		ast_debug(1, "Giving bridge technology %s the bridge structure %p to destroy\n", bridge->technology->name, bridge);
-		if (bridge->technology->destroy(bridge))
-			ast_debug(1, "Bridge technology %s failed to destroy bridge structure %p... trying our best\n", bridge->technology->name, bridge);
-	}
-
-	/* We are no longer using the bridge technology so decrement it's module reference count */
-	if (bridge->technology->mod) {
-		ast_module_unref(bridge->technology->mod);
-	}
-
-	/* Destroy the mutex that protects the bridge and the condition */
-	ast_mutex_destroy(&bridge->lock);
-	ast_cond_destroy(&bridge->cond);
-
-	/* Before we free the entire bridge we need to pass off our features structure in case hooks were added */
-	ast_bridge_features_cleanup(&bridge->features);
-
-	/* Deallocate the bridge */
-	free(bridge);
+	ao2_unlock(bridge);
+	
+	ao2_ref(bridge, -1);
 
 	return 0;
 }
@@ -441,9 +414,10 @@
 			return -1;
 		}
 		ast_debug(1, "Bridge %p put channel %s into read format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(best_format), best_format);
-	} else
+	} else {
 		ast_debug(1, "Bridge %p is happy that channel %s already has read format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(formats[0]), formats[0]);
-	
+	}
+
 	if (!(bridge->technology->formats & formats[1])) {
 		int best_format = ast_best_codec(bridge->technology->formats);
 		
@@ -459,50 +433,40 @@
 			return -1;
 		}
 		ast_debug(1, "Bridge %p put channel %s into write format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(best_format), best_format);
-	} else
+	} else {
 		ast_debug(1, "Bridge %p is happy that channel %s already has write format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(formats[1]), formats[1]);
-	
+	}
+
 	return 0;
 }
 
 /*! \brief Perform the smart bridge operation. Basically sees if a new bridge technology should be used instead of the current one. */
-static int smart_bridge_operation(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, int count)
+static int smart_bridge_operation(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
 	int new_capabilities = 0;
 	struct ast_bridge_technology *new_technology = NULL, *old_technology = bridge->technology;
-	pthread_t thread = bridge->thread;
 	struct ast_bridge temp_bridge = {
 		.technology = bridge->technology,
 		.bridge_pvt = bridge->bridge_pvt,
 	};
 	struct ast_bridge_channel *bridge_channel2 = NULL;
 
-	/* Don't bother optimizing if the bridge itself is being shutdown */
-	if (bridge->thread == AST_PTHREADT_STOP) {
-		ast_debug(1, "Bridge %p is shutting down, thus we don't need to perform the smart bridge operation.\n", bridge);
-		return -1;
-	}
-
 	/* Based on current feature determine whether we want to change bridge technologies or not */
 	if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX) {
-		/* If the channel count is 2 or less it can stay like this */
-		if (count <= 2) {
-			ast_debug(1, "Bridge %p channel count (%d) is within limits for bridge technology %s, not performing smart bridge operation.\n", bridge, count, bridge->technology->name);
+		if (bridge->num <= 2) {
+			ast_debug(1, "Bridge %p channel count (%d) is within limits for bridge technology %s, not performing smart bridge operation.\n", bridge, bridge->num, bridge->technology->name);
 			return 0;
 		}
-		/* Uh oh... we need to move to multiparty mixing for shizzle */
 		new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX;
 	} else if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) {
-		/* If the channel count is greater than 2 it can stay like this */
-		if (count > 2) {
-			ast_debug(1, "Bridge %p channel count (%d) is within limits for bridge technology %s, not performing smart bridge operation.\n", bridge, count, bridge->technology->name);
+		if (bridge->num > 2) {
+			ast_debug(1, "Bridge %p channel count (%d) is within limits for bridge technology %s, not performing smart bridge operation.\n", bridge, bridge->num, bridge->technology->name);
 			return 0;
 		}
-		/* Yay we can drop down to 1TO1 mixing */
 		new_capabilities = AST_BRIDGE_CAPABILITY_1TO1MIX;
 	}
 
-	/* Now that the above code has determined we need to change to a new technology let's find one */
+	/* Attempt to find a new bridge technology to satisfy the capabilities */
 	if (!(new_technology = find_best_technology(new_capabilities))) {
 		ast_debug(1, "Smart bridge operation was unable to find new bridge technology with capabilities %d to satisfy bridge %p\n", new_capabilities, bridge);
 		return -1;
@@ -510,178 +474,101 @@
 
 	ast_debug(1, "Performing smart bridge operation on bridge %p, moving from bridge technology %s to %s\n", bridge, old_technology->name, new_technology->name);
 
-	/* Mark this bridge as undergoing a smart bridge operation */
-	bridge->smart = 1;
-
-	/* Only try to join the bridge thread if one is running */
+	/* If a thread is currently executing for the current technology tell it to stop */
 	if (bridge->thread != AST_PTHREADT_NULL) {
-		/* We start off by getting the thread servicing the current technology to stop and give control to us */
+		ast_debug(1, "Telling current bridge thread for bridge %p to stop\n", bridge);
+		pthread_kill(bridge->thread, SIGURG);
 		bridge->thread = AST_PTHREADT_STOP;
-		ast_cond_signal(&bridge->cond);
-		pthread_kill(thread, SIGURG);
-		
-		/* Once we let go of the lock the bridge thread should quit and control should be given to us */
-		ast_mutex_unlock(&bridge->lock);
-		
-		/* Of course we have to wait for the thread to exit, that can take some time */
-		ast_debug(1, "Waiting for current bridge thread on %p to exit and control to return to us\n", bridge);
-		pthread_join(thread, NULL);
-		
-		/* Now that it is back to us we have to reacquire the lock so we can muck with things :D */
-		ast_mutex_lock(&bridge->lock);
-	}
-
-	ast_debug(1, "Control of bridge %p now belongs to the smart bridge operation\n", bridge);
-
-	/* Since we have a temporary bridge structure up above we can make the current one clean */
+	}
+
+	/* Since we are soon going to pass this bridge to a new technology we need to NULL out the bridge_pvt pointer but don't worry as it still exists in temp_bridge, ditto for the old technology */
 	bridge->bridge_pvt = NULL;
-	bridge->thread = AST_PTHREADT_NULL;
-
-	/* And setup the new bridge technology now */
 	bridge->technology = new_technology;
-	if (bridge->technology->create) {
-		ast_debug(1, "Giving bridge technology %s the bridge structure %p to setup\n", bridge->technology->name, bridge);
-		if (bridge->technology->create(bridge))
-			ast_debug(1, "Bridge technology %s failed to setup bridge structure %p\n", bridge->technology->name, bridge);
-	}
-
-	/* Our next step is to depart all the channels from one bridge technology and join them with the other */
+
+	/* Pass the bridge to the new bridge technology so it can set it up */
+	if (new_technology->create) {
+		ast_debug(1, "Giving bridge technology %s the bridge structure %p to setup\n", new_technology->name, bridge);
+		if (new_technology->create(bridge)) {
+			ast_debug(1, "Bridge technology %s failed to setup bridge structure %p\n", new_technology->name, bridge);
+		}
+	}
+
+	/* Move existing channels over to the new technology, while taking them away from the old one */
 	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel2, entry) {
-		/* Skip over channel that initiated the smart bridge operation if present */
-		if (bridge_channel && bridge_channel2 == bridge_channel)
+		/* Skip over channel that initiated the smart bridge operation */
+		if (bridge_channel == bridge_channel2) {
 			continue;
+		}
+
 		/* First we part them from the old technology */
 		if (old_technology->leave) {
 			ast_debug(1, "Giving bridge technology %s notification that %p is leaving bridge %p (really %p)\n", old_technology->name, bridge_channel2, &temp_bridge, bridge);
-			if (old_technology->leave(&temp_bridge, bridge_channel2))
+			if (old_technology->leave(&temp_bridge, bridge_channel2)) {
 				ast_debug(1, "Bridge technology %s failed to allow %p (really %p) to leave bridge %p\n", old_technology->name, bridge_channel2, &temp_bridge, bridge);
-		}
-		/* Second we have to make the channel compatible with the bridge */
+			}
+		}
+
+		/* Second we make them compatible again with the bridge */
 		bridge_make_compatible(bridge, bridge_channel2);
-		/* Third we join them to the new one */
+
+		/* Third we join them to the new technology */
 		if (new_technology->join) {
 			ast_debug(1, "Giving bridge technology %s notification that %p is joining bridge %p\n", new_technology->name, bridge_channel2, bridge);
-			if (new_technology->join(bridge, bridge_channel2))
+			if (new_technology->join(bridge, bridge_channel2)) {
 				ast_debug(1, "Bridge technology %s failed to join %p to bridge %p\n", new_technology->name, bridge_channel2, bridge);
-		}
-		/* Fourth we notify the bridge channel so they can call the respective bridge channel thread function */
-		ast_bridge_change_state(bridge_channel2, AST_BRIDGE_CHANNEL_STATE_SMART);
-	}
-
-	/* Now that all the channels are gone the old bridge technology destruction can be finalized */
+			}
+		}
+
+		/* Fourth we tell them to wake up so they become aware that they above has happened */
+		pthread_kill(bridge_channel2->thread, SIGURG);
+		ast_mutex_lock(&bridge_channel2->lock);
+		ast_cond_signal(&bridge_channel2->cond);
+		ast_mutex_unlock(&bridge_channel2->lock);
+	}
+
+	/* Now that all the channels have been moved over we need to get rid of all the information the old technology may have left around */
 	if (old_technology->destroy) {
 		ast_debug(1, "Giving bridge technology %s the bridge structure %p (really %p) to destroy\n", old_technology->name, &temp_bridge, bridge);
-		if (old_technology->destroy(&temp_bridge))
+		if (old_technology->destroy(&temp_bridge)) {
 			ast_debug(1, "Bridge technology %s failed to destroy bridge structure %p (really %p)... some memory may have leaked\n", old_technology->name, &temp_bridge, bridge);
-	}
-
-	/* Since the old technology is no longer being used decrement it's module reference count */
+		}
+	}
+
+	/* Finally if the old technology has module referencing remove our reference, we are no longer going to use it */
 	if (old_technology->mod) {
 		ast_module_unref(old_technology->mod);
 	}
 
-	/* If the new technology needs a thread and we were called when a channel hung up start one up */
-	if (!bridge_channel && (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_THREAD)) {
-		if ((bridge->thread == AST_PTHREADT_NULL) && (ast_pthread_create(&bridge->thread, NULL, bridge_thread, bridge))) {
-			ast_debug(1, "Failed to create bridge thread for %p\n", bridge);
-			bridge->smart = 0;
-			return -1;
-		} else {
-			ast_debug(1, "Poked thread servicing bridge %p\n", bridge);
-			ast_cond_signal(&bridge->cond);
-			pthread_kill(bridge->thread, SIGURG);
-		}
-	}
-
-	bridge->smart = 0;
-
-	return 0;
-}
-
-/*! \brief Run in a multithreaded model. Each joined channel does writing/reading in their own thread. */
-static enum ast_bridge_channel_state bridge_channel_join_multithreaded(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
-{
-	struct ast_bridge_technology *technology = bridge->technology;
-	int fds[4] = {-1, }, nfds = 0, i = 0;
-
-	/* Add any file descriptors to be watched if a callback function exists for them */
-	if (bridge->technology->fd) {
-		for (i = 0; i < 4; i++) {
-			if (bridge_channel->fds[i] >= 0) {
-				fds[nfds] = bridge_channel->fds[i];
-				nfds++;
-			}
-		}
-	}
-
-	/* Go into a loop waiting for frames from the channel, or the associated file descriptors to trip */
-	while (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT && technology == bridge->technology) {
-		struct ast_channel *chan = NULL;
-		int outfd = -1, ms = -1;
-
-		/* If the channel is not suspended wait on it (and the descriptors), if it is suspended wait on the condition */
-		if (!bridge_channel->suspended) {
-			ast_mutex_unlock(&bridge->lock);
-			chan = ast_waitfor_nandfds(&bridge_channel->chan, 1, fds, nfds, NULL, &outfd, &ms);
-			ast_mutex_lock(&bridge->lock);
-		} else {
-			ast_cond_wait(&bridge_channel->cond, &bridge->lock);
-		}
-
-		bridge_handle_trip(bridge, bridge_channel, chan, outfd);
-	}
-
+	return 0;
+}
+
+/*! \brief Run in a multithreaded model. Each joined channel does writing/reading in their own thread. TODO: Improve */
+static enum ast_bridge_channel_state bridge_channel_join_multithreaded(struct ast_bridge_channel *bridge_channel)
+{
 	return bridge_channel->state;
 }
 
-/*! \brief Run in a singlethreaded model. Each joined channel yields itself to the main bridge thread. */
-static enum ast_bridge_channel_state bridge_channel_join_singlethreaded(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
-{
-	struct ast_bridge_technology *technology = bridge->technology;
-
-	/* Go into a loop waiting for the bridge thread to make us do something */
-	while (technology == bridge->technology) {
-		if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
-			ast_debug(1, "Bridge channel %p entering signalling wait state.\n", bridge_channel);
-			ast_cond_wait(&bridge_channel->cond, &bridge->lock);
-		}
-		if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_END) {
-			ast_debug(1, "Bridge channel %p entering end state.\n", bridge_channel);
-			break;
-		} else if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_HANGUP) {
-			ast_debug(1, "Bridge channel %p entering hangup state.\n", bridge_channel);
-			break;
-		} else if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_DEPART) {
-			ast_debug(1, "Bridge channel %p entering depart state.\n", bridge_channel);
-			break;
-		} else if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_SWAP) {
-			ast_debug(1, "Bridge channel %p entering swap state.\n", bridge_channel);
-			break;
-		} else if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_FEATURE) {
-			ast_debug(1, "Bridge channel %p entering feature state.\n", bridge_channel);
-			break;
-		} else if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_DTMF) {
-			ast_debug(1, "Bridge channel %p entering DTMF stream state.\n", bridge_channel);
-			break;
-		} else if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_SMART) {
-			ast_debug(1, "Bridge channel %p entering smart bridge state.\n", bridge_channel);
-			break;
-		}
-	}
+/*! \brief Run in a singlethreaded model. Each joined channel yields itself to the main bridge thread. TODO: Improve */
+static enum ast_bridge_channel_state bridge_channel_join_singlethreaded(struct ast_bridge_channel *bridge_channel)
+{
+	ao2_unlock(bridge_channel->bridge);
+	ast_mutex_lock(&bridge_channel->lock);
+	if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+		ast_cond_wait(&bridge_channel->cond, &bridge_channel->lock);
+	}
+	ast_mutex_unlock(&bridge_channel->lock);
+	ao2_lock(bridge_channel->bridge);
 
 	return bridge_channel->state;
 }
 
 /*! \brief Internal function that executes a feature on a bridge channel */
-static void bridge_channel_feature(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+attribute_unused static void bridge_channel_feature(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
 	struct ast_bridge_features *features = (bridge_channel->features ? bridge_channel->features : &bridge->features);
 	struct ast_bridge_features_hook *hook = NULL;
 	char dtmf[MAXIMUM_DTMF_FEATURE_STRING] = "";
 	int look_for_dtmf = 1, dtmf_len = 0;
-
-	/* Don't bother with the bridge anymore, it's not going to go away */
-	ast_mutex_unlock(&bridge->lock);
 
 	/* The channel is now under our control and we don't really want any begin frames to do our DTMF matching so disable 'em at the core level */
 	ast_set_flag(bridge_channel->chan, AST_FLAG_END_DTMF_ONLY);
@@ -735,135 +622,123 @@
 		bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
 	}
 
-	/* And back into the groove... */
-	ast_mutex_lock(&bridge->lock);
-
 	return;
 }
 
 /*! \brief Internal function that plays back DTMF on a bridge channel */
-static void bridge_channel_dtmf_stream(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+attribute_unused static void bridge_channel_dtmf_stream(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
 	char dtmf_q[8] = "";
 
 	ast_copy_string(dtmf_q, bridge_channel->dtmf_stream_q, sizeof(dtmf_q));
 	bridge_channel->dtmf_stream_q[0] = '\0';
 
-	ast_mutex_unlock(&bridge->lock);
-
 	ast_debug(1, "Playing DTMF stream '%s' out to bridge channel %p\n", dtmf_q, bridge_channel);
 	ast_dtmf_stream(bridge_channel->chan, NULL, dtmf_q, 250, 0);
 
-	ast_mutex_lock(&bridge->lock);
 	bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
 
 	return;
 }
 
 /*! \brief Join a channel to a bridge and handle anything the bridge may want us to do */
-static enum ast_bridge_channel_state bridge_channel_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
-{
-	int formats[2] = {bridge_channel->chan->readformat, bridge_channel->chan->writeformat};
-
-	/* Unsuspend the channel being added to the bridge */
-	bridge_channel->suspended = 0;
-
-	/* Notify the bridge thread that a new bridged channel is part of the bridge, this will cause it to rebuild the bridge array */
-	ast_bridge_rebuild(bridge);
-
-	/* If this channel is part of a swap operation don't bother optimizing or starting a thread, it doesn't matter */
-	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_SWAP) {
-		/* If smart bridging is enabled perform the operation to see if the underlying bridge technology needs to change */
-		if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_SMART))
-			smart_bridge_operation(bridge, bridge_channel, bridge->num);
-		
-		/* Of course if this is the first channel we actually have to create the bridge thread if the technology wants it */
-		if ((bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_THREAD)) {
-			if ((bridge->thread == AST_PTHREADT_NULL) && (ast_pthread_create(&bridge->thread, NULL, bridge_thread, bridge))) {
-				ast_debug(1, "Failed to create bridge thread for %p\n", bridge);
-				return -1;
-			} else {
-				ast_debug(1, "Poked thread servicing bridge %p\n", bridge);
-				/* Poke the bridge out of it's poll if there */
-				ast_cond_signal(&bridge->cond);
-				pthread_kill(bridge->thread, SIGURG);
+static enum ast_bridge_channel_state bridge_channel_join(struct ast_bridge_channel *bridge_channel)
+{
+	int formats[2] = { bridge_channel->chan->readformat, bridge_channel->chan->writeformat };
+	enum ast_bridge_channel_state state;
+
+	/* Record the thread that will be the owner of us */
+	bridge_channel->thread = pthread_self();
+
+	ast_debug(1, "Joining bridge channel %p to bridge %p\n", bridge_channel, bridge_channel->bridge);
+
+	ao2_lock(bridge_channel->bridge);
+
+	state = bridge_channel->state;
+
+	/* Add channel into the bridge */
+	AST_LIST_INSERT_TAIL(&bridge_channel->bridge->channels, bridge_channel, entry);
+	bridge_channel->bridge->num++;
+
+	/* Make the channel compatible with the bridge */
+	bridge_make_compatible(bridge_channel->bridge, bridge_channel);
+
+	if (bridge_channel->swap) {
+		struct ast_bridge_channel *bridge_channel2 = NULL;
+
+		/* If we are performing a swap operation we do not need to execute the smart bridge operation as the actual number of channels involved will not have changed, we just need to tell the other channel to leave */
+		if ((bridge_channel2 = find_bridge_channel(bridge_channel->bridge, bridge_channel->swap))) {
+			ast_debug(1, "Swapping bridge channel %p out from bridge %p so bridge channel %p can slip in\n", bridge_channel2, bridge_channel->bridge, bridge_channel);
+			ast_bridge_change_state(bridge_channel2, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+		}
+
+		bridge_channel->swap = NULL;
+	} else if (ast_test_flag(&bridge_channel->bridge->feature_flags, AST_BRIDGE_FLAG_SMART)) {
+		/* Perform the smart bridge operation, basically see if we need to move around between technologies */
+		smart_bridge_operation(bridge_channel->bridge, bridge_channel);
+	}
+	
+	/* Tell the bridge technology we are joining so they set us up */
+	if (bridge_channel->bridge->technology->join) {
+		ast_debug(1, "Giving bridge technology %s notification that %p is joining bridge %p\n", bridge_channel->bridge->technology->name, bridge_channel, bridge_channel->bridge);
+		if (bridge_channel->bridge->technology->join(bridge_channel->bridge, bridge_channel)) {
+			ast_debug(1, "Bridge technology %s failed to join %p to bridge %p\n", bridge_channel->bridge->technology->name, bridge_channel, bridge_channel->bridge);
+		}
+	}
+	
+	/* Tell the bridge to rebuild as we are joining in */
+	ast_bridge_rebuild(bridge_channel->bridge);
+
+	/* Actually execute the respective threading model, and keep our bridge thread alive */
+	while (state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+		/* If the technology requires a thread and one is not running, start it up */
+		if (bridge_channel->bridge->thread == AST_PTHREADT_NULL && (bridge_channel->bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_THREAD)) {
+			ast_debug(1, "Starting a bridge thread for bridge %p\n", bridge_channel->bridge);
+			ao2_ref(bridge_channel->bridge, +1);
+			if (ast_pthread_create(&bridge_channel->bridge->thread, NULL, bridge_thread, bridge_channel->bridge)) {
+				ast_debug(1, "Failed to create a bridge thread for bridge %p, giving it another go.\n", bridge_channel->bridge);
+				ao2_ref(bridge_channel->bridge, -1);
+				continue;
 			}
 		}
-	}
-
-	/* Make sure that the channel's formats are compatible before joining */
-	bridge_make_compatible(bridge, bridge_channel);
-
-	/* If the bridge technology wants notification that this channel is joining the bridge, give it it */
-	if (bridge->technology->join) {
-		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, bridge_channel))
-			ast_debug(1, "Bridge technology %s failed to join %p to bridge %p\n", bridge->technology->name, bridge_channel, bridge);
-	}
-
-	/* Before entering ensure the state is set to wait to begin with */
-	bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
-
-	/* Pass ourselves off to our respective threading model */
-	while (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
-		if ((bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTITHREADED))
-			bridge_channel_join_multithreaded(bridge, bridge_channel);
-		else
-			bridge_channel_join_singlethreaded(bridge, bridge_channel);
-		/* If the above exited because of a merge operation update our own reference to the new bridge and go back to waiting */
-		if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_MERGE) {
-			struct ast_bridge *old_bridge = bridge;
-			/* This is sort of crazy but basically the old bridge can't go away yet and we still have a lock on it, we can therefore update our own reference to point to the new bridge, give up the lock on the old bridge, and decrement the channel count on the old bridge. Once every other channel does this as well the old bridge can be destroyed and all will be well. */
-			bridge = bridge_channel->chan->bridge;
-			ast_mutex_unlock(&old_bridge->lock);
-			ast_atomic_fetchadd_int(&old_bridge->num, -1);
-			ast_mutex_lock(&bridge->lock);
-			bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
-		} else if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_FEATURE) {
-			bridge_channel_feature(bridge, bridge_channel);
-			/* Just in case... tell the bridge thread to rebuild */
-			ast_bridge_rebuild(bridge);
-		} else if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_DTMF) {
-			bridge_channel_dtmf_stream(bridge, bridge_channel);
-			ast_bridge_rebuild(bridge);
-		} 
-		if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_SMART) {
-			bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
-			ast_bridge_rebuild(bridge);
-		}
-	}
-
-	/* If the bridge technology wants notification that this channel is leaving the bridge, give it it */
-	if (bridge->technology->leave) {
-		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, bridge_channel))
-			ast_debug(1, "Bridge technology %s failed to allow %p to leave bridge %p\n", bridge->technology->name, bridge_channel, bridge);
-	}
-
-	/* If this channel is exiting the bridge in a swap state then we need not remove them or perform the smart bridge operation, it doesn't matter */
-	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_SWAP) {
-		/* Remove ourselves from the bridge */
-		AST_LIST_REMOVE(&bridge->channels, bridge_channel, entry);
-		
-		/* And for my last trick... perform the smart bridge operation yet again */
-		if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_SMART))
-			smart_bridge_operation(bridge, NULL, bridge->num-1);
-	}
-
-	/* Queue a rebuild on the bridge since we left */
-	ast_bridge_rebuild(bridge);
-
-	/* Restore original formats if need be */
+		/* Execute the threading model */
+		state = (bridge_channel->bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTITHREADED ? bridge_channel_join_multithreaded(bridge_channel) : bridge_channel_join_singlethreaded(bridge_channel));
+	}
+
+	/* Tell the bridge to rebuild as we are leaving */
+	ast_bridge_rebuild(bridge_channel->bridge);
+
+	/* Tell the bridge technology we are leaving so they tear us down */
+	if (bridge_channel->bridge->technology->leave) {
+		ast_debug(1, "Giving bridge technology %s notification that %p is leaving bridge %p\n", bridge_channel->bridge->technology->name, bridge_channel, bridge_channel->bridge);
+		if (bridge_channel->bridge->technology->leave(bridge_channel->bridge, bridge_channel)) {
+			ast_debug(1, "Bridge technology %s failed to leave %p from bridge %p\n", bridge_channel->bridge->technology->name, bridge_channel, bridge_channel->bridge);
+		}
+	}
+
+	/* Remove channel from the bridge */
+	bridge_channel->bridge->num--;
+	AST_LIST_REMOVE(&bridge_channel->bridge->channels, bridge_channel, entry);
+
+	/* Perform the smart bridge operation if needed since a channel has left */
+	if (ast_test_flag(&bridge_channel->bridge->feature_flags, AST_BRIDGE_FLAG_SMART)) {
+		smart_bridge_operation(bridge_channel->bridge, NULL);
+	}
+
+	ao2_unlock(bridge_channel->bridge);
+
+	/* Restore original formats of the channel as they came in */
 	if (bridge_channel->chan->readformat != formats[0]) {
 		ast_debug(1, "Bridge is returning %p to read format %s(%d)\n", bridge_channel, ast_getformatname(formats[0]), formats[0]);
-		if (ast_set_read_format(bridge_channel->chan, formats[0]))
-			ast_log(LOG_WARNING, "Failed to return channel %s to read format %s(%d)\n", bridge_channel->chan->name, ast_getformatname(formats[0]), formats[0]);
-	}
-
+		if (ast_set_read_format(bridge_channel->chan, formats[0])) {
+			ast_debug(1, "Bridge failed to return channel %p to read format %s(%d)\n", bridge_channel, ast_getformatname(formats[0]), formats[0]);
+		}
+	}
 	if (bridge_channel->chan->writeformat != formats[1]) {
 		ast_debug(1, "Bridge is returning %p to write format %s(%d)\n", bridge_channel, ast_getformatname(formats[1]), formats[1]);
-		if (ast_set_write_format(bridge_channel->chan, formats[1]))
-			ast_log(LOG_WARNING, "Failed to return channel %s to write format %s(%d)\n", bridge_channel->chan->name, ast_getformatname(formats[1]), formats[1]);
+		if (ast_set_write_format(bridge_channel->chan, formats[1])) {
+			ast_debug(1, "Bridge failed to return channel %p to write format %s(%d)\n", bridge_channel, ast_getformatname(formats[1]), formats[1]);
+		}
 	}
 
 	return bridge_channel->state;
@@ -871,123 +746,50 @@
 
 enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features)
 {
-	struct ast_bridge_channel bridge_channel;
+	struct ast_bridge_channel bridge_channel = {
+		.chan = chan,
+		.swap = swap,
+		.bridge = bridge,
+		.features = features,
+	};
 	enum ast_bridge_channel_state state;
-	int i = 0;
-
-	/* Wipe out the bridge channel structure just in case */
-	memset(&bridge_channel, 0, sizeof(bridge_channel));
-
-	/* Ensure no additional file descriptors are present */
-	for (i = 0; i < 4; i++)
-		bridge_channel.fds[i] = -1;
-

[... 508 lines stripped ...]



More information about the asterisk-commits mailing list