infra: fix limiter overflow in denominator

This commit is contained in:
Thomas Schoebel-Theuer 2013-11-20 14:33:38 +01:00
parent 8696e417db
commit 8b74dddc24

View File

@ -18,16 +18,17 @@ int mars_limit(struct mars_limiter *lim, int amount)
* down to the root of the hierarchy tree. * down to the root of the hierarchy tree.
*/ */
while (lim != NULL) { while (lim != NULL) {
long long elapsed = now - lim->lim_stamp; long long window = now - lim->lim_stamp;
/* Sometimes, raw CPU clocks may do weired things... /* Sometimes, raw CPU clocks may do weired things...
* Smaller windows in the denominator than 1s could fake unrealistic rates.
*/ */
if (unlikely(elapsed <= 0)) if (unlikely(window < LIMITER_TIME_RESOLUTION))
elapsed = 1; window = LIMITER_TIME_RESOLUTION;
/* Only use incremental accumulation at repeated calls, but /* Only use incremental accumulation at repeated calls, but
* never after longer pauses. * never after longer pauses.
*/ */
if (likely(lim->lim_stamp && elapsed < LIMITER_TIME_RESOLUTION * 8)) { if (likely(lim->lim_stamp && window < LIMITER_TIME_RESOLUTION * 8)) {
long long rate_raw; long long rate_raw;
int rate; int rate;
@ -39,7 +40,7 @@ int mars_limit(struct mars_limiter *lim, int amount)
lim->lim_cumul += amount; lim->lim_cumul += amount;
} }
rate_raw = lim->lim_accu * LIMITER_TIME_RESOLUTION / elapsed; rate_raw = lim->lim_accu * LIMITER_TIME_RESOLUTION / window;
rate = rate_raw; rate = rate_raw;
if (unlikely(rate_raw > INT_MAX)) { if (unlikely(rate_raw > INT_MAX)) {
rate = INT_MAX; rate = INT_MAX;
@ -53,20 +54,21 @@ int mars_limit(struct mars_limiter *lim, int amount)
if (this_delay > delay) if (this_delay > delay)
delay = this_delay; delay = this_delay;
} }
elapsed -= LIMITER_TIME_RESOLUTION * 2; /* Try to keep the next window below 2s
if (elapsed > LIMITER_TIME_RESOLUTION) { */
lim->lim_stamp += elapsed; window -= LIMITER_TIME_RESOLUTION;
lim->lim_accu -= (unsigned long long)lim->lim_rate * (unsigned long long)elapsed / LIMITER_TIME_RESOLUTION; if (window > LIMITER_TIME_RESOLUTION) {
if (lim->lim_accu < 0) { lim->lim_stamp += window;
lim->lim_accu -= (unsigned long long)lim->lim_rate * (unsigned long long)window / LIMITER_TIME_RESOLUTION;
if (unlikely(lim->lim_accu < 0))
lim->lim_accu = 0; lim->lim_accu = 0;
}
} }
} else { } else { // reset, start over with new measurement cycle
if (unlikely(amount < 0)) if (unlikely(amount < 0))
amount = 0; amount = 0;
lim->lim_accu = amount; lim->lim_accu = amount;
lim->lim_stamp = now; lim->lim_stamp = now - LIMITER_TIME_RESOLUTION;
lim->lim_rate = 0; lim->lim_rate = 0;
} }
lim = lim->lim_father; lim = lim->lim_father;