diff options
author | Nick Mathewson <nickm@torproject.org> | 2016-06-23 09:01:24 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2016-06-23 09:01:24 -0400 |
commit | 703254a8321788bd6d03ec5f335fe338916fef6f (patch) | |
tree | 6decd78f0fc701c0c8f068089a4fa7cfa4cdc433 /src/or/directory.c | |
parent | 61dac57aa3790464dd50da9bbc78fd275f511f3d (diff) | |
parent | a09ec22a9b1d213716ac1792752c266c3a92a1f6 (diff) | |
download | tor-703254a8321788bd6d03ec5f335fe338916fef6f.tar.gz tor-703254a8321788bd6d03ec5f335fe338916fef6f.zip |
Merge remote-tracking branch 'public/bug15942_v2_alternative'
Diffstat (limited to 'src/or/directory.c')
-rw-r--r-- | src/or/directory.c | 129 |
1 files changed, 118 insertions, 11 deletions
diff --git a/src/or/directory.c b/src/or/directory.c index 876eaa4415..5886a73403 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -3763,17 +3763,84 @@ find_dl_schedule(download_status_t *dls, const or_options_t *options) return NULL; } -/* Find the current delay for dls based on schedule. - * Set dls->next_attempt_at based on now, and return the delay. +/** Decide which minimum and maximum delay step we want to use based on + * descriptor type in <b>dls</b> and <b>options</b>. + * Helper function for download_status_schedule_get_delay(). */ +STATIC void +find_dl_min_and_max_delay(download_status_t *dls, const or_options_t *options, + int *min, int *max) +{ + tor_assert(dls); + tor_assert(options); + tor_assert(min); + tor_assert(max); + + /* + * For now, just use the existing schedule config stuff and pick the + * first/last entries off to get min/max delay for backoff purposes + */ + const smartlist_t *schedule = find_dl_schedule(dls, options); + tor_assert(schedule != NULL && smartlist_len(schedule) >= 2); + *min = *((int *)(smartlist_get(schedule, 0))); + *max = *((int *)((smartlist_get(schedule, smartlist_len(schedule) - 1)))); +} + +/** Advance one delay step. The algorithm is to use the previous delay to + * compute an increment, we construct a value uniformly at random between + * delay and MAX(delay*2,delay+1). We then clamp that value to be no larger + * than max_delay, and return it. + * + * Requires that delay is less than INT_MAX, and delay is in [0,max_delay]. + */ +STATIC int +next_random_exponential_delay(int delay, int max_delay) +{ + /* Check preconditions */ + if (BUG(delay > max_delay)) + delay = max_delay; + if (BUG(delay == INT_MAX)) + delay -= 1; /* prevent overflow */ + if (BUG(delay < 0)) + delay = 0; + + /* How much are we willing to add to the delay? */ + int max_increment; + + if (delay) + max_increment = delay; /* no more than double. */ + else + max_increment = 1; /* we're always willing to slow down a little. */ + + /* the + 1 here is so that we include the end of the interval */ + int increment = crypto_rand_int(max_increment+1); + + if (increment < max_delay - delay) + return delay + increment; + else + return max_delay; +} + +/** Find the current delay for dls based on schedule or min_delay/ + * max_delay if we're using exponential backoff. If dls->backoff is + * DL_SCHED_RANDOM_EXPONENTIAL, we must have 0 <= min_delay <= max_delay <= + * INT_MAX, but schedule may be set to NULL; otherwise schedule is required. + * This function sets dls->next_attempt_at based on now, and returns the delay. * Helper for download_status_increment_failure and * download_status_increment_attempt. */ STATIC int download_status_schedule_get_delay(download_status_t *dls, const smartlist_t *schedule, + int min_delay, int max_delay, time_t now) { tor_assert(dls); - tor_assert(schedule); + /* We don't need a schedule if we're using random exponential backoff */ + tor_assert(dls->backoff == DL_SCHED_RANDOM_EXPONENTIAL || + schedule != NULL); + /* If we're using random exponential backoff, we do need min/max delay */ + tor_assert(dls->backoff != DL_SCHED_RANDOM_EXPONENTIAL || + (min_delay >= 0 && max_delay >= min_delay && + max_delay <= INT_MAX)); int delay = INT_MAX; uint8_t dls_schedule_position = (dls->increment_on @@ -3781,12 +3848,42 @@ download_status_schedule_get_delay(download_status_t *dls, ? dls->n_download_attempts : dls->n_download_failures); - if (dls_schedule_position < smartlist_len(schedule)) - delay = *(int *)smartlist_get(schedule, dls_schedule_position); - else if (dls_schedule_position == IMPOSSIBLE_TO_DOWNLOAD) - delay = INT_MAX; - else - delay = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1); + if (dls->backoff == DL_SCHED_DETERMINISTIC) { + if (dls_schedule_position < smartlist_len(schedule)) + delay = *(int *)smartlist_get(schedule, dls_schedule_position); + else if (dls_schedule_position == IMPOSSIBLE_TO_DOWNLOAD) + delay = INT_MAX; + else + delay = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1); + } else if (dls->backoff == DL_SCHED_RANDOM_EXPONENTIAL) { + /* Check if we missed a reset somehow */ + if (dls->last_backoff_position > dls_schedule_position) { + dls->last_backoff_position = 0; + dls->last_delay_used = 0; + } + + if (dls_schedule_position > 0) { + delay = dls->last_delay_used; + + while (dls->last_backoff_position < dls_schedule_position) { + /* Do one increment step */ + delay = next_random_exponential_delay(delay, max_delay); + /* Update our position */ + ++(dls->last_backoff_position); + } + } else { + /* If we're just starting out, use the minimum delay */ + delay = min_delay; + } + + /* Clamp it within min/max if we have them */ + if (min_delay >= 0 && delay < min_delay) delay = min_delay; + if (max_delay != INT_MAX && delay > max_delay) delay = max_delay; + + /* Store it for next time */ + dls->last_backoff_position = dls_schedule_position; + dls->last_delay_used = delay; + } /* A negative delay makes no sense. Knowing that delay is * non-negative allows us to safely do the wrapping check below. */ @@ -3847,6 +3944,8 @@ download_status_increment_failure(download_status_t *dls, int status_code, const char *item, int server, time_t now) { int increment = -1; + int min_delay = 0, max_delay = INT_MAX; + tor_assert(dls); /* only count the failure if it's permanent, or we're a server */ @@ -3867,7 +3966,9 @@ download_status_increment_failure(download_status_t *dls, int status_code, /* only return a failure retry time if this schedule increments on failures */ const smartlist_t *schedule = find_dl_schedule(dls, get_options()); - increment = download_status_schedule_get_delay(dls, schedule, now); + find_dl_min_and_max_delay(dls, get_options(), &min_delay, &max_delay); + increment = download_status_schedule_get_delay(dls, schedule, + min_delay, max_delay, now); } download_status_log_helper(item, !dls->increment_on, "failed", @@ -3896,6 +3997,8 @@ download_status_increment_attempt(download_status_t *dls, const char *item, time_t now) { int delay = -1; + int min_delay = 0, max_delay = INT_MAX; + tor_assert(dls); if (dls->increment_on == DL_SCHED_INCREMENT_FAILURE) { @@ -3910,7 +4013,9 @@ download_status_increment_attempt(download_status_t *dls, const char *item, ++dls->n_download_attempts; const smartlist_t *schedule = find_dl_schedule(dls, get_options()); - delay = download_status_schedule_get_delay(dls, schedule, now); + find_dl_min_and_max_delay(dls, get_options(), &min_delay, &max_delay); + delay = download_status_schedule_get_delay(dls, schedule, + min_delay, max_delay, now); download_status_log_helper(item, dls->increment_on, "attempted", "on failure", dls->n_download_attempts, @@ -3942,6 +4047,8 @@ download_status_reset(download_status_t *dls) dls->n_download_failures = 0; dls->n_download_attempts = 0; dls->next_attempt_at = time(NULL) + *(int *)smartlist_get(schedule, 0); + dls->last_backoff_position = 0; + dls->last_delay_used = 0; /* Don't reset dls->want_authority or dls->increment_on */ } |