1 /* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "ioloop.h"
5 #include "time-util.h"
6 #include "log-throttle.h"
7 
8 struct log_throttle {
9 	struct log_throttle_settings set;
10 	log_throttle_callback_t *callback;
11 	void *context;
12 
13 	struct timeval last_time;
14 	unsigned int last_count;
15 
16 	struct timeout *to_throttled;
17 };
18 
19 #undef log_throttle_init
20 struct log_throttle *
log_throttle_init(const struct log_throttle_settings * set,log_throttle_callback_t * callback,void * context)21 log_throttle_init(const struct log_throttle_settings *set,
22 		  log_throttle_callback_t *callback, void *context)
23 {
24 	struct log_throttle *throttle;
25 
26 	i_assert(set->throttle_at_max_per_interval > 0);
27 	i_assert(set->unthrottle_at_max_per_interval > 0);
28 
29 	throttle = i_new(struct log_throttle, 1);
30 	throttle->set = *set;
31 	if (throttle->set.interval_msecs == 0)
32 		throttle->set.interval_msecs = 1000;
33 	throttle->callback = callback;
34 	throttle->context = context;
35 	throttle->last_time = ioloop_timeval;
36 	return throttle;
37 }
38 
log_throttle_deinit(struct log_throttle ** _throttle)39 void log_throttle_deinit(struct log_throttle **_throttle)
40 {
41 	struct log_throttle *throttle = *_throttle;
42 
43 	*_throttle = NULL;
44 	timeout_remove(&throttle->to_throttled);
45 	i_free(throttle);
46 }
47 
log_throttle_callback(struct log_throttle * throttle)48 static void log_throttle_callback(struct log_throttle *throttle)
49 {
50 	if (throttle->last_count > 0)
51 		throttle->callback(throttle->last_count, throttle->context);
52 	if (throttle->last_count < throttle->set.unthrottle_at_max_per_interval)
53 		timeout_remove(&throttle->to_throttled);
54 	throttle->last_count = 0;
55 }
56 
log_throttle_accept(struct log_throttle * throttle)57 bool log_throttle_accept(struct log_throttle *throttle)
58 {
59 	if (throttle->to_throttled != NULL) {
60 		/* unthrottling and last_count resets are done only by
61 		   the callback */
62 		throttle->last_count++;
63 		return FALSE;
64 	} else if (timeval_diff_msecs(&ioloop_timeval, &throttle->last_time) >=
65 				(int)throttle->set.interval_msecs) {
66 		throttle->last_time = ioloop_timeval;
67 		throttle->last_count = 1;
68 		return TRUE;
69 	} else if (++throttle->last_count <= throttle->set.throttle_at_max_per_interval) {
70 		return TRUE;
71 	} else {
72 		throttle->last_count = 1;
73 		throttle->to_throttled =
74 			timeout_add(throttle->set.interval_msecs,
75 				    log_throttle_callback, throttle);
76 		return FALSE;
77 	}
78 }
79