[asterisk-commits] rmudgett: branch rmudgett/bridge_phase r393260 - /team/rmudgett/bridge_phase/...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Jun 28 22:59:01 CDT 2013


Author: rmudgett
Date: Fri Jun 28 22:59:00 2013
New Revision: 393260

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=393260
Log:
Work so far on dealing with local channel optimization on the agent channel.

Modified:
    team/rmudgett/bridge_phase/apps/app_agent_pool.c

Modified: team/rmudgett/bridge_phase/apps/app_agent_pool.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/apps/app_agent_pool.c?view=diff&rev=393260&r1=393259&r2=393260
==============================================================================
--- team/rmudgett/bridge_phase/apps/app_agent_pool.c (original)
+++ team/rmudgett/bridge_phase/apps/app_agent_pool.c Fri Jun 28 22:59:00 2013
@@ -217,6 +217,9 @@
 /*! Maximum wait time (in ms) for the custom_beep file to play announcing the caller. */
 #define CALLER_SAFETY_TIMEOUT_TIME	(2 * 60 * 1000)
 
+/*! Number of seconds to wait for local channel optimizations to complete. */
+#define LOGIN_WAIT_TIMEOUT_TIME		5
+
 static const char app_agent_login[] = "AgentLogin";
 static const char app_agent_request[] = "AgentRequest";
 
@@ -487,6 +490,15 @@
 enum agent_state {
 	/*! The agent is defined but an agent is not present. */
 	AGENT_STATE_LOGGED_OUT,
+/*
+ * BUGBUG the login probation time should be only if it is needed.
+ *
+ * Need to determine if there are any local channels that can
+ * optimize and wait until they actually do before leaving the
+ * state.
+ */
+	/*! Forced initial login wait to allow any local channel optimizations to happen. */
+	AGENT_STATE_PROBATION_WAIT,
 	/*! The agent is ready for a call. */
 	AGENT_STATE_READY_FOR_CALL,
 	/*! The agent has a call waiting to connect. */
@@ -547,6 +559,8 @@
 
 	/*! When agent first logged in */
 	time_t login_start;
+	/*! When agent login probation started. */
+	time_t probation_start;
 	/*! When call started */
 	time_t call_start;
 	/*! When ack timer started */
@@ -972,6 +986,7 @@
 static int bridge_agent_hold_heartbeat(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 {
 	struct agent_pvt *agent = hook_pvt;
+	int probation_timedout = 0;
 	int ack_timedout = 0;
 	int wrapup_timedout = 0;
 	int deferred_logoff;
@@ -985,6 +1000,15 @@
 	}
 
 	switch (agent->state) {
+	case AGENT_STATE_PROBATION_WAIT:
+		probation_timedout =
+			LOGIN_WAIT_TIMEOUT_TIME <= (time(NULL) - agent->probation_start);
+		if (probation_timedout) {
+			/* Now ready for a caller. */
+			agent->state = AGENT_STATE_READY_FOR_CALL;
+			agent->devstate = AST_DEVICE_NOT_INUSE;
+		}
+		break;
 	case AGENT_STATE_CALL_WAIT_ACK:
 		/* Check ack call time. */
 		auto_logoff = agent->cfg->auto_logoff;
@@ -1019,6 +1043,9 @@
 	if (deferred_logoff) {
 		ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
 		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+	} else if (probation_timedout) {
+		ast_debug(1, "Agent %s: Login complete.\n", agent->username);
+		agent_devstate_changed(agent->username);
 	} else if (ack_timedout) {
 		ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
 		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
@@ -1029,6 +1056,10 @@
 
 	return 0;
 }
+
+static void agent_logout(struct agent_pvt *agent);
+static void agent_after_bridge_cb(struct ast_channel *chan, void *data);
+static void agent_after_bridge_cb_failed(struct ast_channel *chan, void *data, enum ast_after_bridge_cb_reason reason);
 
 /*!
  * \internal
@@ -1098,35 +1129,52 @@
 		res = -1;
 	}
 
+	res |= ast_bridge_base_v_table.push(self, bridge_channel, swap);
 	if (res) {
 		ast_channel_remove_bridge_role(chan, "holding_participant");
 		return -1;
 	}
 
-	res = ast_bridge_base_v_table.push(self, bridge_channel, swap);
-	if (res) {
-		ast_channel_remove_bridge_role(chan, "holding_participant");
-		return -1;
-	}
-
 	if (swap) {
-/*! \todo BUGBUG bridge_agent_hold_push() needs swap after bridge callback added.  */
 		agent_lock(agent);
 		ast_channel_unref(agent->logged);
 		agent->logged = ast_channel_ref(chan);
 		agent_unlock(agent);
+
+		/*
+		 * Kick the channel out so it can come back in fully controlled.
+		 * Otherwise, the after bridge callback will linger and the
+		 * agent will have some slightly different behavior in corner
+		 * cases.
+		 */
+		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+
+/* BUGBUG this failure condition as well as other threads potentially trying to add their own after bridge callbacks has issues. */
+		ao2_ref(agent, +1);
+		res = ast_after_bridge_callback_set(chan, agent_after_bridge_cb,
+			agent_after_bridge_cb_failed, agent);
+		if (res) {
+			ao2_ref(agent, -1);
+			agent_lock(agent);
+			agent_logout(agent);
+			ast_channel_remove_bridge_role(chan, "holding_participant");
+			return -1;
+		}
 		return 0;
 	}
 
 	agent_lock(agent);
 	switch (agent->state) {
 	case AGENT_STATE_LOGGED_OUT:
-		/* Now ready for a caller. */
-		agent->state = AGENT_STATE_READY_FOR_CALL;
-		agent->devstate = AST_DEVICE_NOT_INUSE;
+		/* Start the login probation timer.*/
+		time(&agent->probation_start);
+		agent->state = AGENT_STATE_PROBATION_WAIT;
 		agent_unlock(agent);
-		ast_debug(1, "Agent %s: Login complete.\n", agent->username);
-		agent_devstate_changed(agent->username);
+		break;
+	case AGENT_STATE_PROBATION_WAIT:
+		/* Restart the probation timer. */
+		time(&agent->probation_start);
+		agent_unlock(agent);
 		break;
 	case AGENT_STATE_READY_FOR_CALL:
 		/*
@@ -1340,6 +1388,8 @@
  *
  * \param agent Which agent logging out.
  *
+ * \note On entry agent is already locked.  On exit it is no longer locked.
+ *
  * \return Nothing
  */
 static void agent_logout(struct agent_pvt *agent)
@@ -1348,7 +1398,6 @@
 	struct ast_bridge *caller_bridge;
 	long time_logged_in;
 
-	agent_lock(agent);
 	time_logged_in = time(NULL) - agent->login_start;
 	logged = agent->logged;
 	agent->logged = NULL;
@@ -1376,19 +1425,15 @@
  * \since 12.0.0
  *
  * \param agent Which agent.
+ * \param logged The logged in channel.
  *
  * \return Nothing
  */
-static void agent_run(struct agent_pvt *agent)
-{
-	struct ast_channel *logged;
+static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
+{
 	struct ast_bridge_features features;
 
 	if (!ast_bridge_features_init(&features)) {
-		agent_lock(agent);
-		logged = ast_channel_ref(agent->logged);
-		agent_unlock(agent);
-
 		for (;;) {
 			struct agents_cfg *cfgs;
 			struct agent_cfg *cfg_new;
@@ -1403,13 +1448,7 @@
 
 			ast_bridge_join(holding, logged, NULL, &features, NULL, 1);
 			if (logged != agent->logged) {
-				/*
-				 * We are no longer the agent channel because of local channel
-				 * optimization.
-				 */
-				ast_channel_unref(logged);
-				ast_bridge_features_cleanup(&features);
-				return;
+				break;
 			}
 
 			if (agent->dead) {
@@ -1453,11 +1492,80 @@
 			 */
 			ast_channel_update_connected_line(logged, &agent->waiting_colp, NULL);
 		}
+		ast_bridge_features_cleanup(&features);
+	}
+
+	agent_lock(agent);
+	if (logged != agent->logged) {
+		/*
+		 * We are no longer the agent channel because of local channel
+		 * optimization.
+		 */
+		agent_unlock(agent);
+		ast_debug(1, "Agent %s: Channel %s is no longer the agent.\n",
+			agent->username, ast_channel_name(logged));
+		return;
+	}
+	agent_logout(agent);
+}
+
+static void agent_after_bridge_cb(struct ast_channel *chan, void *data)
+{
+	struct agent_pvt *agent = data;
+
+	ast_debug(1, "Agent %s: New agent channel %s.\n",
+		agent->username, ast_channel_name(chan));
+	agent_run(agent, chan);
+	ao2_ref(agent, -1);
+}
+
+/*
+ * BUGBUG must change after bridge callback failed to also be called when the channel leaves the bridge system.
+ *
+ * The after bridge callbacks will become a list of callbacks.
+ * Only the last callback added to the list may be active if it
+ * was not explicitly removed.  When the channel leaves the
+ * bridging system it will call all the failed callbacks in
+ * order of when they were added.  This will prevent rapid fire
+ * local optimizations from causing locking issues with the
+ * bridge lock.
+ *
+ * The failed callback will always run in a known thread
+ * context.  A big plus.
+ */
+static void agent_after_bridge_cb_failed(struct ast_channel *chan, void *data, enum ast_after_bridge_cb_reason reason)
+{
+	struct ast_bridge *holding;
+	struct ast_channel *logged;
+	RAII_VAR(struct agent_pvt *, agent, data, ao2_cleanup);
+
+	ast_assert(chan != NULL);
+
+	agent_lock(agent);
+	if (chan != agent->logged) {
+		/*
+		 * We are no longer the agent channel because of local channel
+		 * optimization.
+		 */
+		agent_unlock(agent);
+		ast_debug(1, "Agent %s: Channel %s is no longer the agent.\n",
+			agent->username, ast_channel_name(chan));
+		return;
+	}
+	logged = ast_channel_ref(agent->logged);
+	ast_log(LOG_WARNING, "Agent %s: Forced logout.  Lost control of %s because: %s\n",
+		agent->username, ast_channel_name(chan),
+		ast_after_bridge_cb_reason_string(reason));
+	agent_logout(agent);
+
+	holding = ao2_global_obj_ref(agent_holding);
+	if (!holding) {
 		ast_channel_unref(logged);
-		ast_bridge_features_cleanup(&features);
-	}
-
-	agent_logout(agent);
+		return;
+	}
+	ast_bridge_remove(holding, logged);
+	ao2_ref(holding, -1);
+	ast_channel_unref(logged);
 }
 
 /*!
@@ -1895,7 +2003,7 @@
 		ast_channel_unlock(chan);
 	}
 
-	agent_run(agent);
+	agent_run(agent, chan);
 	return -1;
 }
 
@@ -2431,7 +2539,6 @@
 	res |= ast_register_application_xml(app_agent_request, agent_request_exec);
 
 /* BUGBUG agent call recording not written. */
-/* BUGBUG bridge channel swap hook not written. */
 
 	if (res) {
 		unload_module();




More information about the asterisk-commits mailing list