This adds "struct ratelimit" and "ratelimit_test()". It's a very simple rate-limit helper modeled after Linux' lib/ratelimit.c by Dave Young.
This comes in handy to limit log-messages in possible busy loops etc.. Signed-off-by: David Herrmann <[email protected]> --- src/libinput-util.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ src/libinput-util.h | 19 +++++++++++++++++++ test/misc.c | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/src/libinput-util.c b/src/libinput-util.c index eeb9786..19594e3 100644 --- a/src/libinput-util.c +++ b/src/libinput-util.c @@ -65,3 +65,51 @@ list_empty(const struct list *list) { return list->next == list; } + +/* + * Perform rate-limit test. Returns true if the rate-limited action is still + * allowed, false if it should be suppressed. + * + * The ratelimit object must be initialized via RATELIMIT_INIT(). + * + * Modelled after Linux' lib/ratelimit.c by Dave Young + * <[email protected]>, which is licensed GPLv2. + */ +bool ratelimit_test(struct ratelimit *r) +{ + struct timespec ts; + uint64_t utime; + + if (r->interval <= 0 || r->burst <= 0) + return true; + + clock_gettime(CLOCK_MONOTONIC, &ts); + utime = ts.tv_sec * 1000 * 1000 + ts.tv_nsec / 1000; + + if (r->begin <= 0 || r->begin + r->interval < utime) { + /* reset counter */ + r->begin = utime; + r->num = 1; + return true; + } else if (r->num < r->burst) { + /* continue burst */ + r->num++; + return true; + } + + /* rate-limit with overflow check */ + if (r->num + 1 > r->num) + ++r->num; + + return false; +} + +/* + * Return true if the ratelimit counter just crossed the cutoff value. That is, + * this function returns true iff the last call to ratelimit_test() was the + * first call to exceed the burst value in this interval. + */ +bool ratelimit_cutoff(struct ratelimit *r) +{ + return r->num == r->burst + 1; +} diff --git a/src/libinput-util.h b/src/libinput-util.h index 51759e8..8ff8778 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -25,6 +25,7 @@ #include <unistd.h> #include <math.h> +#include <stdbool.h> #include <string.h> #include <time.h> @@ -280,4 +281,22 @@ matrix_to_farray6(const struct matrix *m, float out[6]) out[5] = m->val[1][2]; } +struct ratelimit { + uint64_t interval; + uint64_t begin; + unsigned burst; + unsigned num; +} RateLimit; + +#define RATELIMIT_INIT(_interval, _burst) \ + ((struct ratelimit){ \ + .interval = (_interval), \ + .begin = 0, \ + .burst = (_burst), \ + .num = 0, \ + }) + +bool ratelimit_test(struct ratelimit *r); +bool ratelimit_cutoff(struct ratelimit *r); + #endif /* LIBINPUT_UTIL_H */ diff --git a/test/misc.c b/test/misc.c index 1512180..70b3e57 100644 --- a/test/misc.c +++ b/test/misc.c @@ -508,6 +508,42 @@ START_TEST(matrix_helpers) } END_TEST +START_TEST(ratelimit_helpers) +{ + /* 10 attempts every 10ms */ + struct ratelimit rl = RATELIMIT_INIT(10000, 10); + unsigned int i, j; + + for (j = 0; j < 100; ++j) { + /* a burst of 10 attempts must succeed */ + for (i = 0; i < 10; ++i) { + ck_assert(ratelimit_test(&rl)); + ck_assert(!ratelimit_cutoff(&rl)); + } + + /* ..then further attempts must fail.. */ + ck_assert(!ratelimit_test(&rl)); + ck_assert(ratelimit_cutoff(&rl)); + + /* ..regardless of how often we try. */ + for (i = 0; i < 100; ++i) { + ck_assert(!ratelimit_test(&rl)); + ck_assert(!ratelimit_cutoff(&rl)); + } + + /* ..even after waiting 5ms */ + usleep(5000); + for (i = 0; i < 100; ++i) { + ck_assert(!ratelimit_test(&rl)); + ck_assert(!ratelimit_cutoff(&rl)); + } + + /* but after 10ms the counter is reset */ + usleep(6000); /* +1ms to account for time drifts */ + } +} +END_TEST + int main (int argc, char **argv) { litest_add_no_device("events:conversion", event_conversion_device_notify); litest_add_no_device("events:conversion", event_conversion_pointer); @@ -519,5 +555,6 @@ int main (int argc, char **argv) { litest_add_no_device("config:status string", config_status_string); litest_add_no_device("misc:matrix", matrix_helpers); + litest_add_no_device("misc:ratelimit", ratelimit_helpers); return litest_run(argc, argv); } -- 2.1.3 _______________________________________________ wayland-devel mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/wayland-devel
