Join the VAC read timeout thread
[vpp.git] / src / vppinfra / timer.c
1 /*
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>
44 #include <vppinfra/smp.h>
45 #include <vppinfra/time.h>
46 #include <vppinfra/timer.h>
47 #include <vppinfra/error.h>
48
49 typedef struct
50 {
51   f64 time;
52   timer_func_t *func;
53   any arg;
54 } timer_callback_t;
55
56 /* Vector of currently unexpired timers. */
57 static timer_callback_t *timers;
58
59 /* Convert time from 64bit floating format to struct timeval. */
60 always_inline void
61 f64_to_tv (f64 t, struct timeval *tv)
62 {
63   tv->tv_sec = t;
64   tv->tv_usec = 1e6 * (t - tv->tv_sec);
65   while (tv->tv_usec >= 1000000)
66     {
67       tv->tv_usec -= 1000000;
68       tv->tv_sec += 1;
69     }
70 }
71
72 /* Sort timers so that timer soonest to expire is at end. */
73 static int
74 timer_compare (const void *_a, const void *_b)
75 {
76   const timer_callback_t *a = _a;
77   const timer_callback_t *b = _b;
78   f64 dt = b->time - a->time;
79   return dt < 0 ? -1 : (dt > 0 ? +1 : 0);
80 }
81
82 static inline void
83 sort_timers (timer_callback_t * timers)
84 {
85   qsort (timers, vec_len (timers), sizeof (timers[0]), timer_compare);
86 }
87
88 #define TIMER_SIGNAL SIGALRM
89
90 /* Don't bother set timer if time different is less than this value. */
91 /* We would like to initialize this to 0.75 / (f64) HZ,
92  * but HZ may not be a compile-time constant on some systems,
93  * so instead we do the initialization before first use.
94  */
95 static f64 time_resolution;
96
97 /* Interrupt handler.  Call functions for all expired timers.
98    Set time for next timer interrupt. */
99 static void
100 timer_interrupt (int signum)
101 {
102   f64 now = unix_time_now ();
103   f64 dt;
104   timer_callback_t *t;
105
106   while (1)
107     {
108       if (vec_len (timers) <= 0)
109         return;
110
111       /* Consider last (earliest) timer in reverse sorted
112          vector of pending timers. */
113       t = vec_end (timers) - 1;
114
115       ASSERT (now >= 0 && finite (now));
116
117       /* Time difference between when timer goes off and now. */
118       dt = t->time - now;
119
120       /* If timer is within threshold of going off
121          call user's callback. */
122       if (dt <= time_resolution && finite (dt))
123         {
124           _vec_len (timers) -= 1;
125           (*t->func) (t->arg, -dt);
126         }
127       else
128         {
129           /* Set timer for to go off in future. */
130           struct itimerval itv;
131           memset (&itv, 0, sizeof (itv));
132           f64_to_tv (dt, &itv.it_value);
133           if (setitimer (ITIMER_REAL, &itv, 0) < 0)
134             clib_unix_error ("sititmer");
135           return;
136         }
137     }
138 }
139
140 void
141 timer_block (sigset_t * save)
142 {
143   sigset_t block_timer;
144
145   memset (&block_timer, 0, sizeof (block_timer));
146   sigaddset (&block_timer, TIMER_SIGNAL);
147   sigprocmask (SIG_BLOCK, &block_timer, save);
148 }
149
150 void
151 timer_unblock (sigset_t * save)
152 {
153   sigprocmask (SIG_SETMASK, save, 0);
154 }
155
156 /* Arrange for function to be called some time,
157    roughly equal to dt seconds, in the future. */
158 void
159 timer_call (timer_func_t * func, any arg, f64 dt)
160 {
161   timer_callback_t *t;
162   sigset_t save;
163
164   /* Install signal handler on first call. */
165   static word signal_installed = 0;
166
167   if (!signal_installed)
168     {
169       struct sigaction sa;
170
171       /* Initialize time_resolution before first call to timer_interrupt */
172       time_resolution = 0.75 / (f64) HZ;
173
174       memset (&sa, 0, sizeof (sa));
175       sa.sa_handler = timer_interrupt;
176
177       if (sigaction (TIMER_SIGNAL, &sa, 0) < 0)
178         clib_panic ("sigaction");
179
180       signal_installed = 1;
181     }
182
183   timer_block (&save);
184
185   /* Add new timer. */
186   vec_add2 (timers, t, 1);
187
188   t->time = unix_time_now () + dt;
189   t->func = func;
190   t->arg = arg;
191
192   {
193     word reset_timer = vec_len (timers) == 1;
194
195     if (_vec_len (timers) > 1)
196       {
197         reset_timer += t->time < (t - 1)->time;
198         sort_timers (timers);
199       }
200
201     if (reset_timer)
202       timer_interrupt (TIMER_SIGNAL);
203   }
204
205   timer_unblock (&save);
206 }
207
208 #ifdef TEST
209
210 #include <vppinfra/random.h>
211
212 /* Compute average delay of function calls to foo.
213    If this is a small number over a lot of iterations we know
214    the code is working. */
215
216 static f64 ave_delay = 0;
217 static word ave_delay_count = 0;
218
219 always_inline
220 update (f64 delay)
221 {
222   ave_delay += delay;
223   ave_delay_count += 1;
224 }
225
226 typedef struct
227 {
228   f64 time_requested, time_called;
229 } foo_t;
230
231 static f64 foo_base_time = 0;
232 static foo_t *foos = 0;
233
234 void
235 foo (any arg, f64 delay)
236 {
237   foos[arg].time_called = unix_time_now () - foo_base_time;
238   update (delay);
239 }
240
241 typedef struct
242 {
243   word count;
244   word limit;
245 } bar_t;
246
247 void
248 bar (any arg, f64 delay)
249 {
250   bar_t *b = (bar_t *) arg;
251
252   fformat (stdout, "bar %d delay %g\n", b->count++, delay);
253
254   update (delay);
255   if (b->count < b->limit)
256     timer_call (bar, arg, random_f64 ());
257 }
258
259 int
260 main (int argc, char *argv[])
261 {
262   word i, n = atoi (argv[1]);
263   word run_foo = argc > 2;
264 bar_t b = { limit:10 };
265
266   if (run_foo)
267     {
268       f64 time_limit;
269
270       time_limit = atof (argv[2]);
271
272       vec_resize (foos, n);
273       for (i = 0; i < n; i++)
274         {
275           foos[i].time_requested = time_limit * random_f64 ();
276           foos[i].time_called = 1e100;
277         }
278
279       foo_base_time = unix_time_now ();
280       for (i = 0; i < n; i++)
281         timer_call (foo, i, foos[i].time_requested);
282     }
283   else
284     timer_call (bar, (any) & b, random_f64 ());
285
286   while (vec_len (timers) > 0)
287     os_sched_yield ();
288
289   if (vec_len (foos) > 0)
290     {
291       f64 min = 1e100, max = -min;
292       f64 ave = 0, rms = 0;
293
294       for (i = 0; i < n; i++)
295         {
296           f64 dt = foos[i].time_requested - foos[i].time_called;
297           if (dt < min)
298             min = dt;
299           if (dt > max)
300             max = dt;
301           ave += dt;
302           rms += dt * dt;
303         }
304       ave /= n;
305       rms = sqrt (rms / n - ave * ave);
306       fformat (stdout, "error min %g max %g ave %g +- %g\n", min, max, ave,
307                rms);
308     }
309
310   fformat (stdout, "%d function calls, ave. timer delay %g secs\n",
311            ave_delay_count, ave_delay / ave_delay_count);
312
313   return 0;
314 }
315 #endif
316
317 /*
318  * fd.io coding-style-patch-verification: ON
319  *
320  * Local Variables:
321  * eval: (c-set-style "gnu")
322  * End:
323  */