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