[PATCH libinput v2 1/2] util: introduce ratelimit helpers

David Herrmann dh.herrmann at gmail.com
Wed Nov 5 04:32:16 PST 2014


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 <dh.herrmann at gmail.com>
---
 src/libinput-util.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/libinput-util.h | 16 ++++++++++++++++
 test/misc.c         | 36 ++++++++++++++++++++++++++++++++++++
 3 files changed, 100 insertions(+)

diff --git a/src/libinput-util.c b/src/libinput-util.c
index eeb9786..34d5549 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;
 }
+
+void
+ratelimit_init(struct ratelimit *r, uint64_t ival_ms, unsigned int burst)
+{
+	r->interval = ival_ms;
+	r->begin = 0;
+	r->burst = burst;
+	r->num = 0;
+}
+
+/*
+ * Perform rate-limit test. Returns RATELIMIT_PASS if the rate-limited action
+ * is still allowed, RATELIMIT_THRESHOLD if the limit has been reached with
+ * this call, and RATELIMIT_EXCEEDED if you're beyond the threshold.
+ * It's safe to treat the return-value as boolean, if you're not interested in
+ * the exact state. It evaluates to "true" if the threshold hasn't been
+ * exceeded, yet.
+ *
+ * The ratelimit object must be initialized via ratelimit_init().
+ *
+ * Modelled after Linux' lib/ratelimit.c by Dave Young
+ * <hidave.darkstar at gmail.com>, which is licensed GPLv2.
+ */
+enum ratelimit_state
+ratelimit_test(struct ratelimit *r)
+{
+	struct timespec ts;
+	uint64_t mtime;
+
+	if (r->interval <= 0 || r->burst <= 0)
+		return RATELIMIT_PASS;
+
+	clock_gettime(CLOCK_MONOTONIC, &ts);
+	mtime = ts.tv_sec * 1000 + ts.tv_nsec / 1000 / 1000;
+
+	if (r->begin <= 0 || r->begin + r->interval < mtime) {
+		/* reset counter */
+		r->begin = mtime;
+		r->num = 1;
+		return RATELIMIT_PASS;
+	} else if (r->num < r->burst) {
+		/* continue burst */
+		return (++r->num == r->burst) ? RATELIMIT_THRESHOLD
+					      : RATELIMIT_PASS;
+	}
+
+	return RATELIMIT_EXCEEDED;
+}
diff --git a/src/libinput-util.h b/src/libinput-util.h
index 51759e8..909c9db 100644
--- a/src/libinput-util.h
+++ b/src/libinput-util.h
@@ -280,4 +280,20 @@ matrix_to_farray6(const struct matrix *m, float out[6])
 	out[5] = m->val[1][2];
 }
 
+enum ratelimit_state {
+	RATELIMIT_EXCEEDED,
+	RATELIMIT_THRESHOLD,
+	RATELIMIT_PASS,
+};
+
+struct ratelimit {
+	uint64_t interval;
+	uint64_t begin;
+	unsigned int burst;
+	unsigned int num;
+};
+
+void ratelimit_init(struct ratelimit *r, uint64_t ival_ms, unsigned int burst);
+enum ratelimit_state ratelimit_test(struct ratelimit *r);
+
 #endif /* LIBINPUT_UTIL_H */
diff --git a/test/misc.c b/test/misc.c
index 1512180..aa411ec 100644
--- a/test/misc.c
+++ b/test/misc.c
@@ -508,6 +508,40 @@ START_TEST(matrix_helpers)
 }
 END_TEST
 
+START_TEST(ratelimit_helpers)
+{
+	struct ratelimit rl;
+	unsigned int i, j;
+
+	/* 10 attempts every 10ms */
+	ratelimit_init(&rl, 10, 10);
+
+	for (j = 0; j < 100; ++j) {
+		/* a burst of 9 attempts must succeed */
+		for (i = 0; i < 9; ++i)
+			ck_assert(ratelimit_test(&rl) == RATELIMIT_PASS);
+
+		/* the 10th attempt reaches the threshold */
+		ck_assert(ratelimit_test(&rl) == RATELIMIT_THRESHOLD);
+
+		/* ..then further attempts must fail.. */
+		ck_assert(ratelimit_test(&rl) == RATELIMIT_EXCEEDED);
+
+		/* ..regardless of how often we try. */
+		for (i = 0; i < 100; ++i)
+			ck_assert(ratelimit_test(&rl) == RATELIMIT_EXCEEDED);
+
+		/* ..even after waiting 5ms */
+		msleep(5);
+		for (i = 0; i < 100; ++i)
+			ck_assert(ratelimit_test(&rl) == RATELIMIT_EXCEEDED);
+
+		/* but after 10ms the counter is reset */
+		msleep(6); /* +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 +553,7 @@ 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



More information about the wayland-devel mailing list