]> git.proxmox.com Git - mirror_frr.git/blame - pceplib/pcep_timers.c
Merge pull request #12730 from louis-6wind/fix-ext-te-metrics
[mirror_frr.git] / pceplib / pcep_timers.c
CommitLineData
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
43static pcep_timers_context *timers_context_ = NULL;
44static int timer_id_ = 0;
45
46
47/* simple compare method callback used by pcep_utils_ordered_list
48 * for ordered list insertion. */
49int 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. */
63int 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. */
72int 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 78static 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 */
92static 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
123bool 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
141bool 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 */
183void 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
2816045a 200bool teardown_timers(void)
74971473
JG
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
2816045a 255int get_next_timer_id(void)
74971473
JG
256{
257 if (timer_id_ == INT_MAX) {
258 timer_id_ = 0;
259 }
260
261 return timer_id_++;
262}
263
264int 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
307bool 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
347bool 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
439void 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}