[asterisk-commits] file: branch file/bridging-phase2 r178021 - in /team/file/bridging-phase2: br...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Feb 23 09:27:06 CST 2009


Author: file
Date: Mon Feb 23 09:27:05 2009
New Revision: 178021

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=178021
Log:
Add support for interval hooks (the lower level portion required for bridge warnings and bridge lifetime control) and make the bridging core aware of native bridges. Any bridging technology that has the native bridge capability and the proper compatible callback will be considered when a bridge technology can change. It will get preference over any other bridge technology and can be used for both 2 party bridges and multiple channel bridges.

Modified:
    team/file/bridging-phase2/bridges/bridge_multiplexed.c
    team/file/bridging-phase2/include/asterisk/bridging.h
    team/file/bridging-phase2/include/asterisk/bridging_features.h
    team/file/bridging-phase2/main/bridging.c

Modified: team/file/bridging-phase2/bridges/bridge_multiplexed.c
URL: http://svn.digium.com/svn-view/asterisk/team/file/bridging-phase2/bridges/bridge_multiplexed.c?view=diff&rev=178021&r1=178020&r2=178021
==============================================================================
--- team/file/bridging-phase2/bridges/bridge_multiplexed.c (original)
+++ team/file/bridging-phase2/bridges/bridge_multiplexed.c Mon Feb 23 09:27:05 2009
@@ -59,12 +59,52 @@
 	unsigned int count;
 	/*! Number of channels actually being serviced by this thread */
 	unsigned int service_count;
+	/*! Timeout value to use for monitoring thread */
+	int timeout;
 	/*! Bit used to indicate that the thread is waiting on channels */
 	unsigned int waiting:1;
 };
 
 /*! \brief Container of all operating multiplexed threads */
 static struct ao2_container *multiplexed_threads;
+
+/*! \brief Helper function to update the timeout value of a multiplexed thread structure */
+static void multiplexed_thread_update_timeout(struct multiplexed_thread *multiplexed_thread, struct ast_bridge *bridge)
+{
+	if (!bridge) {
+		int i;
+
+		multiplexed_thread->timeout = -1;
+
+		for (i = 0; i < MULTIPLEXED_MAX_CHANNELS && multiplexed_thread->chans[i]; i += 2) {
+			int timeout = multiplexed_thread->chans[i]->bridge->timeout;
+
+			if ((timeout >= 0) && ((timeout < multiplexed_thread->timeout) || (multiplexed_thread->timeout == -1))) {
+				multiplexed_thread->timeout = timeout;
+			}
+		}
+	} else {
+		int timeout = bridge->timeout;
+
+		if ((timeout >= 0) && ((timeout < multiplexed_thread->timeout) || (multiplexed_thread->timeout == -1))) {
+			multiplexed_thread->timeout = timeout;
+		}
+	}
+
+	return;
+}
+
+/*! \brief Helper function which triggers upon timeout */
+static void multiplexed_thread_trigger_timeout(struct multiplexed_thread *multiplexed_thread)
+{
+	int i;
+
+	for (i = 0; i < MULTIPLEXED_MAX_CHANNELS && multiplexed_thread->chans[i]; i += 2) {
+		ast_bridge_handle_trip(multiplexed_thread->chans[i]->bridge, NULL, NULL, -1);
+	}
+
+	return;
+}
 
 /*! \brief Callback function for finding a free multiplexed thread */
 static int find_multiplexed_thread(void *obj, void *arg, int flags)
@@ -88,6 +128,8 @@
 			ao2_unlock(multiplexed_threads);
 			return -1;
 		}
+		/* Default timeout is of course no timeout */
+		multiplexed_thread->timeout = -1;
 		/* Set up default parameters */
 		multiplexed_thread->thread = AST_PTHREADT_NULL;
 		/* Finally link us into the container so others may find us */
@@ -156,7 +198,6 @@
 
 	while (multiplexed_thread->thread != AST_PTHREADT_STOP) {
 		struct ast_channel *winner = NULL, *first = multiplexed_thread->chans[0];
-		int to = -1;
 
 		/* Move channels around so not just the first one gets priority */
 		memmove(multiplexed_thread->chans, multiplexed_thread->chans + 1, sizeof(struct ast_channel *) * (multiplexed_thread->service_count - 1));
@@ -164,10 +205,15 @@
 
 		ao2_unlock(multiplexed_thread);
 		multiplexed_thread->waiting = 1;
-		winner = ast_waitfor_n(multiplexed_thread->chans, multiplexed_thread->service_count, &to);
+		winner = ast_waitfor_n(multiplexed_thread->chans, multiplexed_thread->service_count, &multiplexed_thread->timeout);
 		multiplexed_thread->waiting = 0;
 		ao2_lock(multiplexed_thread);
 
+		if (!multiplexed_thread->timeout) {
+			multiplexed_thread_trigger_timeout(multiplexed_thread);
+			multiplexed_thread_update_timeout(multiplexed_thread, NULL);
+		}
+
 		if (winner && winner->bridge) {
 			ast_bridge_handle_trip(winner->bridge, NULL, winner, -1);
 		}
@@ -184,7 +230,7 @@
 }
 
 /*! \brief Helper function which adds or removes a channel and nudges the thread */
-static void multiplexed_add_or_remove(struct multiplexed_thread *multiplexed_thread, struct ast_channel *chan, int add)
+static void multiplexed_add_or_remove(struct multiplexed_thread *multiplexed_thread, struct ast_channel *chan, int add, struct ast_bridge *bridge)
 {
 	int i, removed = 0;
 	pthread_t thread = AST_PTHREADT_NULL;
@@ -210,6 +256,7 @@
 
 	if (multiplexed_thread->service_count && multiplexed_thread->thread == AST_PTHREADT_NULL) {
 		ao2_ref(multiplexed_thread, +1);
+		multiplexed_thread_update_timeout(multiplexed_thread, bridge);
 		if (ast_pthread_create(&multiplexed_thread->thread, NULL, multiplexed_thread_function, multiplexed_thread)) {
 			ao2_ref(multiplexed_thread, -1);
 			ast_debug(1, "Failed to create an actual thread for multiplexed thread '%p', trying next time\n", multiplexed_thread);
@@ -218,6 +265,7 @@
 		thread = multiplexed_thread->thread;
 		multiplexed_thread->thread = AST_PTHREADT_STOP;
 	} else if (!add && removed) {
+		multiplexed_thread_update_timeout(multiplexed_thread, NULL);
 		memmove(multiplexed_thread->chans + i, multiplexed_thread->chans + i + 1, sizeof(struct ast_channel *) * (MULTIPLEXED_MAX_CHANNELS - (i + 1)));
 	}
 
@@ -238,7 +286,7 @@
 
 	ast_debug(1, "Adding channel '%s' to multiplexed thread '%p' for monitoring\n", bridge_channel->chan->name, multiplexed_thread);
 
-	multiplexed_add_or_remove(multiplexed_thread, bridge_channel->chan, 1);
+	multiplexed_add_or_remove(multiplexed_thread, bridge_channel->chan, 1, bridge);
 
 	/* If the second channel has not yet joined do not make things compatible */
 	if (c0 == c1) {
@@ -259,7 +307,7 @@
 
 	ast_debug(1, "Removing channel '%s' from multiplexed thread '%p'\n", bridge_channel->chan->name, multiplexed_thread);
 
-	multiplexed_add_or_remove(multiplexed_thread, bridge_channel->chan, 0);
+	multiplexed_add_or_remove(multiplexed_thread, bridge_channel->chan, 0, bridge);
 
 	return 0;
 }
@@ -271,7 +319,7 @@
 
 	ast_debug(1, "Suspending channel '%s' from multiplexed thread '%p'\n", bridge_channel->chan->name, multiplexed_thread);
 
-	multiplexed_add_or_remove(multiplexed_thread, bridge_channel->chan, 0);
+	multiplexed_add_or_remove(multiplexed_thread, bridge_channel->chan, 0, NULL);
 
 	return;
 }
@@ -283,7 +331,7 @@
 
 	ast_debug(1, "Unsuspending channel '%s' from multiplexed thread '%p'\n", bridge_channel->chan->name, multiplexed_thread);
 
-	multiplexed_add_or_remove(multiplexed_thread, bridge_channel->chan, 1);
+	multiplexed_add_or_remove(multiplexed_thread, bridge_channel->chan, 1, bridge);
 
 	return;
 }

Modified: team/file/bridging-phase2/include/asterisk/bridging.h
URL: http://svn.digium.com/svn-view/asterisk/team/file/bridging-phase2/include/asterisk/bridging.h?view=diff&rev=178021&r1=178020&r2=178021
==============================================================================
--- team/file/bridging-phase2/include/asterisk/bridging.h (original)
+++ team/file/bridging-phase2/include/asterisk/bridging.h Mon Feb 23 09:27:05 2009
@@ -96,6 +96,8 @@
 	AST_BRIDGE_CHANNEL_STATE_FEATURE,
 	/*! Bridged channel is sending a DTMF stream out */
 	AST_BRIDGE_CHANNEL_STATE_DTMF,
+	/*! Bridged channel is executing an interval hook */
+	AST_BRIDGE_CHANNEL_STATE_INTERVAL,
 };
 
 /*! \brief Return values for bridge technology write function */
@@ -149,6 +151,10 @@
 struct ast_bridge {
 	/*! Number of channels participating in the bridge */
 	int num;
+	/*! Timeout when polling for channels in this bridge */
+	int timeout;
+	/*! Requested capabilities when bridge was created */
+	int capabilities;
 	/*! Bit to indicate that the bridge thread is waiting on channels in the bridge array */
 	unsigned int waiting:1;
 	/*! Bit to indicate the bridge thread should stop */
@@ -418,7 +424,7 @@
  *       make sure the channel either hangs up or returns to the bridge.
  */
 void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state);
-
+	
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif

Modified: team/file/bridging-phase2/include/asterisk/bridging_features.h
URL: http://svn.digium.com/svn-view/asterisk/team/file/bridging-phase2/include/asterisk/bridging_features.h?view=diff&rev=178021&r1=178020&r2=178021
==============================================================================
--- team/file/bridging-phase2/include/asterisk/bridging_features.h (original)
+++ team/file/bridging-phase2/include/asterisk/bridging_features.h Mon Feb 23 09:27:05 2009
@@ -72,8 +72,14 @@
  * \brief Structure that is the essence of a features hook
  */
 struct ast_bridge_features_hook {
-	/*! DTMF String that is examined during a feature hook lookup */
-	char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
+	union {
+		/*! DTMF String that is examined during a feature hook lookup */
+		char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
+		/*! Interval that the feature hook should execute at */
+		unsigned int interval;
+	};
+	/*! Time at which the interval should actually trip */
+	struct timeval interval_trip_time;
 	/*! Callback that is called when DTMF string is matched */
 	ast_bridge_features_hook_callback callback;
 	/*! Unique data that was passed into us */
@@ -88,6 +94,8 @@
 struct ast_bridge_features {
 	/*! Attached DTMF based feature hooks */
 	AST_LIST_HEAD_NOLOCK(, ast_bridge_features_hook) hooks;
+	/*! Attached Interval based feature hooks */
+	AST_LIST_HEAD_NOLOCK(, ast_bridge_features_hook) interval_hooks;
 	/*! Feature flags that are enabled */
 	struct ast_flags feature_flags;
 	/*! Bit to indicate that this structure is useful and should be considered when looking for features */
@@ -182,6 +190,53 @@
  */
 int ast_bridge_features_hook(struct ast_bridge_features *features, const char *dtmf, ast_bridge_features_hook_callback callback, void *hook_pvt);
 
+/*! \brief Attach a custom interval hook to a bridge features structure
+ *
+ * \param features Bridge features structure
+ * \param interval The interval that the hook should execute at
+ * \param callback Function to execute upon activation
+ * \param hook_pvt Unique data
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features features;
+ * ast_bridge_features_init(&features);
+ * ast_bridge_features_interval_hook(&features, 1000, playback_callback, NULL);
+ * \endcode
+ *
+ * This makes the bridging core call playback_callback every second. A pointer to useful
+ * data may be provided to the hook_pvt parameter.
+ *
+ * \note It is important that the callback set the bridge channel state back to
+ *       AST_BRIDGE_CHANNEL_STATE_WAIT or the bridge thread will not service the channel.
+ */
+int ast_bridge_features_interval_hook(struct ast_bridge_features *features, unsigned int interval, ast_bridge_features_hook_callback callback, void *hook_pvt);	
+
+/*! \brief Update the interval on an interval hook that is currently executing a callback
+ *
+ * \param bridge_channel The bridge channel that is executing the callback
+ * \param interval The new interval value or 0 to remove the interval hook
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_features_interval_update(bridge_channel, 10000);
+ * \endcode
+ *
+ * This updates the executing interval hook so that it will be triggered next in 10 seconds.
+ *
+ * \note This can only be called from the context of the interval hook callback itself. If this
+ *       is called outside the callback behaviour is undefined.
+ */
+int ast_bridge_features_interval_update(struct ast_bridge_channel *bridge_channel, unsigned int interval);
+
 /*! \brief Enable a built in feature on a bridge features structure
  *
  * \param features Bridge features structure

Modified: team/file/bridging-phase2/main/bridging.c
URL: http://svn.digium.com/svn-view/asterisk/team/file/bridging-phase2/main/bridging.c?view=diff&rev=178021&r1=178020&r2=178021
==============================================================================
--- team/file/bridging-phase2/main/bridging.c (original)
+++ team/file/bridging-phase2/main/bridging.c Mon Feb 23 09:27:05 2009
@@ -66,6 +66,12 @@
 		return -1;
 	}
 
+	/* Native based bridging technologies are required to have a compatible callback, it would be silly not to have one */
+	if ((technology->capabilities & AST_BRIDGE_CAPABILITY_NATIVE) && !technology->compatible) {
+		ast_log(LOG_WARNING, "Bridge technology %s failed for native bridge registration sanity check.\n", technology->name);
+		return -1;
+	}
+
 	AST_RWLIST_WRLOCK(&bridge_technologies);
 
 	/* Look for duplicate bridge technology already using this name, or already registered */
@@ -250,6 +256,28 @@
 	}
 
 	return frame;
+}
+
+static void bridge_handle_timeout(struct ast_bridge *bridge)
+{
+	struct ast_bridge_channel *bridge_channel;
+
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		if (!bridge_channel->features || !bridge_channel->features->usable || AST_LIST_EMPTY(&bridge_channel->features->interval_hooks)) {
+			continue;
+		}
+
+		if (ast_tvdiff_ms(AST_LIST_FIRST(&bridge_channel->features->interval_hooks)->interval_trip_time, ast_tvnow()) > 0) {
+			continue;
+		}
+
+		ast_debug(1, "Disabling timeout on bridge '%p' until we can go\n", bridge);
+		bridge->timeout = -1;
+
+		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_INTERVAL);
+	}
+
+	return;
 }
 
 void ast_bridge_handle_trip(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_channel *chan, int outfd)
@@ -283,6 +311,8 @@
 			ast_frfree(frame);
 		}
 		return;
+	} else if (!bridge_channel && !chan && outfd == -1) {
+		bridge_handle_timeout(bridge);
 	}
 
 	/* If a file descriptor actually tripped pass it off to the bridge technology */
@@ -300,12 +330,48 @@
 	return;
 }
 
+static int bridge_channel_timeout(struct ast_bridge_channel *bridge_channel)
+{
+	int timeout = -1;
+	struct ast_bridge_features_hook *hook;
+
+	if (!bridge_channel->features || !bridge_channel->features->usable) {
+		return -1;
+	}
+
+	AST_LIST_TRAVERSE(&bridge_channel->features->interval_hooks, hook, entry) {
+		int channel_timeout = ast_tvdiff_ms(hook->interval_trip_time, ast_tvnow());
+
+		if (channel_timeout > 0) {
+			timeout = channel_timeout;
+			break;
+		}
+	}
+
+	return timeout;
+}
+
+static int bridge_timeout(struct ast_bridge *bridge)
+{
+	int timeout = -1;
+	struct ast_bridge_channel *bridge_channel;
+
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		int channel_timeout = bridge_channel_timeout(bridge_channel);
+
+		if (channel_timeout >= 0 && (channel_timeout < timeout || timeout == -1)) {
+			timeout = channel_timeout;
+		}
+	}
+
+	return timeout;
+}
+
 /*! \brief Generic thread loop, TODO: Rethink this/improve it */
 static int generic_thread_loop(struct ast_bridge *bridge)
 {
 	while (!bridge->stop && !bridge->refresh && bridge->array_num) {
 		struct ast_channel *winner = NULL;
-		int to = -1;
 
 		/* Move channels around for priority reasons if we have more than one channel in our array */
 		if (bridge->array_num > 1) {
@@ -317,7 +383,7 @@
 		/* Wait on the channels */
 		ao2_unlock(bridge);
 		bridge->waiting = 1;
-		winner = ast_waitfor_n(bridge->array, (int)bridge->array_num, &to);
+		winner = ast_waitfor_n(bridge->array, (int)bridge->array_num, &bridge->timeout);
 		bridge->waiting = 0;
 		ao2_lock(bridge);
 
@@ -360,8 +426,22 @@
 	return NULL;
 }
 
+/*! \brief Helper function used to determine if the participants in a bridge are compatible with a native bridge technology */
+static int native_bridge_technology_test(struct ast_bridge_technology *technology, struct ast_bridge *bridge)
+{
+	struct ast_bridge_channel *bridge_channel;
+
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		if (!technology->compatible(bridge_channel)) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
 /*! \brief Helper function used to find the "best" bridge technology given a specified capabilities */
-static struct ast_bridge_technology *find_best_technology(int capabilities)
+static struct ast_bridge_technology *find_best_technology(int capabilities, struct ast_bridge *bridge)
 {
 	struct ast_bridge_technology *current = NULL, *best = NULL;
 
@@ -372,9 +452,13 @@
 			ast_debug(1, "Bridge technology %s is suspended. Skipping.\n", current->name);
 			continue;
 		}
-		if (!(current->capabilities & capabilities)) {
+		if (!(current->capabilities & (capabilities & ~AST_BRIDGE_CAPABILITY_NATIVE))) {
 			ast_debug(1, "Bridge technology %s does not have the capabilities we need.\n", current->name);
 			continue;
+		}
+		if (bridge && (current->capabilities & AST_BRIDGE_CAPABILITY_NATIVE) && (capabilities & AST_BRIDGE_CAPABILITY_NATIVE) && native_bridge_technology_test(current, bridge)) {
+			best = current;
+			break;
 		}
 		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);
@@ -440,9 +524,12 @@
 		ast_bridge_destroy(other_bridge);
 	}
 
-	/* If capabilities were provided use our helper function to find the "best" bridge technology, otherwise we can
-	 * just look for the most basic capability needed, single 1to1 mixing. */
-	bridge_technology = (capabilities ? find_best_technology(capabilities) : find_best_technology(AST_BRIDGE_CAPABILITY_1TO1MIX));
+	/* If no capabilities were provided assume they just want a simple 1to1mix bridge with nothing fancy */
+	if (!capabilities) {
+		capabilities = AST_BRIDGE_CAPABILITY_1TO1MIX;
+	}
+
+	bridge_technology = find_best_technology(capabilities, NULL);
 
 	/* If no bridge technology was found we can't possibly do bridging so fail creation of the bridge */
 	if (!bridge_technology) {
@@ -457,6 +544,8 @@
 
 	bridge->technology = bridge_technology;
 	bridge->thread = AST_PTHREADT_NULL;
+	bridge->timeout = -1;
+	bridge->capabilities = capabilities;
 
 	/* Create an array of pointers for the channels that will be joining us */
 	bridge->array = ast_calloc(BRIDGE_ARRAY_START, sizeof(struct ast_channel*));
@@ -481,7 +570,7 @@
 {
 	struct ast_bridge_technology *bridge_technology = NULL;
 
-	if (!(bridge_technology = find_best_technology(capabilities))) {
+	if (!(bridge_technology = find_best_technology(capabilities, NULL))) {
 		return 0;
 	}
 
@@ -571,30 +660,36 @@
 	};
 	struct ast_bridge_channel *bridge_channel2 = NULL;
 
-	/* Based on current feature determine whether we want to change bridge technologies or not */
-	if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX) {
-		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);
-			return 0;
-		}
-		new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX;
-	} else if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) {
-		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);
-			return 0;
-		}
-		new_capabilities = AST_BRIDGE_CAPABILITY_1TO1MIX;
-	}
-
-	if (!new_capabilities) {
-		ast_debug(1, "Bridge '%p' has no new capabilities, not performing smart bridge operation.\n", bridge);
+	/* If no channels are present then moving between technologies is just silly */
+	if (!count) {
+		ast_debug(1, "Smart bridge operation aborted on bridge %p since no channels exist\n", bridge);
+		return -1;
+	}
+
+	new_capabilities = (bridge->capabilities & ~(AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_MULTIMIX));
+
+	if (count == 1) {
+		new_capabilities &= ~AST_BRIDGE_CAPABILITY_NATIVE;
+		new_capabilities |= AST_BRIDGE_CAPABILITY_1TO1MIX;
+	} else if (count == 2) {
+		new_capabilities |= AST_BRIDGE_CAPABILITY_1TO1MIX;
+	} else {
+		new_capabilities |= AST_BRIDGE_CAPABILITY_MULTIMIX;
+	}
+
+	/* Attempt to find a new bridge technology to satisfy the capabilities */
+	if (!(new_technology = find_best_technology(new_capabilities, bridge))) {
+		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;
+	}
+
+	/* Moving from the technology we are currently using to itself is just silly */
+	if (bridge->technology == new_technology) {
+		ast_debug(1, "Smart bridge operation aborted on bridge %p since we would move to the same technology\n", bridge);
+		if (new_technology->mod) {
+			ast_module_unref(new_technology->mod);
+		}
 		return 0;
-	}
-
-	/* 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;
 	}
 
 	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);
@@ -676,7 +771,7 @@
 /*! \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)
 {
-	int fds[4] = { -1, }, nfds = 0, i = 0, outfd = -1, ms = -1;
+	int fds[4] = { -1, }, nfds = 0, i = 0, outfd = -1, to = bridge_channel_timeout(bridge_channel);
 	struct ast_channel *chan = NULL;
 
 	/* Add any file descriptors we may want to monitor */
@@ -693,7 +788,7 @@
 	/* Wait for data to either come from the channel or us to be signalled */
 	if (!bridge_channel->suspended) {
 		ast_debug(1, "Going into a multithreaded waitfor for bridge channel %p of bridge %p\n", bridge_channel, bridge_channel->bridge);
-		chan = ast_waitfor_nandfds(&bridge_channel->chan, 1, fds, nfds, NULL, &outfd, &ms);
+		chan = ast_waitfor_nandfds(&bridge_channel->chan, 1, fds, nfds, NULL, &outfd, &to);
 	} else {
 		ast_mutex_lock(&bridge_channel->lock);
 		ast_debug(1, "Going into a multithreaded signal wait for bridge channel %p of bridge %p\n", bridge_channel, bridge_channel->bridge);
@@ -704,7 +799,11 @@
 	ao2_lock(bridge_channel->bridge);
 
 	if (!bridge_channel->suspended) {
-		ast_bridge_handle_trip(bridge_channel->bridge, bridge_channel, chan, outfd);
+		if (!to) {
+			ast_bridge_handle_trip(bridge_channel->bridge, NULL, NULL, -1);
+		} else {
+			ast_bridge_handle_trip(bridge_channel->bridge, bridge_channel, chan, outfd);
+		}
 	}
 
 	return bridge_channel->state;
@@ -732,6 +831,9 @@
 
 	bridge_array_remove(bridge, bridge_channel->chan);
 
+	bridge->timeout = bridge_timeout(bridge);
+	ast_debug(1, "Recalculated timeout on bridge %p\n", bridge);
+
 	if (bridge->technology->suspend) {
 		bridge->technology->suspend(bridge, bridge_channel);
 	}
@@ -745,6 +847,9 @@
 	bridge_channel->suspended =0;
 
 	bridge_array_add(bridge, bridge_channel->chan);
+
+	bridge->timeout = bridge_timeout(bridge);
+	ast_debug(1, "Recalculated timeout on bridge %p\n", bridge);
 
 	if (bridge->technology->unsuspend) {
 		bridge->technology->unsuspend(bridge, bridge_channel);
@@ -835,6 +940,77 @@
 	return;
 }
 
+/*! \brief Internal function that activates interval hooks on a bridge channel */
+static void bridge_channel_interval(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge_features_hook *hook;
+
+	while ((hook = AST_LIST_FIRST(&bridge_channel->features->interval_hooks))) {
+		int res;
+
+		if (ast_tvdiff_ms(hook->interval_trip_time, ast_tvnow()) > 0) {
+			ast_debug(1, "Hook '%p' on '%p' wants to happen in the future, stopping our traversal\n", hook, bridge_channel);
+			break;
+		}
+
+		ast_debug(1, "Executing hook '%p' on channel '%p'\n", hook, bridge_channel);
+		res = hook->callback(bridge, bridge_channel, hook->hook_pvt);
+
+		AST_LIST_REMOVE(&bridge_channel->features->interval_hooks, hook, entry);
+
+		if (res || !hook->interval) {
+			ast_debug(1, "Hook '%p' is being removed from '%p'\n", hook, bridge_channel);
+			ast_free(hook);
+		} else {
+			struct ast_bridge_features_hook *existing_hook = NULL;
+
+			ast_debug(1, "Updating hook '%p' and adding it back to '%p'\n", hook, bridge_channel);
+			hook->interval_trip_time = ast_tvadd(ast_tvnow(), ast_samp2tv(hook->interval, 1000));
+
+			AST_LIST_TRAVERSE_SAFE_BEGIN(&bridge_channel->features->interval_hooks, existing_hook, entry) {
+				if (ast_tvdiff_ms(hook->interval_trip_time, ast_tvnow()) < ast_tvdiff_ms(existing_hook->interval_trip_time, ast_tvnow())) {
+					ast_debug(1, "Added hook '%p' before hook '%p' on '%p'\n", hook, existing_hook, bridge_channel->features);
+					AST_LIST_INSERT_BEFORE_CURRENT(hook, entry);
+					break;
+				}
+			}
+			AST_LIST_TRAVERSE_SAFE_END;
+
+			if (!existing_hook) {
+				ast_debug(1, "Added hook '%p' at end of list on '%p'\n", hook, bridge_channel->features);
+				AST_LIST_INSERT_TAIL(&bridge_channel->features->interval_hooks, hook, entry);
+			}
+		}
+	}
+
+	return;
+}
+
+/*! \brief Internal function which nudges a bridged channel out of a bridge */
+static void bridge_channel_throw_out(struct ast_bridge_channel *bridge_channel)
+{
+	/* See if we need to dissolve the bridge itself if they hung up */
+	if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_END) {
+		bridge_check_dissolve(bridge_channel->bridge, bridge_channel);
+	}
+
+	/* 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);
+
+	bridge_array_remove(bridge_channel->bridge, bridge_channel->chan);
+
+	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_channel *bridge_channel)
 {
@@ -859,20 +1035,36 @@
 	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_channel2->swap = bridge_channel->chan;
+			bridge_channel_throw_out(bridge_channel2);
 		}
 
 		bridge_channel->swap = NULL;
-	} else if (ast_test_flag(&bridge_channel->bridge->feature_flags, AST_BRIDGE_FLAG_SMART)) {
+	}
+
+	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, bridge_channel->bridge->num);
 	}
 
 	/* Make the channel compatible with the bridge */
 	bridge_make_compatible(bridge_channel->bridge, bridge_channel);
+
+	/* Update all the interval hooks for the first time */
+	if (bridge_channel->features && bridge_channel->features->usable && !AST_LIST_EMPTY(&bridge_channel->features->interval_hooks)) {
+		struct ast_bridge_features_hook *hook;
+
+		AST_LIST_TRAVERSE(&bridge_channel->features->interval_hooks, hook, entry) {
+			hook->interval_trip_time = ast_tvadd(ast_tvnow(), ast_samp2tv(hook->interval, 1000));
+			ast_debug(1, "Updated interval hook '%p' on bridge channel '%p'\n", hook, bridge_channel);
+		}
+
+		ast_debug(1, "Updating timeout on bridge '%p' since a channel that may change it has entered\n", bridge_channel->bridge);
+		bridge_channel->bridge->timeout = bridge_timeout(bridge_channel->bridge);
+	}
 
 	/* Tell the bridge technology we are joining so they set us up */
 	if (bridge_channel->bridge->technology->join) {
@@ -908,33 +1100,23 @@
 			bridge_channel_suspend(bridge_channel->bridge, bridge_channel);
 			bridge_channel_dtmf_stream(bridge_channel->bridge, bridge_channel);
 			bridge_channel_unsuspend(bridge_channel->bridge, bridge_channel);
+		} else if (state == AST_BRIDGE_CHANNEL_STATE_INTERVAL) {
+			bridge_channel_suspend(bridge_channel->bridge, bridge_channel);
+			bridge_channel_interval(bridge_channel->bridge, bridge_channel);
+			bridge_channel_unsuspend(bridge_channel->bridge, bridge_channel);
 		}
 	}
 
 	bridge_channel->chan->bridge = NULL;
 
-	/* See if we need to dissolve the bridge itself if they hung up */
-	if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_END) {
-		bridge_check_dissolve(bridge_channel->bridge, bridge_channel);
-	}
-
-	/* 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);
-
-	bridge_array_remove(bridge_channel->bridge, bridge_channel->chan);
-
-	/* 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, bridge_channel->bridge->num);
+	/* A swap operation will have already taken care of removing us from the bridge */
+	if (!bridge_channel->swap) {
+		bridge_channel_throw_out(bridge_channel);
+
+		/* 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, bridge_channel->bridge->num);
+		}
 	}
 
 	ao2_unlock(bridge_channel->bridge);
@@ -1258,6 +1440,48 @@
 	return 0;
 }
 
+int ast_bridge_features_interval_hook(struct ast_bridge_features *features, unsigned int interval, ast_bridge_features_hook_callback callback, void *hook_pvt)
+{
+	struct ast_bridge_features_hook *hook = NULL, *existing_hook = NULL;
+
+	if (!(hook = ast_calloc(1, sizeof(*hook)))) {
+		return -1;
+	}
+
+	hook->interval = interval;
+	hook->callback = callback;
+	hook->hook_pvt = hook_pvt;
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&features->interval_hooks, existing_hook, entry) {
+		if (hook->interval < existing_hook->interval) {
+			ast_debug(1, "Put interval hook '%p' before interval hook '%p' on features '%p'\n", hook, existing_hook, features);
+			AST_LIST_INSERT_BEFORE_CURRENT(hook, entry);
+			break;
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	if (!existing_hook) {
+		ast_debug(1, "Put interval hook '%p' at the end of the interval hooks on features '%p'\n", hook, features);
+		AST_LIST_INSERT_TAIL(&features->interval_hooks, hook, entry);
+	}
+
+	features->usable = 1;
+
+	return 0;
+}
+
+int ast_bridge_features_interval_update(struct ast_bridge_channel *bridge_channel, unsigned int interval)
+{
+	if (!bridge_channel->features || !bridge_channel->features->usable || AST_LIST_EMPTY(&bridge_channel->features->interval_hooks)) {
+		return -1;
+	}
+
+	AST_LIST_FIRST(&bridge_channel->features->interval_hooks)->interval = interval;
+
+	return 0;
+}
+
 int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature, const char *dtmf, void *config)
 {
 	/* If no alternate DTMF stream was provided use the default one */
@@ -1292,6 +1516,7 @@
 
 	/* Initialize the hooks list, just in case */
 	AST_LIST_HEAD_INIT_NOLOCK(&features->hooks);
+	AST_LIST_HEAD_INIT_NOLOCK(&features->interval_hooks);
 
 	return 0;
 }
@@ -1305,6 +1530,10 @@
 		ast_free(hook);
 	}
 
+	while ((hook = AST_LIST_REMOVE_HEAD(&features->interval_hooks, entry))) {
+		ast_free(hook);
+	}
+
 	return 0;
 }
 




More information about the asterisk-commits mailing list