[asterisk-commits] russell: branch russell/sla_updates r54709 - /team/russell/sla_updates/apps/

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Thu Feb 15 16:32:45 MST 2007


Author: russell
Date: Thu Feb 15 17:32:44 2007
New Revision: 54709

URL: http://svn.digium.com/view/asterisk?view=rev&rev=54709
Log:
This timeout handling is really hurting my head.  It is a lot more complicated
than I first thought!  Anyway, I have improved a lot of the logic involved
in handling station ring timeouts.

- When we have a per-trunk timeout on a station, to calculate the time that has
  elapsed, we have to use the time that the specific trunk started ringing, not
  when the station started ringing.  This ensures that the full timeout will be
  honored.

- When a station does time out, add it to a list of stations that have timed
  out on each of the ringing trunks that this station uses.

- When processing which stations should be ringing, ignore ringing trunks for
  a station where the station already timed out on it.

- When a station answers, answer the highest priority trunk that the station
  did not already reach a timeout for.

Modified:
    team/russell/sla_updates/apps/app_meetme.c

Modified: team/russell/sla_updates/apps/app_meetme.c
URL: http://svn.digium.com/view/asterisk/team/russell/sla_updates/apps/app_meetme.c?view=diff&rev=54709&r1=54708&r2=54709
==============================================================================
--- team/russell/sla_updates/apps/app_meetme.c (original)
+++ team/russell/sla_updates/apps/app_meetme.c Thu Feb 15 17:32:44 2007
@@ -357,6 +357,11 @@
 	AST_LIST_ENTRY(ast_conf_user) list;
 };
 
+enum sla_which_trunk_refs {
+	ALL_TRUNK_REFS,
+	INACTIVE_TRUNK_REFS,
+};
+
 enum sla_trunk_state {
 	SLA_TRUNK_STATE_IDLE,
 	SLA_TRUNK_STATE_RINGING,
@@ -452,7 +457,13 @@
 	struct sla_trunk *trunk;
 	/*! The time that this trunk started ringing */
 	struct timeval ring_begin;
+	AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
 	AST_LIST_ENTRY(sla_ringing_trunk) entry;
+};
+
+enum sla_station_hangup {
+	SLA_STATION_HANGUP_NORMAL,
+	SLA_STATION_HANGUP_TIMEOUT,
 };
 
 /*! \brief A station that is ringing */
@@ -3055,7 +3066,8 @@
 	return ringing_station;
 }
 
-static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state, int inactive_only)
+static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state, 
+	enum sla_which_trunk_refs inactive_only)
 {
 	struct sla_station *station;
 	struct sla_trunk_ref *trunk_ref;
@@ -3110,7 +3122,7 @@
 	if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations)) {
 		strncat(conf_name, "|K", sizeof(conf_name) - strlen(conf_name) - 1);
 		admin_exec(NULL, conf_name);
-		sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, 0);
+		sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS);
 	}
 
 	ast_dial_join(station->dial);
@@ -3123,19 +3135,50 @@
 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
 {
 	char buf[80];
+	struct sla_station_ref *station_ref;
 
 	snprintf(buf, sizeof(buf), "SLA_%s|K", ringing_trunk->trunk->name);
 	admin_exec(NULL, buf);
-	sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, 0);
+	sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS);
+
+	while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
+		free(station_ref);
 
 	free(ringing_trunk);
 }
 
-static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station)
-{
+static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
+	enum sla_station_hangup hangup)
+{
+	struct sla_ringing_trunk *ringing_trunk;
+	struct sla_trunk_ref *trunk_ref;
+	struct sla_station_ref *station_ref;
+
 	ast_dial_join(ringing_station->station->dial);
 	ast_dial_destroy(ringing_station->station->dial);
 	ringing_station->station->dial = NULL;
+
+	if (hangup == SLA_STATION_HANGUP_NORMAL)
+		goto done;
+
+	/* If the station is being hung up because of a timeout, then add it to the
+	 * list of timed out stations on each of the ringing trunks.  This is so
+	 * that when doing further processing to figure out which stations should be
+	 * ringing, which trunk to answer, determining timeouts, etc., we know which
+	 * ringing trunks we should ignore. */
+	AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
+		AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
+			if (ringing_trunk->trunk == trunk_ref->trunk)
+				break;
+		}
+		if (!trunk_ref)
+			continue;
+		if (!(station_ref = sla_create_station_ref(ringing_station->station)))
+			continue;
+		AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
+	}
+
+done:
 	free(ringing_station);
 }
 
@@ -3165,7 +3208,7 @@
 		case AST_DIAL_RESULT_TIMEOUT:
 		case AST_DIAL_RESULT_UNANSWERED:
 			AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
-			sla_stop_ringing_station(ringing_station);
+			sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
 			break;
 		case AST_DIAL_RESULT_ANSWERED:
 			AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
@@ -3173,10 +3216,19 @@
 			AST_LIST_TRAVERSE(&ringing_station->station->trunks, s_trunk_ref, entry) {
 				ast_mutex_lock(&sla.lock);
 				AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
-					if (s_trunk_ref->trunk == ringing_trunk->trunk) {
-						AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
-						break;
+					struct sla_station_ref *station_ref;
+					if (s_trunk_ref->trunk != ringing_trunk->trunk)
+						continue;
+					/* This trunk on the station is ringing.  But, make sure this station
+					 * didn't already time out while this trunk was ringing. */
+					AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
+						if (station_ref->station == ringing_station->station)
+							break;
 					}
+					if (station_ref)
+						continue;
+					AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
+					break;
 				}
 				AST_LIST_TRAVERSE_SAFE_END
 				ast_mutex_unlock(&sla.lock);
@@ -3190,7 +3242,7 @@
 			}
 			s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
 			ast_answer(ringing_trunk->trunk->chan);
-			sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, 0);
+			sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS);
 			args.trunk_ref = s_trunk_ref;
 			args.station = ringing_station->station;
 			args.cond = &cond;
@@ -3241,6 +3293,7 @@
 			char *tech, *tech_data;
 			struct ast_dial *dial;
 			struct sla_failed_station *failed_station;
+			struct sla_station_ref *timed_out_station;
 			/* Did we fail to dial this station earlier?  If so, has it been
  			 * a minute since we tried? */
 			AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
@@ -3262,6 +3315,14 @@
 			}
 			if (ringing_station)
 				continue;
+			/* If this station already timed out while this trunk was ringing,
+			 * do not dial it again for this ringing trunk. */
+			AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
+				if (station_ref->station == timed_out_station->station)
+					break;
+			}
+			if (timed_out_station)
+				continue;
 			if (!(dial = ast_dial_create()))
 				continue;
 			ast_dial_set_state_callback(dial, sla_dial_state_callback);
@@ -3323,13 +3384,13 @@
 	event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD;
 	ast_device_state_changed("SLA:%s_%s", 
 		event->station->name, event->trunk_ref->trunk->name);
-	sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD, 1);	
+	sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD, INACTIVE_TRUNK_REFS);	
 }
 
 static void sla_handle_unhold_event(struct sla_event *event)
 {
 	if (ast_atomic_dec_and_test((int *) &event->trunk_ref->trunk->hold_stations) == 1)
-		sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_UP, 0);
+		sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS);
 	else {
 		event->trunk_ref->state = SLA_TRUNK_STATE_UP;
 		ast_device_state_changed("SLA:%s_%s",
@@ -3368,31 +3429,53 @@
 	/* Check for ring timeouts on ringing stations */
 	AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
 		unsigned int ring_timeout = 0;
-		int time_elapsed, time_left;
+		int time_elapsed, time_left, final_trunk_time_left = INT_MIN;
 		struct sla_trunk_ref *trunk_ref;
 		/* If there are any ring timeouts specified for a specific trunk
 		 * on the station, then use the highest per-trunk ring timeout.
 		 * Otherwise, use the ring timeout set for the entire station. */
 		AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
+			struct sla_station_ref *station_ref;
+			int trunk_time_elapsed, trunk_time_left;
 			AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
 				if (ringing_trunk->trunk == trunk_ref->trunk)
 					break;
 			}
-			if (!ringing_trunk || !trunk_ref->ring_timeout)
+			if (!ringing_trunk)
 				continue;
-			if (trunk_ref->ring_timeout > ring_timeout)
-				ring_timeout = trunk_ref->ring_timeout;
-		}
-		if (!ring_timeout && !ringing_station->station->ring_timeout)
+			/* If there is a trunk that is ringing without a timeout, then the
+			 * only timeout that could matter is a global station ring timeout. */
+			if (!trunk_ref->ring_timeout) {
+				final_trunk_time_left = INT_MAX;
+				break;
+			}
+			/* This trunk on this station is ringing and has a timeout.
+			 * However, make sure this trunk isn't still ringing from a
+			 * previous timeout.  If so, don't consider it. */
+			AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
+				if (station_ref->station == ringing_station->station)
+					break;
+			}
+			if (station_ref)
+				continue;
+			trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
+			trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
+			if (trunk_time_left > final_trunk_time_left)
+				final_trunk_time_left = trunk_time_left;
+		}
+		if (final_trunk_time_left == INT_MAX && !ringing_station->station->ring_timeout)
 			continue;
+
 		ring_timeout = ringing_station->station->ring_timeout;
-
-		/* Finally, we actually have a ring timeout for this station. */
 		time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
 		time_left = (ring_timeout * 1000) - time_elapsed;
+		/* If the time left based on the per-trunk timeouts is smaller than the
+		 * global station ring timeout, use that. */
+		if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
+			time_left = final_trunk_time_left;
 		if (time_left <= 0) {
 			AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
-			sla_stop_ringing_station(ringing_station);
+			sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
 			change_made = 1;
 			continue;
 		}
@@ -3633,7 +3716,7 @@
 			.cond_lock = &cond_lock,
 			.cond = &cond,
 		};
-		sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, 0);
+		sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS);
 		/* Create a thread to dial the trunk and dump it into the conference.
 		 * However, we want to wait until the trunk has been dialed and the
 		 * conference is created before continuing on here. */
@@ -3653,7 +3736,7 @@
 		if (!trunk_ref->trunk->chan) {
 			ast_log(LOG_DEBUG, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
 			pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
-			sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, 0);
+			sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS);
 			return 0;
 		}
 	}
@@ -3672,7 +3755,7 @@
 	if (res == 1) {	
 		strncat(conf_name, "|K", sizeof(conf_name) - strlen(conf_name) - 1);
 		admin_exec(NULL, conf_name);
-		sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, 0);
+		sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS);
 	}
 	
 	pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");



More information about the asterisk-commits mailing list