FD.io VPP  v16.06
Vector Packet Processing
timer.c
Go to the documentation of this file.
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/time.h>
45 #include <vppinfra/timer.h>
46 #include <vppinfra/error.h>
47 
48 typedef struct {
53 
54 /* Vector of currently unexpired timers. */
56 
57 /* Convert time from 64bit floating format to struct timeval. */
58 always_inline void f64_to_tv (f64 t, struct timeval * tv)
59 {
60  tv->tv_sec = t;
61  tv->tv_usec = 1e6*(t - tv->tv_sec);
62  while (tv->tv_usec >= 1000000)
63  {
64  tv->tv_usec -= 1000000;
65  tv->tv_sec += 1;
66  }
67 }
68 
69 /* Sort timers so that timer soonest to expire is at end. */
70 static int timer_compare (const void * _a, const void * _b)
71 {
72  const timer_callback_t * a = _a;
73  const timer_callback_t * b = _b;
74  f64 dt = b->time - a->time;
75  return dt < 0 ? -1 : (dt > 0 ? +1 : 0);
76 }
77 
78 static inline void sort_timers (timer_callback_t * timers)
79 { qsort (timers, vec_len (timers), sizeof (timers[0]), timer_compare); }
80 
81 #define TIMER_SIGNAL SIGALRM
82 
83 /* Don't bother set timer if time different is less than this value. */
84 /* We would like to initialize this to 0.75 / (f64) HZ,
85  * but HZ may not be a compile-time constant on some systems,
86  * so instead we do the initialization before first use.
87  */
89 
90 /* Interrupt handler. Call functions for all expired timers.
91  Set time for next timer interrupt. */
92 static void timer_interrupt (int signum)
93 {
94  f64 now = unix_time_now ();
95  f64 dt;
96  timer_callback_t * t;
97 
98  while (1)
99  {
100  if (vec_len (timers) <= 0)
101  return;
102 
103  /* Consider last (earliest) timer in reverse sorted
104  vector of pending timers. */
105  t = vec_end (timers) - 1;
106 
107  ASSERT (now >= 0 && finite (now));
108 
109  /* Time difference between when timer goes off and now. */
110  dt = t->time - now;
111 
112  /* If timer is within threshold of going off
113  call user's callback. */
114  if (dt <= time_resolution && finite (dt))
115  {
116  _vec_len (timers) -= 1;
117  (* t->func) (t->arg, -dt);
118  }
119  else
120  {
121  /* Set timer for to go off in future. */
122  struct itimerval itv;
123  memset (&itv, 0, sizeof (itv));
124  f64_to_tv (dt, &itv.it_value);
125  if (setitimer (ITIMER_REAL, &itv, 0) < 0)
126  clib_unix_error ("sititmer");
127  return;
128  }
129  }
130 }
131 
132 void timer_block (sigset_t * save)
133 {
134  sigset_t block_timer;
135 
136  memset (&block_timer, 0, sizeof (block_timer));
137  sigaddset (&block_timer, TIMER_SIGNAL);
138  sigprocmask (SIG_BLOCK, &block_timer, save);
139 }
140 
141 void timer_unblock (sigset_t * save)
142 { sigprocmask (SIG_SETMASK, save, 0); }
143 
144 /* Arrange for function to be called some time,
145  roughly equal to dt seconds, in the future. */
146 void timer_call (timer_func_t * func, any arg, f64 dt)
147 {
148  timer_callback_t * t;
149  sigset_t save;
150 
151  /* Install signal handler on first call. */
152  static word signal_installed = 0;
153 
154  if (! signal_installed)
155  {
156  struct sigaction sa;
157 
158  /* Initialize time_resolution before first call to timer_interrupt */
159  time_resolution = 0.75 / (f64) HZ;
160 
161  sa.sa_handler = timer_interrupt;
162  sa.sa_flags = 0;
163 
164  if (sigaction (TIMER_SIGNAL, &sa, 0) < 0)
165  clib_panic ("sigaction");
166 
167  signal_installed = 1;
168  }
169 
170  timer_block (&save);
171 
172  /* Add new timer. */
173  vec_add2 (timers, t, 1);
174 
175  t->time = unix_time_now () + dt;
176  t->func = func;
177  t->arg = arg;
178 
179  {
180  word reset_timer = vec_len (timers) == 1;
181 
182  if (_vec_len (timers) > 1)
183  {
184  reset_timer += t->time < (t-1)->time;
185  sort_timers (timers);
186  }
187 
188  if (reset_timer)
190  }
191 
192  timer_unblock (&save);
193 }
194 
195 #ifdef TEST
196 
197 #include <vppinfra/random.h>
198 
199 /* Compute average delay of function calls to foo.
200  If this is a small number over a lot of iterations we know
201  the code is working. */
202 
203 static f64 ave_delay = 0;
204 static word ave_delay_count = 0;
205 
206 always_inline update (f64 delay)
207 {
208  ave_delay += delay;
209  ave_delay_count += 1;
210 }
211 
212 typedef struct {
213  f64 time_requested, time_called;
214 } foo_t;
215 
216 static f64 foo_base_time = 0;
217 static foo_t * foos = 0;
218 
219 void foo (any arg, f64 delay)
220 {
221  foos[arg].time_called = unix_time_now () - foo_base_time;
222  update (delay);
223 }
224 
225 typedef struct {
226  word count;
227  word limit;
228 } bar_t;
229 
230 void bar (any arg, f64 delay)
231 {
232  bar_t * b = (bar_t *) arg;
233 
234  fformat (stdout, "bar %d delay %g\n", b->count++, delay);
235 
236  update (delay);
237  if (b->count < b->limit)
238  timer_call (bar, arg, random_f64 ());
239 }
240 
241 int main (int argc, char * argv[])
242 {
243  word i, n = atoi (argv[1]);
244  word run_foo = argc > 2;
245  bar_t b = {limit: 10};
246 
247  if (run_foo)
248  {
249  f64 time_limit;
250 
251  time_limit = atof (argv[2]);
252 
253  vec_resize (foos, n);
254  for (i = 0; i < n; i++)
255  {
256  foos[i].time_requested = time_limit * random_f64 ();
257  foos[i].time_called = 1e100;
258  }
259 
260  foo_base_time = unix_time_now ();
261  for (i = 0; i < n; i++)
262  timer_call (foo, i, foos[i].time_requested);
263  }
264  else
265  timer_call (bar, (any) &b, random_f64 ());
266 
267  while (vec_len (timers) > 0)
268  sched_yield ();
269 
270  if (vec_len (foos) > 0)
271  {
272  f64 min = 1e100, max = -min;
273  f64 ave = 0, rms = 0;
274 
275  for (i = 0; i < n; i++)
276  {
277  f64 dt = foos[i].time_requested - foos[i].time_called;
278  if (dt < min)
279  min = dt;
280  if (dt > max)
281  max = dt;
282  ave += dt;
283  rms += dt*dt;
284  }
285  ave /= n;
286  rms = sqrt (rms / n - ave*ave);
287  fformat (stdout, "error min %g max %g ave %g +- %g\n", min, max, ave, rms);
288  }
289 
290  fformat (stdout, "%d function calls, ave. timer delay %g secs\n",
291  ave_delay_count, ave_delay / ave_delay_count);
292 
293  return 0;
294 }
295 #endif
always_inline f64 unix_time_now(void)
Definition: time.h:208
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:267
a
Definition: bitmap.h:393
void timer_unblock(sigset_t *save)
Definition: timer.c:141
#define vec_add2(V, P, N)
Add N elements to end of vector V, return pointer to new elements in P.
Definition: vec.h:519
static f64 time_resolution
Definition: timer.c:88
always_inline f64 sqrt(f64 x)
Definition: math.h:43
#define always_inline
Definition: clib.h:84
static timer_callback_t * timers
Definition: timer.c:55
#define vec_resize(V, N)
Resize a vector (no header, unspecified alignment) Add N elements to end of given vector V...
Definition: vec.h:199
#define vec_end(v)
End (last data address) of vector.
always_inline f64 random_f64(u32 *seed)
Generate f64 random number in the interval [0,1].
Definition: random.h:133
always_inline void f64_to_tv(f64 t, struct timeval *tv)
Definition: timer.c:58
void timer_call(timer_func_t *func, any arg, f64 dt)
Definition: timer.c:146
static int timer_compare(const void *_a, const void *_b)
Definition: timer.c:70
#define TIMER_SIGNAL
Definition: timer.c:81
word any
Definition: types.h:137
static void timer_interrupt(int signum)
Definition: timer.c:92
#define ASSERT(truth)
#define clib_unix_error(format, args...)
Definition: error.h:65
void( timer_func_t)(any arg, f64 delay)
Definition: timer.h:29
void timer_block(sigset_t *save)
Definition: timer.c:132
i64 word
Definition: types.h:111
void qsort(void *base, uword n, uword size, int(*compar)(const void *, const void *))
Definition: qsort.c:55
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
double f64
Definition: types.h:140
word fformat(FILE *f, char *fmt,...)
Definition: format.c:437
Linear Congruential Random Number Generator.
#define clib_panic(format, args...)
Definition: error.h:72
int main(int argc, char *argv[])
Definition: elftool.c:369
timer_func_t * func
Definition: timer.c:50
CLIB vectors are ubiquitous dynamically resized arrays with by user defined "headers".
static void sort_timers(timer_callback_t *timers)
Definition: timer.c:78