MINOR: freq_ctr: Add a function to get events excess over the current period
freq_ctr_overshoot_period() function may be used to retrieve the excess of
events over the current period for a givent frequency counter, ignoring the
history. It is a way compare the "current rate" (the number of events over
the current period) to a given rate and estimate the excess of events.
It may be used to safely add new events, especially at the begining of the
current period for a frequency counter with large period. This way, it is
possible to smoothly add events during the whole period without quickly
consuming all the quota at the beginning of the period and waiting for the
next one to be able to add new events.
diff --git a/include/haproxy/freq_ctr.h b/include/haproxy/freq_ctr.h
index e0f5080..be9c9d8 100644
--- a/include/haproxy/freq_ctr.h
+++ b/include/haproxy/freq_ctr.h
@@ -29,6 +29,7 @@
/* exported functions from freq_ctr.c */
ullong freq_ctr_total(const struct freq_ctr *ctr, uint period, int pend);
+int freq_ctr_overshoot_period(const struct freq_ctr *ctr, uint period, uint freq);
/* Update a frequency counter by <inc> incremental units. It is automatically
* rotated if the period is over. It is important that it correctly initializes
diff --git a/src/freq_ctr.c b/src/freq_ctr.c
index 54aa78f..9d9ab3b 100644
--- a/src/freq_ctr.c
+++ b/src/freq_ctr.c
@@ -96,6 +96,64 @@
return past * remain + (curr + pend) * period;
}
+/* Returns the excess of events (may be negative) over the current period for
+ * target frequency <freq>. It returns 0 if the counter is in the future. The
+ * result considers the position of the current time within the current period.
+ *
+ * The caller may safely add new events if result is negative or null.
+ */
+int freq_ctr_overshoot_period(const struct freq_ctr *ctr, uint period, uint freq)
+{
+ uint curr, old_curr;
+ uint tick, old_tick;
+ int elapsed;
+
+ tick = HA_ATOMIC_LOAD(&ctr->curr_tick);
+ curr = HA_ATOMIC_LOAD(&ctr->curr_ctr);
+
+ while (1) {
+ if (tick & 0x1) // change in progress
+ goto redo0;
+
+ old_tick = tick;
+ old_curr = curr;
+
+ /* now let's load the values a second time and make sure they
+ * did not change, which will indicate it was a stable reading.
+ */
+
+ tick = HA_ATOMIC_LOAD(&ctr->curr_tick);
+ if (tick & 0x1) // change in progress
+ goto redo0;
+
+ if (tick != old_tick)
+ goto redo1;
+
+ curr = HA_ATOMIC_LOAD(&ctr->curr_ctr);
+ if (curr != old_curr)
+ goto redo2;
+
+ /* all values match between two loads, they're stable, let's
+ * quit now.
+ */
+ break;
+ redo0:
+ tick = HA_ATOMIC_LOAD(&ctr->curr_tick);
+ redo1:
+ curr = HA_ATOMIC_LOAD(&ctr->curr_ctr);
+ redo2:
+ __ha_cpu_relax();
+ };
+
+ elapsed = HA_ATOMIC_LOAD(&global_now_ms) - tick;
+ if (unlikely(elapsed < 0)) {
+ /* The counter is in the future, there is no overshoot */
+ return 0;
+ }
+
+ return curr - div64_32((uint64_t)elapsed * freq, period);
+}
+
/*
* Local variables:
* c-indent-level: 8