On Wed, Nov 05, 2014 at 01:32:16PM +0100, David Herrmann wrote:
> 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 | 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
> + * <[email protected]>, 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);

merged both, with changes to ck_assert_int_eq:
                ck_assert_int_eq(ratelimit_test(&rl), RATELIMIT_THRESHOLD);

a bit quicker to debug when it fails, the error message will print both
values so you get an idea of what the mismatch is.

thanks

Cheers,
   Peter


> +
> +             /* ..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
> 
_______________________________________________
wayland-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to