Commit 15c0546f authored by Fabrice Bellet's avatar Fabrice Bellet

conncheck: improve the selection of the pairs to be checked

This patch aims to implement more closely the algorithm described
in RFC 5245 indicating how pairs are transitionned from state Frozen
to Waiting. This is described in 7.1.3.2 when a check succeeded, and
correspond to modifications in function priv_conn_check_unfreeze_related().
This is also described in 5.7.4 when defining the initial state of the
pairs in a conncheck, and correspond to modifications in function
priv_conn_check_unfreeze_next().

This patch introduces the notion of active and frozen check list. It
allows us to define the timer restranmission delay as described in 16.1.

Another modification in priv_conn_check_tick_unlocked() is that every
stream in handled consecutively, and in an independant way. The pacing
was previously of a single STUN request emitted per callback, it is now
of a triggered check per callback OR a single STUN per callback AND per
stream per callback.

The description of ordinary checks per stream in 5.8 is detailled in
function priv_conn_check_tick_stream(), and a remaining of the code
used to nominate a pair by the controlling agent is put in a dedicated
function priv_conn_check_tick_stream_nominate()

Differential Revision: https://phabricator.freedesktop.org/D813
parent 58d061df
...@@ -211,6 +211,89 @@ priv_get_pair_from_triggered_check_queue (NiceAgent *agent) ...@@ -211,6 +211,89 @@ priv_get_pair_from_triggered_check_queue (NiceAgent *agent)
return pair; return pair;
} }
/*
* Check if the conncheck list if Active according to
* ICE spec, 5.7.4 (Computing States)
*
* note: the ICE spec in unclear about that, but the check list should
* be considered active when there is at least a pair in Waiting state
* OR a pair in In-Progress state.
*/
static gboolean
priv_is_checklist_active (NiceStream *stream)
{
GSList *i;
for (i = stream->conncheck_list; i ; i = i->next) {
CandidateCheckPair *p = i->data;
if (p->state == NICE_CHECK_WAITING || p->state == NICE_CHECK_IN_PROGRESS)
return TRUE;
}
return FALSE;
}
/*
* Check if the conncheck list if Frozen according to
* ICE spec, 5.7.4 (Computing States)
*/
static gboolean
priv_is_checklist_frozen (NiceStream *stream)
{
GSList *i;
if (stream->conncheck_list == NULL)
return FALSE;
for (i = stream->conncheck_list; i ; i = i->next) {
CandidateCheckPair *p = i->data;
if (p->state != NICE_CHECK_FROZEN)
return FALSE;
}
return TRUE;
}
/*
* Check if all components of the stream have
* a valid pair (used for ICE spec, 7.1.3.2.3, point 2.)
*/
static gboolean
priv_all_components_have_valid_pair (NiceStream *stream)
{
guint i;
GSList *j;
for (i = 1; i <= stream->n_components; i++) {
for (j = stream->conncheck_list; j ; j = j->next) {
CandidateCheckPair *p = j->data;
if (p->component_id == i && p->valid)
break;
}
if (j == NULL)
return FALSE;
}
return TRUE;
}
/*
* Check if the foundation in parameter matches the foundation
* of a valid pair in the conncheck list [of stream] (used for ICE spec,
* 7.1.3.2.3, point 2.)
*/
static gboolean
priv_foundation_matches_a_valid_pair (const gchar *foundation, NiceStream *stream)
{
GSList *i;
for (i = stream->conncheck_list; i ; i = i->next) {
CandidateCheckPair *p = i->data;
if (p->valid &&
strncmp (p->foundation, foundation,
NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0)
return TRUE;
}
return FALSE;
}
/* /*
* Finds the next connectivity check in WAITING state. * Finds the next connectivity check in WAITING state.
*/ */
...@@ -220,7 +303,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check ...@@ -220,7 +303,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check
/* note: list is sorted in priority order to first waiting check has /* note: list is sorted in priority order to first waiting check has
* the highest priority */ * the highest priority */
for (i = conn_check_list; i ; i = i->next) { for (i = conn_check_list; i ; i = i->next) {
CandidateCheckPair *p = i->data; CandidateCheckPair *p = i->data;
if (p->state == NICE_CHECK_WAITING) if (p->state == NICE_CHECK_WAITING)
...@@ -230,6 +312,74 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check ...@@ -230,6 +312,74 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check
return NULL; return NULL;
} }
/*
* Finds the next connectivity check in FROZEN state.
*/
static CandidateCheckPair *
priv_conn_check_find_next_frozen (GSList *conn_check_list)
{
GSList *i;
/* note: list is sorted in priority order to first frozen check has
* the highest priority */
for (i = conn_check_list; i ; i = i->next) {
CandidateCheckPair *p = i->data;
if (p->state == NICE_CHECK_FROZEN)
return p;
}
return NULL;
}
/*
* Returns the number of check lists of the agent
*/
static guint
priv_number_of_check_lists (NiceAgent *agent)
{
guint n = 0;
GSList *i;
for (i = agent->streams; i ; i = i->next) {
NiceStream *stream = i->data;
if (stream->conncheck_list != NULL)
n++;
}
return n;
}
/*
* Returns the number of active check lists of the agent
*/
static guint
priv_number_of_active_check_lists (NiceAgent *agent)
{
guint n = 0;
GSList *i;
for (i = agent->streams; i ; i = i->next)
if (priv_is_checklist_active (i->data))
n++;
return n;
}
/*
* Returns the first stream of the agent having a Frozen
* connection check list
*/
static NiceStream *
priv_find_first_frozen_check_list (NiceAgent *agent)
{
GSList *i;
for (i = agent->streams; i ; i = i->next) {
NiceStream *stream = i->data;
if (priv_is_checklist_frozen (stream))
return stream;
}
return NULL;
}
/* /*
* Initiates a new connectivity check for a ICE candidate pair. * Initiates a new connectivity check for a ICE candidate pair.
* *
...@@ -248,58 +398,55 @@ static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair * ...@@ -248,58 +398,55 @@ static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *
/* /*
* Unfreezes the next connectivity check in the list. Follows the * Unfreezes the next connectivity check in the list. Follows the
* algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec * algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec
* (ID-19), with some exceptions (see comments in code). * (RFC5245)
* *
* See also sect 7.1.2.2.3 (Updating Pair States), and * See also sect 7.1.2.2.3 (Updating Pair States), and
* priv_conn_check_unfreeze_related(). * priv_conn_check_unfreeze_related().
* *
* @return TRUE on success, and FALSE if no frozen candidates were found. * @return TRUE on success, and FALSE if no frozen candidates were found.
*/ */
static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent, NiceStream *stream)
{ {
CandidateCheckPair *pair = NULL;
GSList *i, *j; GSList *i, *j;
GSList *found_list = NULL;
/* XXX: the unfreezing is implemented a bit differently than in the gboolean result = FALSE;
* current ICE spec, but should still be interoperate:
* - checks are not grouped by foundation
* - one frozen check is unfrozen (lowest component-id, highest
* priority)
*/
priv_print_conn_check_lists (agent, G_STRFUNC, NULL); priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
for (i = agent->streams; i; i = i->next) { for (i = stream->conncheck_list; i ; i = i->next) {
NiceStream *stream = i->data; CandidateCheckPair *p1 = i->data;
guint64 max_frozen_priority = 0; CandidateCheckPair *pair = NULL;
guint lowest_component_id = stream->n_components + 1;
guint64 highest_priority = 0;
if (g_slist_find_custom (found_list, p1->foundation, (GCompareFunc)strcmp))
continue;
found_list = g_slist_prepend (found_list, p1->foundation);
for (j = stream->conncheck_list; j ; j = j->next) { for (j = stream->conncheck_list; j ; j = j->next) {
CandidateCheckPair *p = j->data; CandidateCheckPair *p2 = i->data;
if (strncmp (p2->foundation, p1->foundation,
/* XXX: the prio check could be removed as the pairs are sorted NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) {
* already */ if (p2->component_id < lowest_component_id ||
(p2->component_id == lowest_component_id &&
if (p->state == NICE_CHECK_FROZEN) { p2->priority > highest_priority)) {
if (p->priority > max_frozen_priority) { pair = p2;
max_frozen_priority = p->priority; lowest_component_id = p2->component_id;
pair = p; highest_priority = p2->priority;
} }
} }
} }
if (pair)
break;
}
if (pair) { if (pair) {
nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", agent, pair, pair->stream_id, pair->component_id, pair->foundation); nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.",
agent, pair, pair->stream_id, pair->component_id, pair->foundation);
pair->state = NICE_CHECK_WAITING; pair->state = NICE_CHECK_WAITING;
nice_debug ("Agent %p : pair %p state WAITING", agent, pair); nice_debug ("Agent %p : pair %p state WAITING", agent, pair);
return TRUE; result = TRUE;
} }
}
return FALSE; g_slist_free (found_list);
return result;
} }
/* /*
...@@ -316,7 +463,6 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) ...@@ -316,7 +463,6 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent)
static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stream, CandidateCheckPair *ok_check) static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stream, CandidateCheckPair *ok_check)
{ {
GSList *i, *j; GSList *i, *j;
guint unfrozen = 0;
g_assert (ok_check); g_assert (ok_check);
g_assert (ok_check->state == NICE_CHECK_SUCCEEDED); g_assert (ok_check->state == NICE_CHECK_SUCCEEDED);
...@@ -336,40 +482,59 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre ...@@ -336,40 +482,59 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre
nice_debug ("Agent %p : Unfreezing check %p (after successful check %p).", agent, p, ok_check); nice_debug ("Agent %p : Unfreezing check %p (after successful check %p).", agent, p, ok_check);
p->state = NICE_CHECK_WAITING; p->state = NICE_CHECK_WAITING;
nice_debug ("Agent %p : pair %p state WAITING", agent, p); nice_debug ("Agent %p : pair %p state WAITING", agent, p);
++unfrozen;
} }
} }
} }
/* step: perform the step (2) of 'Updating Pair States' */ /* step: perform the step (2) of 'Updating Pair States' */
stream = agent_find_stream (agent, ok_check->stream_id); stream = agent_find_stream (agent, ok_check->stream_id);
if (nice_stream_all_components_ready (stream)) { if (priv_all_components_have_valid_pair (stream)) {
/* step: unfreeze checks from other streams */
for (i = agent->streams; i ; i = i->next) { for (i = agent->streams; i ; i = i->next) {
/* the agent examines the check list for each other
* media stream in turn
*/
NiceStream *s = i->data; NiceStream *s = i->data;
for (j = stream->conncheck_list; j ; j = j->next) { if (s->id == ok_check->stream_id)
continue;
if (priv_is_checklist_active (s)) {
/* checklist is Active
*/
for (j = s->conncheck_list; j ; j = j->next) {
CandidateCheckPair *p = j->data; CandidateCheckPair *p = j->data;
if (p->stream_id == s->id &&
p->stream_id != ok_check->stream_id) {
if (p->state == NICE_CHECK_FROZEN && if (p->state == NICE_CHECK_FROZEN &&
strcmp (p->foundation, ok_check->foundation) == 0) { priv_foundation_matches_a_valid_pair (p->foundation, stream)) {
nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check);
p->state = NICE_CHECK_WAITING; p->state = NICE_CHECK_WAITING;
nice_debug ("Agent %p : pair %p state WAITING", agent, p); nice_debug ("Agent %p : pair %p state WAITING", agent, p);
++unfrozen;
} }
} }
} } else if (priv_is_checklist_frozen (s)) {
/* note: only unfreeze check from one stream at a time */ /* checklist is Frozen
if (unfrozen) */
break; gboolean match_found = FALSE;
/* check if there is one pair in the check list whose
* foundation matches a pair in the valid list under
* consideration
*/
for (j = s->conncheck_list; j ; j = j->next) {
CandidateCheckPair *p = j->data;
if (priv_foundation_matches_a_valid_pair (p->foundation, stream)) {
match_found = TRUE;
nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check);
p->state = NICE_CHECK_WAITING;
nice_debug ("Agent %p : pair %p state WAITING", agent, p);
} }
} }
if (unfrozen == 0) if (!match_found) {
priv_conn_check_unfreeze_next (agent); /* set the pair with the lowest component ID
* and highest priority to Waiting
*/
priv_conn_check_unfreeze_next (agent, s);
}
}
}
}
} }
static void static void
...@@ -400,14 +565,13 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP ...@@ -400,14 +565,13 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP
* *
* @return will return FALSE when no more pending timers. * @return will return FALSE when no more pending timers.
*/ */
static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now, gboolean *stun_transmitted) static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now)
{ {
gboolean keep_timer_going = FALSE; gboolean keep_timer_going = FALSE;
guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0, GSList *i;
s_nominated = 0, s_waiting_for_nomination = 0, s_valid = 0; CandidateCheckPair *pair;
guint frozen = 0, waiting = 0;
GSList *i, *k;
/* step: process ongoing STUN transactions */
for (i = stream->conncheck_list; i ; i = i->next) { for (i = stream->conncheck_list; i ; i = i->next) {
CandidateCheckPair *p = i->data; CandidateCheckPair *p = i->data;
...@@ -451,7 +615,6 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen ...@@ -451,7 +615,6 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen
p->next_tick = *now; p->next_tick = *now;
g_time_val_add (&p->next_tick, timeout * 1000); g_time_val_add (&p->next_tick, timeout * 1000);
*stun_transmitted = TRUE;
return TRUE; return TRUE;
} }
case STUN_USAGE_TIMER_RETURN_SUCCESS: case STUN_USAGE_TIMER_RETURN_SUCCESS:
...@@ -471,7 +634,57 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen ...@@ -471,7 +634,57 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen
} }
} }
} }
}
/* step: perform an ordinary check, ICE spec, 5.8 "Scheduling Checks"
* note: This code is executed when the triggered checks list is
* empty, and when no STUN message has been sent (pacing constraint)
*/
pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
if (pair) {
priv_conn_check_initiate (agent, pair);
return TRUE;
}
/* note: this is unclear in the ICE spec, but a check list in Frozen
* state (where all pairs are in Frozen state) is not supposed to
* change its state by an ordinary check, but only by the success of
* checks in other check lists, in priv_conn_check_unfreeze_related().
* The underlying idea is to concentrate the checks on a single check
* list initially.
*/
if (priv_is_checklist_frozen (stream))
return keep_timer_going;
/* step: ordinary check continued, if there's no pair in the waiting
* state, pick a pair in the frozen state
*/
pair = priv_conn_check_find_next_frozen (stream->conncheck_list);
if (pair) {
pair->state = NICE_CHECK_WAITING;
nice_debug ("Agent %p : pair %p state WAITING", agent, pair);
priv_conn_check_initiate (agent, pair);
return TRUE;
}
return keep_timer_going;
}
static gboolean
priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent)
{
gboolean keep_timer_going = FALSE;
guint s_inprogress = 0;
guint s_succeeded = 0;
guint s_discovered = 0;
guint s_nominated = 0;
guint s_waiting_for_nomination = 0;
guint s_valid = 0;
guint frozen = 0;
guint waiting = 0;
GSList *i, *k;
for (i = stream->conncheck_list; i ; i = i->next) {
CandidateCheckPair *p = i->data;
if (p->state == NICE_CHECK_FROZEN) if (p->state == NICE_CHECK_FROZEN)
++frozen; ++frozen;
else if (p->state == NICE_CHECK_IN_PROGRESS) else if (p->state == NICE_CHECK_IN_PROGRESS)
...@@ -504,13 +717,13 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen ...@@ -504,13 +717,13 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen
keep_timer_going = TRUE; keep_timer_going = TRUE;
if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR &&
agent->controlling_mode && agent->controlling_mode) {
((waiting == 0 && s_inprogress == 0) || #define NICE_MIN_NUMBER_OF_VALID_PAIRS 2
(s_succeeded + s_discovered) >= 5 * stream->n_components)){
/* ICE 8.1.1.1 Regular nomination /* ICE 8.1.1.1 Regular nomination
* we choose to nominate the valid pair if * we choose to nominate the valid pair of a component if
* there is no pair left waiting or in-progress or * - there is no pair left frozen, waiting or in-progress, or
* if there are at least 5 valid pairs per stream on average. * - if there are at least two valid pairs, or
* - if there is at least one valid pair of type HOST-HOST
* *
* This is the "stopping criterion" described in 8.1.1.1, and is * This is the "stopping criterion" described in 8.1.1.1, and is
* a "local optimization" between accumulating more valid pairs, * a "local optimization" between accumulating more valid pairs,
...@@ -523,23 +736,51 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen ...@@ -523,23 +736,51 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen
component_item = component_item->next) { component_item = component_item->next) {
NiceComponent *component = component_item->data; NiceComponent *component = component_item->data;
gboolean already_done = FALSE; gboolean already_done = FALSE;
gboolean stopping_criterion = FALSE;
guint p_valid = 0;
guint p_frozen = 0;
guint p_waiting = 0;
guint p_inprogress = 0;
guint p_host_host_valid = 0;
/* verify that the choice of the pair to be nominated /* verify that the choice of the pair to be nominated
* has not already been done * has not already been done
*/ */
for (k = stream->conncheck_list; k ; k = k->next) { for (k = stream->conncheck_list; k ; k = k->next) {
CandidateCheckPair *p = k->data; CandidateCheckPair *p = k->data;
if (p->component_id == component->id && if (p->component_id == component->id) {
p->use_candidate_on_next_check) { if (p->use_candidate_on_next_check)
already_done = TRUE; already_done = TRUE;
break; if (p->state == NICE_CHECK_FROZEN)
p_frozen++;
else if (p->state == NICE_CHECK_WAITING)
p_waiting++;
else if (p->state == NICE_CHECK_IN_PROGRESS)
p_inprogress++;
if (p->valid)
p_valid++;
if (p->valid &&
p->local->type == NICE_CANDIDATE_TYPE_HOST &&
p->remote->type == NICE_CANDIDATE_TYPE_HOST)
p_host_host_valid++;
} }
} }
/* choose a pair to be nominated in the list of valid if (already_done)
* pairs, and add it to the triggered checks list continue;
stopping_criterion =
(p_host_host_valid > 0 ||
p_valid >= NICE_MIN_NUMBER_OF_VALID_PAIRS ||
(p_waiting == 0 && p_inprogress == 0 && p_frozen == 0));
if (!stopping_criterion)
continue;
/* when the stopping criterion is satisfied, we choose
* a pair to be nominated in the list of valid pairs,
* and add it to the triggered checks list
*/ */
if (!already_done) {
for (k = stream->conncheck_list; k ; k = k->next) { for (k = stream->conncheck_list; k ; k = k->next) {
CandidateCheckPair *p = k->data; CandidateCheckPair *p = k->data;
/* note: highest priority item selected (list always sorted) */ /* note: highest priority item selected (list always sorted) */
...@@ -556,7 +797,6 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen ...@@ -556,7 +797,6 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen
} }
} }
} }
}
} else if (agent->controlling_mode) { } else if (agent->controlling_mode) {
GSList *component_item; GSList *component_item;
...@@ -615,70 +855,55 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) ...@@ -615,70 +855,55 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent)
{ {
CandidateCheckPair *pair = NULL; CandidateCheckPair *pair = NULL;
gboolean keep_timer_going = FALSE; gboolean keep_timer_going = FALSE;
gboolean res;
/* note: we try to only generate a single stun transaction per timer
* callback, to respect some pacing of STUN transaction, as per
* appendix B.1 of ICE spec.
*/
gboolean stun_transmitted = FALSE;
GSList *i, *j; GSList *i, *j;
GTimeVal now; GTimeVal now;
/* step: process ongoing STUN transactions */
g_get_current_time (&now); g_get_current_time (&now);
for (j = agent->streams; j; j = j->next) { /* the conncheck really starts when we have built
NiceStream *stream = j->data; * a connection check list for each stream
res = priv_conn_check_tick_stream (stream, agent, &now, &stun_transmitted); */
if (res) if (priv_number_of_check_lists (agent) < g_slist_length (agent->streams))
keep_timer_going = res;
if (stun_transmitted)
return TRUE; return TRUE;
}
/* step: first initiate a conncheck with a pair from the triggered list */
pair = priv_get_pair_from_triggered_check_queue (agent);
if (pair) { /* configure the initial state of the check lists of the agent
priv_print_conn_check_lists (agent, G_STRFUNC, * as described in ICE spec, 5.7.4
", got a pair from triggered check list"); *
priv_conn_check_initiate (agent, pair); * if all pairs in all check lists are in frozen state, then
return TRUE; * we are in the initial state (5.7.4, point 1.)
*/
if (priv_number_of_active_check_lists (agent) == 0) {
/* set some pairs of the first stream in the waiting state
* ICE spec, 5.7.4, point 2.
*
* note: we adapt the ICE spec here, by selecting the first
* frozen check list, which is not necessarily the check
* list of the first stream (the first stream may be completed)
*/
NiceStream *stream = priv_find_first_frozen_check_list (agent);
if (stream)
priv_conn_check_unfreeze_next (agent, stream);
} }
/* step: when the triggered list is empty, /* step: perform a test from the triggered checks list,
* find the highest priority waiting check and send it */ * ICE spec, 5.8 "Scheduling Checks"
for (i = agent->streams; i ; i = i->next) { */
NiceStream *stream = i->data; pair = priv_get_pair_from_triggered_check_queue (agent);
pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
if (pair)
break;
}
if (pair) { if (pair) {
priv_conn_check_initiate (agent, pair); priv_conn_check_initiate (agent, pair);
return TRUE; return TRUE;
} }
/* step: when there's no pair in the Waiting state, /* step: process ongoing STUN transactions and
* unfreeze a new pair and check it * perform an ordinary check, ICE spec, 5.8, "Scheduling Checks"
*/ */
priv_conn_check_unfreeze_next (agent);
for (i = agent->streams; i ; i = i->next) { for (i = agent->streams; i ; i = i->next) {
NiceStream *stream = i->data; NiceStream *stream = i->data;
if (priv_conn_check_tick_stream (stream, agent, &now))
pair = priv_conn_check_find_next_waiting (stream->conncheck_list); keep_timer_going = TRUE;
if (pair) if (priv_conn_check_tick_stream_nominate (stream, agent))
break; keep_timer_going = TRUE;
}
if (pair) {
priv_print_conn_check_lists (agent, G_STRFUNC,
", got a pair in Waiting state");
priv_conn_check_initiate (agent, pair);
return TRUE;
} }
/* step: stop timer if no work left */ /* step: stop timer if no work left */
...@@ -2169,30 +2394,28 @@ size_t priv_get_password (NiceAgent *agent, NiceStream *stream, ...@@ -2169,30 +2394,28 @@ size_t priv_get_password (NiceAgent *agent, NiceStream *stream,
/* Implement the computation specific in RFC 5245 section 16 */ /* Implement the computation specific in RFC 5245 section 16 */
static unsigned int priv_compute_conncheck_timer (NiceAgent *agent) static unsigned int priv_compute_conncheck_timer (NiceAgent *agent, NiceStream *stream)
{ {
GSList *item1, *item2; GSList *i;
guint waiting_and_in_progress = 0; guint waiting_and_in_progress = 0;
guint n = 0;
unsigned int rto = 0; unsigned int rto = 0;
for (i = stream->conncheck_list; i ; i = i->next) {
for (item1 = agent->streams; item1; item1 = item1->next) { CandidateCheckPair *p = i->data;
NiceStream *stream = item1->data;; if (p->state == NICE_CHECK_IN_PROGRESS ||
for (item2 = stream->conncheck_list; item2; item2 = item2->next) { p->state == NICE_CHECK_WAITING)
CandidateCheckPair *pair = item2->data;
if (pair->state == NICE_CHECK_IN_PROGRESS ||
pair->state == NICE_CHECK_WAITING)
waiting_and_in_progress++; waiting_and_in_progress++;
} }
}
rto = agent->timer_ta * waiting_and_in_progress; n = priv_number_of_active_check_lists (agent);
rto = agent->timer_ta * n * waiting_and_in_progress;
/* We assume non-reliable streams are RTP, so we use 100 as the max */ /* We assume non-reliable streams are RTP, so we use 100 as the max */
nice_debug ("Agent %p : timer set to %dms (waiting+in_progress=%d)", nice_debug ("Agent %p : timer set to %dms, "
"waiting+in_progress=%d, nb_active=%d",
agent, agent->reliable ? MAX (rto, 500) : MAX (rto, 100), agent, agent->reliable ? MAX (rto, 500) : MAX (rto, 100),
waiting_and_in_progress); waiting_and_in_progress, n);
if (agent->reliable) if (agent->reliable)
return MAX (rto, 500); return MAX (rto, 500);
else else
...@@ -2312,7 +2535,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) ...@@ -2312,7 +2535,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout);
} else { } else {
stun_timer_start (&pair->timer, stun_timer_start (&pair->timer,
priv_compute_conncheck_timer (agent), priv_compute_conncheck_timer (agent, stream),
agent->stun_max_retransmissions); agent->stun_max_retransmissions);
} }
...@@ -2477,7 +2700,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str ...@@ -2477,7 +2700,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str
p->timer_restarted ? "no" : "yes"); p->timer_restarted ? "no" : "yes");
if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) {
stun_timer_start (&p->timer, stun_timer_start (&p->timer,
priv_compute_conncheck_timer (agent), priv_compute_conncheck_timer (agent, stream),
agent->stun_max_retransmissions); agent->stun_max_retransmissions);
p->timer_restarted = TRUE; p->timer_restarted = TRUE;
} }
...@@ -2769,7 +2992,6 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * ...@@ -2769,7 +2992,6 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent *
p->valid = TRUE; p->valid = TRUE;
p->state = NICE_CHECK_SUCCEEDED; p->state = NICE_CHECK_SUCCEEDED;
nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p);
priv_conn_check_unfreeze_related (agent, stream, p);
nice_component_add_valid_candidate (component, remote_candidate); nice_component_add_valid_candidate (component, remote_candidate);
} }
else { else {
...@@ -2894,7 +3116,6 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre ...@@ -2894,7 +3116,6 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre
g_assert_not_reached (); g_assert_not_reached ();
nice_debug ("Agent %p : Mapped address not found." nice_debug ("Agent %p : Mapped address not found."
" conncheck %p SUCCEEDED.", agent, p); " conncheck %p SUCCEEDED.", agent, p);
priv_conn_check_unfreeze_related (agent, stream, p);
nice_component_add_valid_candidate (component, p->remote); nice_component_add_valid_candidate (component, p->remote);
} else { } else {
ok_pair = priv_process_response_check_for_reflexive (agent, ok_pair = priv_process_response_check_for_reflexive (agent,
...@@ -2902,6 +3123,12 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre ...@@ -2902,6 +3123,12 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre
local_candidate, remote_candidate); local_candidate, remote_candidate);
} }
/* note: The success of this check might also
* cause the state of other checks to change as well, ICE
* spec 7.1.3.2.3
*/
priv_conn_check_unfreeze_related (agent, stream, p);
/* Note: this assignment helps to reduce the numbers of cases /* Note: this assignment helps to reduce the numbers of cases
* to be tested. If ok_pair and p refer to distinct pairs, it * to be tested. If ok_pair and p refer to distinct pairs, it
* means that ok_pair is a discovered peer reflexive one, * means that ok_pair is a discovered peer reflexive one,
......
...@@ -103,27 +103,6 @@ nice_stream_find_component_by_id (NiceStream *stream, guint id) ...@@ -103,27 +103,6 @@ nice_stream_find_component_by_id (NiceStream *stream, guint id)
return NULL; return NULL;
} }
/*
* Returns true if all components of the stream are either
* 'CONNECTED' or 'READY' (connected plus nominated).
*/
gboolean
nice_stream_all_components_ready (NiceStream *stream)
{
GSList *i;
for (i = stream->components; i; i = i->next) {
NiceComponent *component = i->data;
if (component &&
!(component->state == NICE_COMPONENT_STATE_CONNECTED ||
component->state == NICE_COMPONENT_STATE_READY))
return FALSE;
}
return TRUE;
}
/* /*
* Initialized the local crendentials for the stream. * Initialized the local crendentials for the stream.
*/ */
......
...@@ -103,9 +103,6 @@ nice_stream_new (guint n_components, NiceAgent *agent); ...@@ -103,9 +103,6 @@ nice_stream_new (guint n_components, NiceAgent *agent);
void void
nice_stream_close (NiceStream *stream); nice_stream_close (NiceStream *stream);
gboolean
nice_stream_all_components_ready (NiceStream *stream);
NiceComponent * NiceComponent *
nice_stream_find_component_by_id (NiceStream *stream, guint id); nice_stream_find_component_by_id (NiceStream *stream, guint id);
......
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