Commit 7f7269fa authored by Kai Vehmanen's avatar Kai Vehmanen

Fixed bugs related to cases where checks produce different results for...

Fixed bugs related to cases where checks produce different results for components of the same stream. Updated connectivity check functionality to ICE ID-18.

darcs-hash:20071010134533-77cd4-a48041c85c7525261658d74c94401041065be62d.gz
parent a76e5aa7
......@@ -220,42 +220,20 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, Stream *stream,
}
/**
* Timer callback that handles initiating and managing connectivity
* checks (paced by the Ta timer).
* Helper function for connectivity check timer callback that
* runs through the stream specific part of the state machine.
*
* This function is designed for the g_timeout_add() interface.
* @param schedule if TRUE, schedule a new check
*
* @return will return FALSE when no more pending timers.
*/
static gboolean priv_conn_check_tick (gpointer pointer)
static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, GTimeVal *now)
{
CandidateCheckPair *pair = NULL;
NiceAgent *agent = pointer;
GSList *i, *j;
gboolean keep_timer_going = FALSE;
GTimeVal now;
int frozen = 0, inprogress = 0, waiting = 0, succeeded = 0, nominated = 0;
/* step: find the highest priority waiting check and send it */
for (i = agent->streams; i ; i = i->next) {
Stream *stream = i->data;
pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
guint s_inprogress = 0, s_succeeded = 0, s_nominated = 0, s_waiting_for_nomination = 0;
guint frozen = 0, waiting = 0;
GSList *i, *k;
if (pair)
break;
}
if (pair) {
priv_conn_check_initiate (agent, pair);
keep_timer_going = TRUE;
}
/* step: process ongoing STUN transactions */
g_get_current_time (&now);
for (j = agent->streams; j; j = j->next) {
Stream *stream = j->data;
for (i = stream->conncheck_list; i ; i = i->next) {
CandidateCheckPair *p = i->data;
......@@ -264,14 +242,14 @@ static gboolean priv_conn_check_tick (gpointer pointer)
g_debug ("STUN connectivity check was cancelled, marking as done.");
p->state = NICE_CHECK_FAILED;
}
else if (priv_timer_expired (&p->next_tick, &now)) {
else if (priv_timer_expired (&p->next_tick, now)) {
int res = stun_bind_elapse (p->stun_ctx);
if (res == EAGAIN) {
/* case: not ready, so schedule a new timeout */
unsigned int timeout = stun_bind_timeout (p->stun_ctx);
/* note: convert from milli to microseconds for g_time_val_add() */
p->next_tick = now;
p->next_tick = *now;
g_time_val_add (&p->next_tick, timeout * 1000);
keep_timer_going = TRUE;
......@@ -289,46 +267,40 @@ static gboolean priv_conn_check_tick (gpointer pointer)
if (p->state == NICE_CHECK_FROZEN)
++frozen;
else if (p->state == NICE_CHECK_IN_PROGRESS)
++inprogress;
++s_inprogress;
else if (p->state == NICE_CHECK_WAITING)
++waiting;
else if (p->state == NICE_CHECK_SUCCEEDED)
++succeeded;
++s_succeeded;
if (p->nominated)
++nominated;
}
if (p->state == NICE_CHECK_SUCCEEDED && p->nominated)
++s_nominated;
else if (p->state == NICE_CHECK_SUCCEEDED && !p->nominated)
++s_waiting_for_nomination;
}
/* note: keep the timer going as long as there is work to be done */
if (frozen || inprogress || waiting ||
(succeeded && nominated == 0))
if (s_inprogress)
keep_timer_going = TRUE;
/* note: if no waiting checks, unfreeze one check */
/* XXX: unfreeze the next stream */
if (!waiting && frozen)
priv_conn_check_unfreeze_next (agent);
/* step: nominate some candidate if we are in controlling mode
* - all checks have been completed but no pair has been
* - can be caused by a controlling-controlling role conflict)
*/
if (agent->controlling_mode &&
(!frozen && !inprogress && !waiting && !nominated)) {
g_debug ("no nominated checks, resending checks to select one");
for (j = agent->streams; j; j = j->next) {
Stream *stream = j->data;
for (i = stream->conncheck_list; i ; i = i->next) {
CandidateCheckPair *p = i->data;
/* note: if some components have established connectivity,
* but yet no nominated pair, keep timer going */
if (s_nominated < stream->n_components &&
s_waiting_for_nomination) {
keep_timer_going = TRUE;
if (agent->controlling_mode) {
guint n;
for (n = 0; n < stream->n_components; n++) {
for (k = stream->conncheck_list; k ; k = k->next) {
CandidateCheckPair *p = k->data;
/* note: highest priority item selected (list always sorted) */
if (p->state == NICE_CHECK_SUCCEEDED ||
p->state == NICE_CHECK_DISCOVERED) {
g_debug ("restarting check %p as the nominated pair.", p);
p->nominated = TRUE;
priv_conn_check_initiate (agent, p);
keep_timer_going = TRUE;
break;
break; /* move to the next component */
}
}
}
}
......@@ -338,11 +310,59 @@ static gboolean priv_conn_check_tick (gpointer pointer)
{
static int tick_counter = 0;
if (tick_counter++ % 50 == 0 || keep_timer_going != TRUE)
g_debug ("timer(%p) tick #%d: %d frozen, %d in-progress, %d waiting, %d succeeded, %d nominated.",
agent, tick_counter, frozen, inprogress, waiting, succeeded, nominated);
g_debug ("timer(%p) tick #%u: %u frozen, %u in-progress, %u waiting, %u succeeded, %u nominated, %u waiting-for-nom.",
agent, tick_counter, frozen, s_inprogress, waiting, s_succeeded, s_nominated, s_waiting_for_nomination);
}
#endif
return keep_timer_going;
}
/**
* Timer callback that handles initiating and managing connectivity
* checks (paced by the Ta timer).
*
* This function is designed for the g_timeout_add() interface.
*
* @return will return FALSE when no more pending timers.
*/
static gboolean priv_conn_check_tick (gpointer pointer)
{
CandidateCheckPair *pair = NULL;
NiceAgent *agent = pointer;
gboolean keep_timer_going = FALSE;
GSList *i, *j;
GTimeVal now;
/* step: process ongoing STUN transactions */
g_get_current_time (&now);
/* step: find the highest priority waiting check and send it */
for (i = agent->streams; i ; i = i->next) {
Stream *stream = i->data;
pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
if (pair)
break;
}
if (pair) {
priv_conn_check_initiate (agent, pair);
keep_timer_going = TRUE;
}
else
priv_conn_check_unfreeze_next (agent);
for (j = agent->streams; j; j = j->next) {
Stream *stream = j->data;
gboolean res =
priv_conn_check_tick_stream (stream, agent, &now);
if (res)
keep_timer_going = res;
}
/* step: stop timer if no work left */
if (keep_timer_going != TRUE) {
g_debug ("%s: stopping conncheck timer", G_STRFUNC);
......@@ -426,20 +446,22 @@ static gboolean priv_conn_keepalive_tick (gpointer pointer)
/**
* Initiates the next pending connectivity check.
*
* @return TRUE if a pending check was scheduled
*/
void conn_check_schedule_next (NiceAgent *agent)
gboolean conn_check_schedule_next (NiceAgent *agent)
{
gboolean c = priv_conn_check_unfreeze_next (agent);
gboolean res = priv_conn_check_unfreeze_next (agent);
if (agent->discovery_unsched_items > 0)
g_debug ("WARN: starting conn checks before local candidate gathering is finished.");
if (c == TRUE) {
if (res == TRUE) {
/* step: call once imediately */
gboolean res = priv_conn_check_tick ((gpointer) agent);
res = priv_conn_check_tick ((gpointer) agent);
/* step: schedule timer if not running yet */
if (agent->conncheck_timer_id == 0 && res != FALSE)
if (res && agent->conncheck_timer_id == 0)
agent->conncheck_timer_id =
g_timeout_add (agent->timer_ta, priv_conn_check_tick, agent);
......@@ -449,6 +471,8 @@ void conn_check_schedule_next (NiceAgent *agent)
g_timeout_add (NICE_AGENT_TIMER_TR_DEFAULT, priv_conn_keepalive_tick, agent);
}
return res;
}
/**
......@@ -465,6 +489,46 @@ gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *
return 0;
}
/**
* Enforces the upper limit for connectivity checks as described
* in ICE spec section 5.7.3 (ID-18). See also
* conn_check_add_for_candidate().
*/
static GSList *priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit)
{
guint list_len = g_slist_length (conncheck_list);
guint c = 0;
GSList *result = conncheck_list;
if (list_len > upper_limit) {
c = list_len - upper_limit;
if (c == list_len) {
/* case: delete whole list */
g_slist_foreach (conncheck_list, conn_check_free_item, NULL);
g_slist_free (conncheck_list),
result = NULL;
}
else {
/* case: remove 'c' items from list end (lowest priority) */
GSList *i, *tmp;
g_assert (c > 0);
i = g_slist_nth (conncheck_list, c - 1);
tmp = i->next;
i->next = NULL;
if (tmp) {
/* delete the rest of the connectivity check list */
g_slist_foreach (tmp, conn_check_free_item, NULL);
g_slist_free (tmp);
}
}
}
return result;
}
/**
* Creates a new connectivity check pair and adds it to
* the agent's list of checks.
......@@ -498,6 +562,15 @@ static gboolean priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Comp
stream->conncheck_list = modified_list;
result = TRUE;
g_debug ("added a new conncheck %p with foundation of '%s' to list %u.", pair, pair->foundation, stream_id);
/* implement the hard upper limit for number of
checks (see sect 5.7.3 ICE ID-18): */
stream->conncheck_list =
priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks);
if (!stream->conncheck_list) {
stream->conncheck_state = NICE_CHECKLIST_FAILED;
result = FALSE;
}
}
else { /* memory alloc failed: list insert */
conn_check_free_item (pair, NULL);
......@@ -775,13 +848,12 @@ static void priv_update_check_list_state (NiceAgent *agent, Stream *stream)
/* note: iterate the conncheck list for each component separately */
for (c = 0; c < components; c++) {
guint not_failed = 0, checks = 0;
guint not_failed = 0;
for (i = stream->conncheck_list; i; i = i->next) {
CandidateCheckPair *p = i->data;
if (p->stream_id == stream->id &&
p->component_id == (c + 1)) {
++checks;
if (p->state != NICE_CHECK_FAILED)
++not_failed;
......@@ -794,7 +866,7 @@ static void priv_update_check_list_state (NiceAgent *agent, Stream *stream)
}
/* note: all checks have failed */
if (checks > 0 && not_failed == 0)
if (not_failed == 0)
agent_signal_component_state_change (agent,
stream->id,
(c + 1), /* component-id */
......
......@@ -80,7 +80,7 @@ struct _CandidateCheckPair
int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote);
void conn_check_free_item (gpointer data, gpointer user_data);
void conn_check_free (NiceAgent *agent);
void conn_check_schedule_next (NiceAgent *agent);
gboolean conn_check_schedule_next (NiceAgent *agent);
int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair);
gboolean conn_check_prune_stream (NiceAgent *agent, Stream *stream);
gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, Component *component, NiceUDPSocket *udp_socket, const NiceAddress *from, gchar *buf, guint len);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment