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