1 /*
2  * librdkafka - Apache Kafka C library
3  *
4  * Copyright (c) 2018 Magnus Edenhill
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  *    this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  *    this list of conditions and the following disclaimer in the documentation
14  *    and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #ifndef _RDINTERVAL_H_
30 #define _RDINTERVAL_H_
31 
32 #include "rd.h"
33 
34 typedef struct rd_interval_s {
35         rd_ts_t    ri_ts_last; /* last interval timestamp */
36         rd_ts_t    ri_fixed;   /* fixed interval if provided interval is 0 */
37         int        ri_backoff; /* back off the next interval by this much */
38 } rd_interval_t;
39 
40 
rd_interval_init(rd_interval_t * ri)41 static RD_INLINE RD_UNUSED void rd_interval_init (rd_interval_t *ri) {
42         memset(ri, 0, sizeof(*ri));
43 }
44 
45 
46 
47 /**
48  * Returns the number of microseconds the interval has been over-shot.
49  * If the return value is >0 (i.e., time for next intervalled something) then
50  * the time interval is updated to the current time.
51  *
52  * The current time can be provided in 'now', or if this is set to 0 the time
53  * will be gathered automatically.
54  *
55  * If 'interval_us' is set to 0 the fixed interval will be used, see
56  * 'rd_interval_fixed()'.
57  *
58  * If this is the first time rd_interval() is called after an _init() or
59  * _reset() or the \p immediate parameter is true, then a positive value
60  * will be returned immediately even though the initial interval has not
61  * passed.
62  */
63 #define rd_interval(ri,interval_us,now) rd_interval0(ri,interval_us,now,0)
64 #define rd_interval_immediate(ri,interval_us,now) \
65 	rd_interval0(ri,interval_us,now,1)
rd_interval0(rd_interval_t * ri,rd_ts_t interval_us,rd_ts_t now,int immediate)66 static RD_INLINE RD_UNUSED rd_ts_t rd_interval0 (rd_interval_t *ri,
67 						 rd_ts_t interval_us,
68 						 rd_ts_t now,
69 						 int immediate) {
70         rd_ts_t diff;
71 
72         if (!now)
73                 now = rd_clock();
74         if (!interval_us)
75                 interval_us = ri->ri_fixed;
76 
77         if (ri->ri_ts_last || !immediate) {
78                 diff = now - (ri->ri_ts_last + interval_us + ri->ri_backoff);
79         } else
80                 diff = 1;
81         if (unlikely(diff > 0)) {
82                 ri->ri_ts_last = now;
83                 ri->ri_backoff = 0;
84         }
85 
86         return diff;
87 }
88 
89 
90 /**
91  * Reset the interval to zero, i.e., the next call to rd_interval()
92  * will be immediate.
93  */
rd_interval_reset(rd_interval_t * ri)94 static RD_INLINE RD_UNUSED void rd_interval_reset (rd_interval_t *ri) {
95         ri->ri_ts_last = 0;
96         ri->ri_backoff = 0;
97 }
98 
99 /**
100  * Reset the interval to 'now'. If now is 0, the time will be gathered
101  * automatically.
102  */
rd_interval_reset_to_now(rd_interval_t * ri,rd_ts_t now)103 static RD_INLINE RD_UNUSED void rd_interval_reset_to_now (rd_interval_t *ri,
104                                                           rd_ts_t now) {
105         if (!now)
106                 now = rd_clock();
107 
108         ri->ri_ts_last = now;
109         ri->ri_backoff = 0;
110 }
111 
112 /**
113  * Back off the next interval by `backoff_us` microseconds.
114  */
rd_interval_backoff(rd_interval_t * ri,int backoff_us)115 static RD_INLINE RD_UNUSED void rd_interval_backoff (rd_interval_t *ri,
116                                                     int backoff_us) {
117         ri->ri_backoff = backoff_us;
118 }
119 
120 /**
121  * Expedite (speed up) the next interval by `expedite_us` microseconds.
122  * If `expedite_us` is 0 the interval will be set to trigger
123  * immedately on the next rd_interval() call.
124  */
rd_interval_expedite(rd_interval_t * ri,int expedite_us)125 static RD_INLINE RD_UNUSED void rd_interval_expedite (rd_interval_t *ri,
126 						     int expedite_us) {
127 	if (!expedite_us)
128 		ri->ri_ts_last = 0;
129 	else
130 		ri->ri_backoff = -expedite_us;
131 }
132 
133 /**
134  * Specifies a fixed interval to use if rd_interval() is called with
135  * `interval_us` set to 0.
136  */
rd_interval_fixed(rd_interval_t * ri,rd_ts_t fixed_us)137 static RD_INLINE RD_UNUSED void rd_interval_fixed (rd_interval_t *ri,
138                                                   rd_ts_t fixed_us) {
139         ri->ri_fixed = fixed_us;
140 }
141 
142 /**
143  * Disables the interval (until rd_interval_init()/reset() is called).
144  * A disabled interval will never return a positive value from
145  * rd_interval().
146  */
rd_interval_disable(rd_interval_t * ri)147 static RD_INLINE RD_UNUSED void rd_interval_disable (rd_interval_t *ri) {
148         /* Set last beat to a large value a long time in the future. */
149         ri->ri_ts_last = 6000000000000000000LL; /* in about 190000 years */
150 }
151 
152 /**
153  * Returns true if the interval is disabled.
154  */
rd_interval_disabled(const rd_interval_t * ri)155 static RD_INLINE RD_UNUSED int rd_interval_disabled (const rd_interval_t *ri) {
156         return ri->ri_ts_last == 6000000000000000000LL;
157 }
158 
159 #endif /* _RDINTERVAL_H_ */
160