2
* Copyright (c) 2008, 2009, 2012, 2013 by Farsight Security, Inc.
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
8
* http://www.apache.org/licenses/LICENSE-2.0
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
26
struct timespec next_tick, period, start;
28
unsigned adj_rate, rate, freq;
32
ts_nanos(struct timespec *ts)
34
return (ts->tv_sec * 1000000000 + ts->tv_nsec);
37
static inline struct timespec
38
calc_next_tick(const struct timespec *t, const struct timespec *m)
44
res.tv_sec -= (res.tv_sec % m->tv_sec);
45
res.tv_sec += m->tv_sec;
48
res.tv_nsec -= (res.tv_nsec % m->tv_nsec);
49
res.tv_nsec += m->tv_nsec;
54
while (res.tv_nsec >= 1000000000) {
56
res.tv_nsec -= 1000000000;
63
adjust_rate(struct rate *r, struct timespec *now)
65
struct timespec elapsed;
69
/* amount of time elapsed since first sleep */
71
my_timespec_sub(&r->start, &elapsed);
73
/* the average event rate that has been maintained over the
74
* lifespan of this rate-limiter.
76
actual_rate = r->count / (ts_nanos(&elapsed) / (1000000000 + 0.0));
78
/* simple ratio of nominal event rate and average event rate */
79
ratio = r->rate / (actual_rate + 0.0);
81
/* clamp this ratio to a small interval */
87
/* calculate a new, adjusted rate based on this ratio */
90
/* calculate a new tick period based on the adjusted rate */
91
const double period = 1.0 / (r->adj_rate + 0.0);
92
my_timespec_from_double(period, &r->period);
96
rate_init(unsigned rate, unsigned freq)
100
r = calloc(1, sizeof(*r));
107
/* calculate the tick period */
108
const double period = 1.0 / (r->rate + 0.0);
109
my_timespec_from_double(period, &r->period);
115
rate_destroy(struct rate **r)
124
rate_sleep(struct rate *r)
126
struct timespec now, til;
128
/* what clock to use depends on whether clock_nanosleep() is available */
129
#if HAVE_CLOCK_NANOSLEEP
130
static const clockid_t rate_clock = CLOCK_MONOTONIC;
132
static const clockid_t rate_clock = CLOCK_REALTIME;
138
/* update the event counter */
141
/* fetch the current time */
142
clock_gettime(rate_clock, &now);
144
/* special case: if this is the first call to rate_sleep(),
145
* calculate when the next tick will be. this is a little bit more
146
* accurate than calculating it in rate_init().
150
r->next_tick = calc_next_tick(&now, &r->period);
153
/* adjust the rate and period every 'freq' events.
154
* skip the first window of 'freq' events.
155
* disabled if 'freq' is 0.
157
if (r->freq != 0 && (r->count % r->freq) == 0 && r->count > r->freq)
158
adjust_rate(r, &now);
160
/* 'til', amount of time remaining until the next tick */
162
my_timespec_sub(&now, &til);
164
/* if 'til' is in the past, don't bother sleeping */
165
if (ts_nanos(&til) > 0) {
167
#if HAVE_CLOCK_NANOSLEEP
168
clock_nanosleep(rate_clock, TIMER_ABSTIME, &r->next_tick, NULL);
172
my_timespec_sub(&now, &rel);
176
/* re-fetch the current time */
177
clock_gettime(rate_clock, &now);
180
/* calculate the next tick */
181
r->next_tick = calc_next_tick(&now, &r->period);