]>
Commit | Line | Data |
---|---|---|
74971473 JG |
1 | /* |
2 | * This file is part of the PCEPlib, a PCEP protocol library. | |
3 | * | |
4 | * Copyright (C) 2020 Volta Networks https://voltanet.io/ | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public License | |
17 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
18 | * | |
19 | * Author : Brady Johnson <brady@voltanet.io> | |
20 | * | |
21 | */ | |
22 | ||
23 | ||
24 | /* | |
25 | * Implementation of public API timer functions. | |
26 | */ | |
27 | ||
1f8031f7 DL |
28 | #ifdef HAVE_CONFIG_H |
29 | #include "config.h" | |
30 | #endif | |
31 | ||
74971473 JG |
32 | #include <limits.h> |
33 | #include <pthread.h> | |
34 | #include <stddef.h> | |
35 | #include <stdbool.h> | |
36 | #include <string.h> | |
37 | ||
38 | #include "pcep_timers.h" | |
39 | #include "pcep_utils_logging.h" | |
40 | #include "pcep_utils_memory.h" | |
41 | #include "pcep_utils_ordered_list.h" | |
42 | ||
43 | static pcep_timers_context *timers_context_ = NULL; | |
44 | static int timer_id_ = 0; | |
45 | ||
46 | ||
47 | /* simple compare method callback used by pcep_utils_ordered_list | |
48 | * for ordered list insertion. */ | |
49 | int timer_list_node_compare(void *list_entry, void *new_entry) | |
50 | { | |
51 | /* return: | |
52 | * < 0 if new_entry < list_entry | |
53 | * == 0 if new_entry == list_entry (new_entry will be inserted after | |
54 | * list_entry) > 0 if new_entry > list_entry */ | |
55 | return ((pcep_timer *)new_entry)->expire_time | |
56 | - ((pcep_timer *)list_entry)->expire_time; | |
57 | } | |
58 | ||
59 | ||
60 | /* simple compare method callback used by pcep_utils_ordered_list | |
61 | * ordered_list_remove_first_node_equals2 to remove a timer based on | |
62 | * its timer_id. */ | |
63 | int timer_list_node_timer_id_compare(void *list_entry, void *new_entry) | |
64 | { | |
65 | return ((pcep_timer *)new_entry)->timer_id | |
66 | - ((pcep_timer *)list_entry)->timer_id; | |
67 | } | |
68 | ||
69 | /* simple compare method callback used by pcep_utils_ordered_list | |
70 | * ordered_list_remove_first_node_equals2 to remove a timer based on | |
71 | * its address. */ | |
72 | int timer_list_node_timer_ptr_compare(void *list_entry, void *new_entry) | |
73 | { | |
74 | return ((char *)new_entry - (char *)list_entry); | |
75 | } | |
76 | ||
77 | /* internal util method */ | |
c17662db | 78 | static pcep_timers_context *create_timers_context_(void) |
74971473 JG |
79 | { |
80 | if (timers_context_ == NULL) { | |
81 | timers_context_ = pceplib_malloc(PCEPLIB_INFRA, | |
82 | sizeof(pcep_timers_context)); | |
83 | memset(timers_context_, 0, sizeof(pcep_timers_context)); | |
84 | timers_context_->active = false; | |
85 | } | |
86 | ||
87 | return timers_context_; | |
88 | } | |
89 | ||
90 | ||
91 | /* Internal util function */ | |
92 | static bool initialize_timers_common(timer_expire_handler expire_handler) | |
93 | { | |
94 | if (expire_handler == NULL) { | |
95 | /* Cannot have a NULL handler function */ | |
96 | return false; | |
97 | } | |
98 | ||
99 | timers_context_ = create_timers_context_(); | |
100 | ||
101 | if (timers_context_->active == true) { | |
102 | /* already initialized */ | |
103 | return false; | |
104 | } | |
105 | ||
106 | timers_context_->active = true; | |
107 | timers_context_->timer_list = | |
108 | ordered_list_initialize(timer_list_node_compare); | |
109 | timers_context_->expire_handler = expire_handler; | |
110 | ||
111 | if (pthread_mutex_init(&(timers_context_->timer_list_lock), NULL) | |
112 | != 0) { | |
113 | pcep_log( | |
114 | LOG_ERR, | |
115 | "%s: ERROR initializing timers, cannot initialize the mutex", | |
116 | __func__); | |
117 | return false; | |
118 | } | |
119 | ||
120 | return true; | |
121 | } | |
122 | ||
123 | bool initialize_timers(timer_expire_handler expire_handler) | |
124 | { | |
125 | if (initialize_timers_common(expire_handler) == false) { | |
126 | return false; | |
127 | } | |
128 | ||
129 | if (pthread_create(&(timers_context_->event_loop_thread), NULL, | |
130 | event_loop, timers_context_)) { | |
131 | pcep_log( | |
132 | LOG_ERR, | |
133 | "%s: ERROR initializing timers, cannot initialize the thread", | |
134 | __func__); | |
135 | return false; | |
136 | } | |
137 | ||
138 | return true; | |
139 | } | |
140 | ||
141 | bool initialize_timers_external_infra( | |
142 | timer_expire_handler expire_handler, void *external_timer_infra_data, | |
143 | ext_timer_create timer_create_func, ext_timer_cancel timer_cancel_func, | |
144 | ext_pthread_create_callback thread_create_func) | |
145 | { | |
146 | if (initialize_timers_common(expire_handler) == false) { | |
147 | return false; | |
148 | } | |
149 | ||
150 | if (thread_create_func != NULL) { | |
151 | if (thread_create_func(&(timers_context_->event_loop_thread), | |
152 | NULL, event_loop, timers_context_, | |
153 | "pceplib_timers")) { | |
154 | pcep_log( | |
155 | LOG_ERR, | |
156 | "%s: Cannot initialize external timers thread.", | |
157 | __func__); | |
158 | return false; | |
159 | } | |
160 | } else { | |
161 | if (pthread_create(&(timers_context_->event_loop_thread), NULL, | |
162 | event_loop, timers_context_)) { | |
163 | pcep_log( | |
164 | LOG_ERR, | |
165 | "%s: ERROR initializing timers, cannot initialize the thread", | |
166 | __func__); | |
167 | return false; | |
168 | } | |
169 | } | |
170 | ||
171 | timers_context_->external_timer_infra_data = external_timer_infra_data; | |
172 | timers_context_->timer_create_func = timer_create_func; | |
173 | timers_context_->timer_cancel_func = timer_cancel_func; | |
174 | ||
175 | return true; | |
176 | } | |
177 | ||
178 | /* | |
179 | * This function is only used to tear_down the timer data. | |
180 | * Only the timer data is deleted, not the list itself, | |
181 | * which is deleted by ordered_list_destroy(). | |
182 | */ | |
183 | void free_all_timers(pcep_timers_context *timers_context) | |
184 | { | |
185 | pthread_mutex_lock(&timers_context->timer_list_lock); | |
186 | ||
187 | ordered_list_node *timer_node = timers_context->timer_list->head; | |
188 | ||
189 | while (timer_node != NULL) { | |
190 | if (timer_node->data != NULL) { | |
191 | pceplib_free(PCEPLIB_INFRA, timer_node->data); | |
192 | } | |
193 | timer_node = timer_node->next_node; | |
194 | } | |
195 | ||
196 | pthread_mutex_unlock(&timers_context->timer_list_lock); | |
197 | } | |
198 | ||
199 | ||
200 | bool teardown_timers() | |
201 | { | |
202 | if (timers_context_ == NULL) { | |
203 | pcep_log( | |
204 | LOG_WARNING, | |
205 | "%s: Trying to teardown the timers, but they are not initialized", | |
206 | __func__); | |
207 | return false; | |
208 | } | |
209 | ||
210 | if (timers_context_->active == false) { | |
211 | pcep_log( | |
212 | LOG_WARNING, | |
213 | "%s: Trying to teardown the timers, but they are not active", | |
214 | __func__); | |
215 | return false; | |
216 | } | |
217 | ||
218 | timers_context_->active = false; | |
219 | if (timers_context_->event_loop_thread != 0) { | |
220 | /* TODO this does not build | |
221 | * Instead of calling pthread_join() which could block if the | |
222 | thread | |
223 | * is blocked, try joining for at most 1 second. | |
224 | struct timespec ts; | |
225 | clock_gettime(CLOCK_REALTIME, &ts); | |
226 | ts.tv_sec += 1; | |
227 | int retval = | |
228 | pthread_timedjoin_np(timers_context_->event_loop_thread, NULL, | |
229 | &ts); if (retval != 0) | |
230 | { | |
231 | pcep_log(LOG_WARNING, "%s: thread did not stop after 1 | |
232 | second waiting on it.", __func__); | |
233 | } | |
234 | */ | |
235 | pthread_join(timers_context_->event_loop_thread, NULL); | |
236 | } | |
237 | ||
238 | free_all_timers(timers_context_); | |
239 | ordered_list_destroy(timers_context_->timer_list); | |
240 | ||
241 | if (pthread_mutex_destroy(&(timers_context_->timer_list_lock)) != 0) { | |
242 | pcep_log( | |
243 | LOG_WARNING, | |
244 | "%s: Trying to teardown the timers, cannot destroy the mutex", | |
245 | __func__); | |
246 | } | |
247 | ||
248 | pceplib_free(PCEPLIB_INFRA, timers_context_); | |
249 | timers_context_ = NULL; | |
250 | ||
251 | return true; | |
252 | } | |
253 | ||
254 | ||
255 | int get_next_timer_id() | |
256 | { | |
257 | if (timer_id_ == INT_MAX) { | |
258 | timer_id_ = 0; | |
259 | } | |
260 | ||
261 | return timer_id_++; | |
262 | } | |
263 | ||
264 | int create_timer(uint16_t sleep_seconds, void *data) | |
265 | { | |
266 | if (timers_context_ == NULL) { | |
267 | pcep_log( | |
268 | LOG_WARNING, | |
269 | "%s: Trying to create a timer: the timers have not been initialized", | |
270 | __func__); | |
271 | return -1; | |
272 | } | |
273 | ||
274 | pcep_timer *timer = pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_timer)); | |
275 | memset(timer, 0, sizeof(pcep_timer)); | |
276 | timer->data = data; | |
277 | timer->sleep_seconds = sleep_seconds; | |
278 | timer->expire_time = time(NULL) + sleep_seconds; | |
279 | ||
280 | pthread_mutex_lock(&timers_context_->timer_list_lock); | |
281 | timer->timer_id = get_next_timer_id(); | |
282 | ||
283 | /* implemented in pcep_utils_ordered_list.c */ | |
284 | if (ordered_list_add_node(timers_context_->timer_list, timer) == NULL) { | |
285 | pceplib_free(PCEPLIB_INFRA, timer); | |
286 | pthread_mutex_unlock(&timers_context_->timer_list_lock); | |
287 | pcep_log( | |
288 | LOG_WARNING, | |
289 | "%s: Trying to create a timer, cannot add the timer to the timer list", | |
290 | __func__); | |
291 | ||
292 | return -1; | |
293 | } | |
294 | ||
295 | pthread_mutex_unlock(&timers_context_->timer_list_lock); | |
296 | ||
297 | if (timers_context_->timer_create_func) { | |
298 | timers_context_->timer_create_func( | |
299 | timers_context_->external_timer_infra_data, | |
300 | &timer->external_timer, sleep_seconds, timer); | |
301 | } | |
302 | ||
303 | return timer->timer_id; | |
304 | } | |
305 | ||
306 | ||
307 | bool cancel_timer(int timer_id) | |
308 | { | |
309 | static pcep_timer compare_timer; | |
310 | ||
311 | if (timers_context_ == NULL) { | |
312 | pcep_log( | |
313 | LOG_WARNING, | |
314 | "%s: Trying to cancel a timer: the timers have not been initialized", | |
315 | __func__); | |
316 | return false; | |
317 | } | |
318 | ||
319 | pthread_mutex_lock(&timers_context_->timer_list_lock); | |
320 | ||
321 | compare_timer.timer_id = timer_id; | |
322 | pcep_timer *timer_toRemove = ordered_list_remove_first_node_equals2( | |
323 | timers_context_->timer_list, &compare_timer, | |
324 | timer_list_node_timer_id_compare); | |
325 | if (timer_toRemove == NULL) { | |
326 | pthread_mutex_unlock(&timers_context_->timer_list_lock); | |
327 | pcep_log( | |
328 | LOG_WARNING, | |
329 | "%s: Trying to cancel a timer [%d] that does not exist", | |
330 | __func__, timer_id); | |
331 | return false; | |
332 | } | |
333 | ||
334 | pthread_mutex_unlock(&timers_context_->timer_list_lock); | |
335 | ||
336 | if (timers_context_->timer_cancel_func) { | |
337 | timers_context_->timer_cancel_func( | |
338 | &timer_toRemove->external_timer); | |
339 | } | |
340 | ||
341 | pceplib_free(PCEPLIB_INFRA, timer_toRemove); | |
342 | ||
343 | return true; | |
344 | } | |
345 | ||
346 | ||
347 | bool reset_timer(int timer_id) | |
348 | { | |
349 | static pcep_timer compare_timer; | |
350 | ||
351 | if (timers_context_ == NULL) { | |
352 | pcep_log( | |
353 | LOG_WARNING, | |
354 | "%s: Trying to reset a timer: the timers have not been initialized", | |
355 | __func__); | |
356 | ||
357 | return false; | |
358 | } | |
359 | ||
360 | pthread_mutex_lock(&timers_context_->timer_list_lock); | |
361 | ||
362 | compare_timer.timer_id = timer_id; | |
363 | ordered_list_node *timer_to_reset_node = | |
364 | ordered_list_find2(timers_context_->timer_list, &compare_timer, | |
365 | timer_list_node_timer_id_compare); | |
366 | if (timer_to_reset_node == NULL) { | |
367 | pthread_mutex_unlock(&timers_context_->timer_list_lock); | |
368 | pcep_log(LOG_WARNING, | |
369 | "%s: Trying to reset a timer node that does not exist", | |
370 | __func__); | |
371 | ||
372 | return false; | |
373 | } | |
374 | ||
375 | pcep_timer *timer_to_reset = timer_to_reset_node->data; | |
376 | if (timer_to_reset == NULL) { | |
377 | pthread_mutex_unlock(&timers_context_->timer_list_lock); | |
378 | pcep_log(LOG_WARNING, | |
379 | "%s: Trying to reset a timer that does not exist", | |
380 | __func__); | |
381 | ||
382 | return false; | |
383 | } | |
384 | ||
385 | /* First check if the timer to reset already has the same expire time, | |
386 | * which means multiple reset_timer() calls were made on the same timer | |
387 | * in the same second */ | |
388 | time_t expire_time = time(NULL) + timer_to_reset->sleep_seconds; | |
389 | if (timer_to_reset->expire_time == expire_time) { | |
390 | pthread_mutex_unlock(&timers_context_->timer_list_lock); | |
391 | return true; | |
392 | } | |
393 | ||
394 | ordered_list_remove_node2(timers_context_->timer_list, | |
395 | timer_to_reset_node); | |
396 | ||
397 | timer_to_reset->expire_time = expire_time; | |
398 | if (ordered_list_add_node(timers_context_->timer_list, timer_to_reset) | |
399 | == NULL) { | |
400 | pceplib_free(PCEPLIB_INFRA, timer_to_reset); | |
401 | pthread_mutex_unlock(&timers_context_->timer_list_lock); | |
402 | pcep_log( | |
403 | LOG_WARNING, | |
404 | "%s: Trying to reset a timer, cannot add the timer to the timer list", | |
405 | __func__); | |
406 | ||
407 | return false; | |
408 | } | |
409 | ||
410 | pthread_mutex_unlock(&timers_context_->timer_list_lock); | |
411 | ||
412 | if (timers_context_->timer_cancel_func) { | |
413 | /* Keeping this log for now, since in older versions of FRR the | |
414 | * timer cancellation was blocking. This allows us to see how | |
415 | * long the it takes.*/ | |
3d05c4bd | 416 | pcep_log(LOG_DEBUG, "%s: Resetting timer [%d] with callback", |
74971473 JG |
417 | __func__, timer_to_reset->timer_id); |
418 | timers_context_->timer_cancel_func( | |
419 | &timer_to_reset->external_timer); | |
420 | timer_to_reset->external_timer = NULL; | |
421 | } | |
422 | ||
423 | if (timers_context_->timer_create_func) { | |
424 | timers_context_->timer_create_func( | |
425 | timers_context_->external_timer_infra_data, | |
426 | &timer_to_reset->external_timer, | |
427 | timer_to_reset->sleep_seconds, timer_to_reset); | |
428 | /* Keeping this log for now, since in older versions of FRR the | |
429 | * timer cancellation was blocking. This allows us to see how | |
430 | * long the it takes.*/ | |
431 | pcep_log(LOG_DEBUG, "%s: Reset timer [%d] with callback", | |
432 | __func__, timer_to_reset->timer_id); | |
433 | } | |
434 | ||
435 | return true; | |
436 | } | |
437 | ||
438 | ||
439 | void pceplib_external_timer_expire_handler(void *data) | |
440 | { | |
441 | if (timers_context_ == NULL) { | |
442 | pcep_log( | |
443 | LOG_WARNING, | |
444 | "%s: External timer expired but timers_context is not initialized", | |
445 | __func__); | |
446 | return; | |
447 | } | |
448 | ||
449 | if (timers_context_->expire_handler == NULL) { | |
450 | pcep_log( | |
451 | LOG_WARNING, | |
452 | "%s: External timer expired but expire_handler is not initialized", | |
453 | __func__); | |
454 | return; | |
455 | } | |
456 | ||
457 | if (data == NULL) { | |
458 | pcep_log(LOG_WARNING, | |
459 | "%s: External timer expired with NULL data", __func__); | |
460 | return; | |
461 | } | |
462 | ||
463 | pcep_timer *timer = (pcep_timer *)data; | |
cdcb4051 | 464 | |
74971473 JG |
465 | pthread_mutex_lock(&timers_context_->timer_list_lock); |
466 | ordered_list_node *timer_node = | |
467 | ordered_list_find2(timers_context_->timer_list, timer, | |
468 | timer_list_node_timer_ptr_compare); | |
cdcb4051 MS |
469 | |
470 | /* Remove timer from list */ | |
471 | if (timer_node) | |
472 | ordered_list_remove_node2(timers_context_->timer_list, | |
473 | timer_node); | |
474 | ||
74971473 JG |
475 | pthread_mutex_unlock(&timers_context_->timer_list_lock); |
476 | ||
477 | /* Cannot continue if the timer does not exist */ | |
478 | if (timer_node == NULL) { | |
479 | pcep_log( | |
480 | LOG_WARNING, | |
481 | "%s: pceplib_external_timer_expire_handler timer [%p] id [%d] does not exist", | |
482 | __func__, timer, timer->timer_id); | |
483 | return; | |
484 | } | |
485 | ||
486 | timers_context_->expire_handler(timer->data, timer->timer_id); | |
487 | ||
74971473 JG |
488 | pceplib_free(PCEPLIB_INFRA, timer); |
489 | } |