blob: e1e32eb285f7003698b84add8fff8cdac080b044 [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15/*
16 Copyright (c) 2001, 2002, 2003 Eliot Dresselhaus
17
18 Permission is hereby granted, free of charge, to any person obtaining
19 a copy of this software and associated documentation files (the
20 "Software"), to deal in the Software without restriction, including
21 without limitation the rights to use, copy, modify, merge, publish,
22 distribute, sublicense, and/or sell copies of the Software, and to
23 permit persons to whom the Software is furnished to do so, subject to
24 the following conditions:
25
26 The above copyright notice and this permission notice shall be
27 included in all copies or substantial portions of the Software.
28
29 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36*/
37
38#include <math.h>
39#include <stdlib.h>
40#include <string.h>
41#include <sys/param.h>
42
43#include <vppinfra/vec.h>
Kevin Wang6390df32017-12-08 13:43:27 +080044#include <vppinfra/smp.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070045#include <vppinfra/time.h>
46#include <vppinfra/timer.h>
47#include <vppinfra/error.h>
48
Damjan Marion4dffd1c2018-09-03 12:30:36 +020049#ifndef HZ
50#define HZ 1000
51#endif
52
Dave Barachc3799992016-08-15 11:12:27 -040053typedef struct
54{
Ed Warnickecb9cada2015-12-08 15:45:58 -070055 f64 time;
Dave Barachc3799992016-08-15 11:12:27 -040056 timer_func_t *func;
Ed Warnickecb9cada2015-12-08 15:45:58 -070057 any arg;
58} timer_callback_t;
59
60/* Vector of currently unexpired timers. */
Dave Barachc3799992016-08-15 11:12:27 -040061static timer_callback_t *timers;
Ed Warnickecb9cada2015-12-08 15:45:58 -070062
63/* Convert time from 64bit floating format to struct timeval. */
Dave Barachc3799992016-08-15 11:12:27 -040064always_inline void
65f64_to_tv (f64 t, struct timeval *tv)
Ed Warnickecb9cada2015-12-08 15:45:58 -070066{
67 tv->tv_sec = t;
Dave Barachc3799992016-08-15 11:12:27 -040068 tv->tv_usec = 1e6 * (t - tv->tv_sec);
Ed Warnickecb9cada2015-12-08 15:45:58 -070069 while (tv->tv_usec >= 1000000)
70 {
71 tv->tv_usec -= 1000000;
72 tv->tv_sec += 1;
73 }
74}
75
76/* Sort timers so that timer soonest to expire is at end. */
Dave Barachc3799992016-08-15 11:12:27 -040077static int
78timer_compare (const void *_a, const void *_b)
Ed Warnickecb9cada2015-12-08 15:45:58 -070079{
Dave Barachc3799992016-08-15 11:12:27 -040080 const timer_callback_t *a = _a;
81 const timer_callback_t *b = _b;
Ed Warnickecb9cada2015-12-08 15:45:58 -070082 f64 dt = b->time - a->time;
83 return dt < 0 ? -1 : (dt > 0 ? +1 : 0);
84}
85
Dave Barachc3799992016-08-15 11:12:27 -040086static inline void
87sort_timers (timer_callback_t * timers)
88{
89 qsort (timers, vec_len (timers), sizeof (timers[0]), timer_compare);
90}
Ed Warnickecb9cada2015-12-08 15:45:58 -070091
92#define TIMER_SIGNAL SIGALRM
93
94/* Don't bother set timer if time different is less than this value. */
95/* We would like to initialize this to 0.75 / (f64) HZ,
96 * but HZ may not be a compile-time constant on some systems,
97 * so instead we do the initialization before first use.
98 */
99static f64 time_resolution;
100
101/* Interrupt handler. Call functions for all expired timers.
102 Set time for next timer interrupt. */
Dave Barachc3799992016-08-15 11:12:27 -0400103static void
104timer_interrupt (int signum)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700105{
106 f64 now = unix_time_now ();
107 f64 dt;
Dave Barachc3799992016-08-15 11:12:27 -0400108 timer_callback_t *t;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700109
110 while (1)
111 {
112 if (vec_len (timers) <= 0)
113 return;
114
115 /* Consider last (earliest) timer in reverse sorted
Dave Barachc3799992016-08-15 11:12:27 -0400116 vector of pending timers. */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700117 t = vec_end (timers) - 1;
118
Damjan Marion4dffd1c2018-09-03 12:30:36 +0200119 ASSERT (now >= 0 && isfinite (now));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700120
121 /* Time difference between when timer goes off and now. */
122 dt = t->time - now;
123
124 /* If timer is within threshold of going off
Dave Barachc3799992016-08-15 11:12:27 -0400125 call user's callback. */
Damjan Marion4dffd1c2018-09-03 12:30:36 +0200126 if (dt <= time_resolution && isfinite (dt))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700127 {
128 _vec_len (timers) -= 1;
Dave Barachc3799992016-08-15 11:12:27 -0400129 (*t->func) (t->arg, -dt);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700130 }
131 else
132 {
133 /* Set timer for to go off in future. */
134 struct itimerval itv;
Dave Barachb7b92992018-10-17 10:38:51 -0400135 clib_memset (&itv, 0, sizeof (itv));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700136 f64_to_tv (dt, &itv.it_value);
137 if (setitimer (ITIMER_REAL, &itv, 0) < 0)
138 clib_unix_error ("sititmer");
139 return;
140 }
141 }
142}
143
Dave Barachc3799992016-08-15 11:12:27 -0400144void
145timer_block (sigset_t * save)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700146{
147 sigset_t block_timer;
148
Dave Barachb7b92992018-10-17 10:38:51 -0400149 clib_memset (&block_timer, 0, sizeof (block_timer));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700150 sigaddset (&block_timer, TIMER_SIGNAL);
151 sigprocmask (SIG_BLOCK, &block_timer, save);
152}
153
Dave Barachc3799992016-08-15 11:12:27 -0400154void
155timer_unblock (sigset_t * save)
156{
157 sigprocmask (SIG_SETMASK, save, 0);
158}
Ed Warnickecb9cada2015-12-08 15:45:58 -0700159
160/* Arrange for function to be called some time,
161 roughly equal to dt seconds, in the future. */
Dave Barachc3799992016-08-15 11:12:27 -0400162void
163timer_call (timer_func_t * func, any arg, f64 dt)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700164{
Dave Barachc3799992016-08-15 11:12:27 -0400165 timer_callback_t *t;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700166 sigset_t save;
167
168 /* Install signal handler on first call. */
169 static word signal_installed = 0;
170
Dave Barachc3799992016-08-15 11:12:27 -0400171 if (!signal_installed)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700172 {
173 struct sigaction sa;
174
175 /* Initialize time_resolution before first call to timer_interrupt */
176 time_resolution = 0.75 / (f64) HZ;
177
Dave Barachb7b92992018-10-17 10:38:51 -0400178 clib_memset (&sa, 0, sizeof (sa));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700179 sa.sa_handler = timer_interrupt;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700180
181 if (sigaction (TIMER_SIGNAL, &sa, 0) < 0)
182 clib_panic ("sigaction");
183
184 signal_installed = 1;
185 }
186
187 timer_block (&save);
188
189 /* Add new timer. */
190 vec_add2 (timers, t, 1);
191
192 t->time = unix_time_now () + dt;
193 t->func = func;
194 t->arg = arg;
195
196 {
197 word reset_timer = vec_len (timers) == 1;
198
199 if (_vec_len (timers) > 1)
200 {
Dave Barachc3799992016-08-15 11:12:27 -0400201 reset_timer += t->time < (t - 1)->time;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700202 sort_timers (timers);
203 }
204
205 if (reset_timer)
206 timer_interrupt (TIMER_SIGNAL);
207 }
208
209 timer_unblock (&save);
210}
211
212#ifdef TEST
213
214#include <vppinfra/random.h>
215
216/* Compute average delay of function calls to foo.
217 If this is a small number over a lot of iterations we know
218 the code is working. */
219
220static f64 ave_delay = 0;
221static word ave_delay_count = 0;
222
Dave Barachc3799992016-08-15 11:12:27 -0400223always_inline
224update (f64 delay)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700225{
226 ave_delay += delay;
227 ave_delay_count += 1;
228}
229
Dave Barachc3799992016-08-15 11:12:27 -0400230typedef struct
231{
Ed Warnickecb9cada2015-12-08 15:45:58 -0700232 f64 time_requested, time_called;
233} foo_t;
234
235static f64 foo_base_time = 0;
Dave Barachc3799992016-08-15 11:12:27 -0400236static foo_t *foos = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700237
Dave Barachc3799992016-08-15 11:12:27 -0400238void
239foo (any arg, f64 delay)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700240{
241 foos[arg].time_called = unix_time_now () - foo_base_time;
242 update (delay);
243}
244
Dave Barachc3799992016-08-15 11:12:27 -0400245typedef struct
246{
Ed Warnickecb9cada2015-12-08 15:45:58 -0700247 word count;
248 word limit;
249} bar_t;
250
Dave Barachc3799992016-08-15 11:12:27 -0400251void
252bar (any arg, f64 delay)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700253{
Dave Barachc3799992016-08-15 11:12:27 -0400254 bar_t *b = (bar_t *) arg;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700255
256 fformat (stdout, "bar %d delay %g\n", b->count++, delay);
257
258 update (delay);
259 if (b->count < b->limit)
260 timer_call (bar, arg, random_f64 ());
261}
262
Dave Barachc3799992016-08-15 11:12:27 -0400263int
264main (int argc, char *argv[])
Ed Warnickecb9cada2015-12-08 15:45:58 -0700265{
266 word i, n = atoi (argv[1]);
267 word run_foo = argc > 2;
Dave Barachc3799992016-08-15 11:12:27 -0400268bar_t b = { limit:10 };
Ed Warnickecb9cada2015-12-08 15:45:58 -0700269
270 if (run_foo)
271 {
272 f64 time_limit;
273
274 time_limit = atof (argv[2]);
275
276 vec_resize (foos, n);
277 for (i = 0; i < n; i++)
278 {
279 foos[i].time_requested = time_limit * random_f64 ();
280 foos[i].time_called = 1e100;
281 }
282
283 foo_base_time = unix_time_now ();
284 for (i = 0; i < n; i++)
285 timer_call (foo, i, foos[i].time_requested);
286 }
287 else
Dave Barachc3799992016-08-15 11:12:27 -0400288 timer_call (bar, (any) & b, random_f64 ());
Ed Warnickecb9cada2015-12-08 15:45:58 -0700289
290 while (vec_len (timers) > 0)
Kevin Wang6390df32017-12-08 13:43:27 +0800291 os_sched_yield ();
Ed Warnickecb9cada2015-12-08 15:45:58 -0700292
293 if (vec_len (foos) > 0)
294 {
295 f64 min = 1e100, max = -min;
296 f64 ave = 0, rms = 0;
297
298 for (i = 0; i < n; i++)
299 {
300 f64 dt = foos[i].time_requested - foos[i].time_called;
301 if (dt < min)
302 min = dt;
303 if (dt > max)
304 max = dt;
305 ave += dt;
Dave Barachc3799992016-08-15 11:12:27 -0400306 rms += dt * dt;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700307 }
308 ave /= n;
Dave Barachc3799992016-08-15 11:12:27 -0400309 rms = sqrt (rms / n - ave * ave);
310 fformat (stdout, "error min %g max %g ave %g +- %g\n", min, max, ave,
311 rms);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700312 }
313
314 fformat (stdout, "%d function calls, ave. timer delay %g secs\n",
315 ave_delay_count, ave_delay / ave_delay_count);
316
317 return 0;
318}
319#endif
Dave Barachc3799992016-08-15 11:12:27 -0400320
321/*
322 * fd.io coding-style-patch-verification: ON
323 *
324 * Local Variables:
325 * eval: (c-set-style "gnu")
326 * End:
327 */