1 /*****
2 *
3 * Copyright (C) 1999-2015 CS-SI. All Rights Reserved.
4 * Author: Yoann Vandoorselaere <yoann.v@prelude-ids.com>
5 *
6 * This file is part of the Prelude library.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 *****/
23 
24 #include "config.h"
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <time.h>
29 #include <assert.h>
30 
31 #include "glthread/thread.h"
32 #include "glthread/lock.h"
33 #include "glthread/cond.h"
34 
35 #include "prelude-log.h"
36 #include "prelude-list.h"
37 #include "prelude-linked-object.h"
38 #include "prelude-async.h"
39 #include "prelude-error.h"
40 
41 #include "prelude-timer.h"
42 
43 #ifndef MIN
44 # define MIN(x, y) (((x) < (y)) ? (x) : (y))
45 #endif
46 
47 static PRELUDE_LIST(timer_list);
48 static int next_wakeup = 0;
49 static time_t last_wakeup = 0;
50 static gl_lock_t mutex = gl_lock_initializer;
51 
52 
53 
timer_lock_list(void)54 inline static void timer_lock_list(void)
55 {
56         gl_lock_lock(mutex);
57 }
58 
59 
60 
timer_unlock_list(void)61 inline static void timer_unlock_list(void)
62 {
63         gl_lock_unlock(mutex);
64 }
65 
66 
67 
68 
69 /*
70  * Return the time elapsed by a timer 'timer' from now,
71  * to the time it was created / reset.
72  */
time_elapsed(prelude_timer_t * timer,time_t now)73 static time_t time_elapsed(prelude_timer_t *timer, time_t now)
74 {
75         return now - timer->start_time;
76 }
77 
78 
79 
80 
time_remaining(prelude_timer_t * timer,time_t now)81 static time_t time_remaining(prelude_timer_t *timer, time_t now)
82 {
83         return timer->expire - time_elapsed(timer, now);
84 }
85 
86 
87 
88 
89 /*
90  * If timer 'timer' need to be waked up (it elapsed >= time
91  * for it to expire), call it's callback function, with it's
92  * registered argument.
93  *
94  * All expired timer should be destroyed.
95  */
wake_up_if_needed(prelude_timer_t * timer,time_t now)96 static int wake_up_if_needed(prelude_timer_t *timer, time_t now)
97 {
98         assert(timer->start_time != -1);
99 
100         if ( now == -1 || time_elapsed(timer, now) >= prelude_timer_get_expire(timer) ) {
101                 timer->start_time = -1;
102 
103                 prelude_timer_get_callback(timer)(prelude_timer_get_data(timer));
104 
105                 return 0;
106         }
107 
108         return (int) time_remaining(timer, now);
109 }
110 
111 
112 
113 
get_next_timer(void)114 static prelude_timer_t *get_next_timer(void)
115 {
116         prelude_list_t *tmp;
117         prelude_timer_t *timer = NULL;
118 
119         gl_lock_lock(mutex);
120 
121         prelude_list_for_each(&timer_list, tmp) {
122                 timer = prelude_list_entry(tmp, prelude_timer_t, list);
123                 break;
124         }
125 
126         gl_lock_unlock(mutex);
127 
128         return timer;
129 }
130 
131 
132 
133 /*
134  * Walk the list of timer,
135  * call the wake_up_if_need_function on each timer.
136  */
walk_and_wake_up_timer(time_t now)137 static void walk_and_wake_up_timer(time_t now)
138 {
139         int ret, woke = 0;
140         prelude_timer_t *timer;
141 
142         while ( (timer = get_next_timer()) ) {
143 
144                 ret = wake_up_if_needed(timer, now);
145                 if ( ret != 0 ) {
146                         next_wakeup = ret;
147                         break;
148                 }
149 
150                 woke++;
151         }
152 
153         prelude_log_debug(5, "woke up %d timer, next wakeup in %d seconds\n", woke, next_wakeup);
154 }
155 
156 
157 
158 /*
159  * search the timer list forward for the timer entry
160  * that should be before our inserted timer.
161  */
search_previous_forward(prelude_timer_t * timer,time_t expire)162 static prelude_list_t *search_previous_forward(prelude_timer_t *timer, time_t expire)
163 {
164         int hop = 0;
165         prelude_timer_t *cur;
166         prelude_list_t *tmp, *prev = NULL;
167 
168         prelude_list_for_each(&timer_list, tmp) {
169                 cur = prelude_list_entry(tmp, prelude_timer_t, list);
170 
171                 hop++;
172 
173                 if ( (cur->start_time + cur->expire) < expire ) {
174                         /*
175                          * we found a previous timer (expiring before us),
176                          * but we're walking the list forward, and there could be more...
177                          * save and continue.
178                          */
179                         prev = tmp;
180                         continue;
181                 }
182 
183                 else if ( (cur->start_time + cur->expire) == expire ) {
184                         /*
185                          * we found a timer that's expiring at the same time
186                          * as us. Return it as the previous insertion point.
187                          */
188                         prelude_log_debug(5, "[expire=%d] found forward in %d hop at %p\n", timer->expire, hop, cur);
189                         return tmp;
190                 }
191 
192                 else if ( (cur->start_time + cur->expire) > expire ) {
193                         /*
194                          * we found a timer expiring after us. We can return
195                          * the previously saved entry.
196                          */
197                         prelude_log_debug(5, "[expire=%d] found forward in %d hop at %p\n", timer->expire, hop, cur);
198                         assert(prev);
199                         return prev;
200                 }
201         }
202 
203         /*
204          * this should never happen, as search_previous_timer verify
205          * if timer should be inserted last.
206          */
207         abort();
208 }
209 
210 
211 
212 
213 /*
214  * search the timer list backward for the timer entry
215  * that should be before our inserted timer.
216  */
search_previous_backward(prelude_timer_t * timer,time_t expire)217 static prelude_list_t *search_previous_backward(prelude_timer_t *timer, time_t expire)
218 {
219         int hop = 0;
220         prelude_timer_t *cur;
221         prelude_list_t *tmp;
222 
223         for ( tmp = timer_list.prev; tmp != &timer_list; tmp = tmp->prev ) {
224 
225                 cur = prelude_list_entry(tmp, prelude_timer_t, list);
226 
227                 if ( (cur->start_time + cur->expire) <= expire ) {
228                         prelude_log_debug(5, "[expire=%d] found backward in %d hop at %p\n", timer->expire, hop + 1, cur);
229                         assert(tmp);
230                         return tmp;
231                 }
232 
233                 hop++;
234         }
235 
236         /*
237          * this should never happen, as search_previous_timer verify
238          * if timer should be inserted first.
239          */
240         abort();
241 }
242 
243 
244 
245 
get_first_timer(void)246 inline static prelude_timer_t *get_first_timer(void)
247 {
248         return prelude_list_entry(timer_list.next, prelude_timer_t, list);
249 }
250 
251 
252 
253 
get_last_timer(void)254 inline static prelude_timer_t *get_last_timer(void)
255 {
256         return prelude_list_entry(timer_list.prev, prelude_timer_t, list);
257 }
258 
259 
260 
261 /*
262  * On entering in this function, we know that :
263  * - expire is > than first_expire.
264  * - expire is < than last_expire.
265  */
search_previous_timer(prelude_timer_t * timer)266 static prelude_list_t *search_previous_timer(prelude_timer_t *timer)
267 {
268         prelude_timer_t *last, *first;
269         time_t last_remaining, first_remaining;
270 
271         last = get_last_timer();
272         first = get_first_timer();
273 
274         /*
275          * timer we want to insert expire after (or at the same time) the known
276          * to be expiring last timer. This mean we should insert the new timer
277          * at the end of the list.
278          */
279         if ( timer->expire >= time_remaining(last, timer->start_time) ) {
280                 assert(timer_list.prev);
281                 prelude_log_debug(5, "[expire=%d] found without search (insert last)\n", timer->expire);
282                 return timer_list.prev;
283         }
284 
285         /*
286          * timer we want to insert expire before (or at the same time), the known
287          * to be expiring first timer. This mean we should insert the new timer at
288          * the beginning of the list.
289          */
290         if ( timer->expire <= time_remaining(first, timer->start_time) ) {
291                 prelude_log_debug(5, "[expire=%d] found without search (insert first)\n", timer->expire);
292                 return &timer_list;
293         }
294 
295         /*
296          * we now know we expire after the first expiring timer,
297          * but before the last expiring one.
298          *
299          * compute expiration time for current, last, and first timer.
300          */
301         last_remaining = time_remaining(last, timer->start_time);
302         first_remaining = time_remaining(first, timer->start_time);
303 
304         /*
305          * use the better list iterating function to find the previous timer.
306          */
307         if ( (last_remaining - timer->expire) > (timer->expire - first_remaining) )
308                 /*
309                  * previous is probably near the beginning of the list.
310                  */
311                 return search_previous_forward(timer, timer->expire + timer->start_time);
312         else
313                 /*
314                  * previous is probably near the end of the list.
315                  */
316                 return search_previous_backward(timer, timer->expire + timer->start_time);
317 }
318 
319 
320 
timer_destroy_unlocked(prelude_timer_t * timer)321 static void timer_destroy_unlocked(prelude_timer_t *timer)
322 {
323         if ( ! prelude_list_is_empty(&timer->list) )
324                 prelude_list_del_init(&timer->list);
325 }
326 
327 
328 
329 
timer_init_unlocked(prelude_timer_t * timer)330 static void timer_init_unlocked(prelude_timer_t *timer)
331 {
332         prelude_list_t *prev;
333 
334         timer->start_time = time(NULL);
335 
336         if ( ! prelude_list_is_empty(&timer_list) )
337                 prev = search_previous_timer(timer);
338         else
339                 prev = &timer_list;
340 
341         prelude_list_add(prev, &timer->list);
342         next_wakeup = MIN(next_wakeup, prelude_timer_get_expire(timer));
343 }
344 
345 
346 
347 
348 /**
349  * prelude_timer_init:
350  * @timer: timer to initialize.
351  *
352  * Initialize a timer (add it to the timer list).
353  */
prelude_timer_init(prelude_timer_t * timer)354 void prelude_timer_init(prelude_timer_t *timer)
355 {
356         timer_lock_list();
357         timer_init_unlocked(timer);
358         timer_unlock_list();
359 }
360 
361 
362 
363 /**
364  * prelude_timer_init_list:
365  * @timer: Pointer to a #prelude_timer_t object.
366  *
367  * Initialize @timer list member. This is useful if
368  * you're going to call prelude_timer_destroy() on timer
369  * for which prelude_timer_init() was never called.
370  *
371  */
prelude_timer_init_list(prelude_timer_t * timer)372 void prelude_timer_init_list(prelude_timer_t *timer)
373 {
374         prelude_list_init(&timer->list);
375 }
376 
377 
378 
379 /**
380  * prelude_timer_reset:
381  * @timer: the timer to reset.
382  *
383  * Reset timer 'timer', as if it was just started.
384  */
prelude_timer_reset(prelude_timer_t * timer)385 void prelude_timer_reset(prelude_timer_t *timer)
386 {
387         timer_lock_list();
388 
389         timer_destroy_unlocked(timer);
390         timer_init_unlocked(timer);
391 
392         timer_unlock_list();
393 }
394 
395 
396 
397 
398 /**
399  * prelude_timer_destroy:
400  * @timer: the timer to destroy.
401  *
402  * Destroy the timer 'timer',
403  * this remove it from the active timer list.
404  */
prelude_timer_destroy(prelude_timer_t * timer)405 void prelude_timer_destroy(prelude_timer_t *timer)
406 {
407         timer_lock_list();
408         timer_destroy_unlocked(timer);
409         timer_unlock_list();
410 }
411 
412 
413 
414 /**
415  * prelude_timer_wake_up:
416  *
417  * Wake up timer that need it.
418  * This function should be called every second to work properly.
419  *
420  * Returns: Number of second in which prelude_timer_wake_up() should be called again.
421  */
prelude_timer_wake_up(void)422 int prelude_timer_wake_up(void)
423 {
424         time_t now = time(NULL);
425 
426         if ( now - last_wakeup >= next_wakeup ) {
427                 walk_and_wake_up_timer(now);
428                 last_wakeup = now;
429         }
430 
431         return next_wakeup;
432 }
433 
434 
435 
436 
437 /**
438  * prelude_timer_flush:
439  *
440  * Expire every timer.
441  */
prelude_timer_flush(void)442 void prelude_timer_flush(void)
443 {
444         walk_and_wake_up_timer(-1);
445 }
446 
447 
448 
449 
450 /**
451  * prelude_timer_lock_critical_region:
452  *
453  * Deactivate timer wake-up until timer_unlock_critical_region() is called.
454  */
prelude_timer_lock_critical_region(void)455 void prelude_timer_lock_critical_region(void)
456 {
457         timer_lock_list();
458 }
459 
460 
461 
462 /**
463  * prelude_timer_unlock_critical_region:
464  *
465  * Reactivate timer wake-up after timer_lock_critical_regions() has been called.
466  */
prelude_timer_unlock_critical_region(void)467 void prelude_timer_unlock_critical_region(void)
468 {
469         timer_unlock_list();
470 }
471 
472 
473 
_prelude_timer_init(void)474 int _prelude_timer_init(void)
475 {
476         return 0;
477 }
478 
479 
480 
_prelude_timer_fork_prepare(void)481 void _prelude_timer_fork_prepare(void)
482 {
483         prelude_timer_lock_critical_region();
484 }
485 
486 
_prelude_timer_fork_parent(void)487 void _prelude_timer_fork_parent(void)
488 {
489         prelude_timer_unlock_critical_region();
490 }
491 
492 
_prelude_timer_fork_child(void)493 void _prelude_timer_fork_child(void)
494 {
495         prelude_list_init(&timer_list);
496         gl_lock_init(mutex);
497 }
498