2 * This file is part of the PCEPlib, a PCEP protocol library.
4 * Copyright (C) 2020 Volta Networks https://voltanet.io/
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.
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.
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/>.
19 * Author : Brady Johnson <brady@voltanet.io>
25 * Implementation of public API timer functions.
38 #include "pcep_timers.h"
39 #include "pcep_utils_logging.h"
40 #include "pcep_utils_memory.h"
41 #include "pcep_utils_ordered_list.h"
43 static pcep_timers_context
*timers_context_
= NULL
;
44 static int timer_id_
= 0;
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
)
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
;
60 /* simple compare method callback used by pcep_utils_ordered_list
61 * ordered_list_remove_first_node_equals2 to remove a timer based on
63 int timer_list_node_timer_id_compare(void *list_entry
, void *new_entry
)
65 return ((pcep_timer
*)new_entry
)->timer_id
66 - ((pcep_timer
*)list_entry
)->timer_id
;
69 /* simple compare method callback used by pcep_utils_ordered_list
70 * ordered_list_remove_first_node_equals2 to remove a timer based on
72 int timer_list_node_timer_ptr_compare(void *list_entry
, void *new_entry
)
74 return ((char *)new_entry
- (char *)list_entry
);
77 /* internal util method */
78 static pcep_timers_context
*create_timers_context_(void)
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;
87 return timers_context_
;
91 /* Internal util function */
92 static bool initialize_timers_common(timer_expire_handler expire_handler
)
94 if (expire_handler
== NULL
) {
95 /* Cannot have a NULL handler function */
99 timers_context_
= create_timers_context_();
101 if (timers_context_
->active
== true) {
102 /* already initialized */
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
;
111 if (pthread_mutex_init(&(timers_context_
->timer_list_lock
), NULL
)
115 "%s: ERROR initializing timers, cannot initialize the mutex",
123 bool initialize_timers(timer_expire_handler expire_handler
)
125 if (initialize_timers_common(expire_handler
) == false) {
129 if (pthread_create(&(timers_context_
->event_loop_thread
), NULL
,
130 event_loop
, timers_context_
)) {
133 "%s: ERROR initializing timers, cannot initialize the thread",
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
)
146 if (initialize_timers_common(expire_handler
) == false) {
150 if (thread_create_func
!= NULL
) {
151 if (thread_create_func(&(timers_context_
->event_loop_thread
),
152 NULL
, event_loop
, timers_context_
,
156 "%s: Cannot initialize external timers thread.",
161 if (pthread_create(&(timers_context_
->event_loop_thread
), NULL
,
162 event_loop
, timers_context_
)) {
165 "%s: ERROR initializing timers, cannot initialize the thread",
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
;
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().
183 void free_all_timers(pcep_timers_context
*timers_context
)
185 pthread_mutex_lock(&timers_context
->timer_list_lock
);
187 ordered_list_node
*timer_node
= timers_context
->timer_list
->head
;
189 while (timer_node
!= NULL
) {
190 if (timer_node
->data
!= NULL
) {
191 pceplib_free(PCEPLIB_INFRA
, timer_node
->data
);
193 timer_node
= timer_node
->next_node
;
196 pthread_mutex_unlock(&timers_context
->timer_list_lock
);
200 bool teardown_timers(void)
202 if (timers_context_
== NULL
) {
205 "%s: Trying to teardown the timers, but they are not initialized",
210 if (timers_context_
->active
== false) {
213 "%s: Trying to teardown the timers, but they are not active",
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
223 * is blocked, try joining for at most 1 second.
225 clock_gettime(CLOCK_REALTIME, &ts);
228 pthread_timedjoin_np(timers_context_->event_loop_thread, NULL,
229 &ts); if (retval != 0)
231 pcep_log(LOG_WARNING, "%s: thread did not stop after 1
232 second waiting on it.", __func__);
235 pthread_join(timers_context_
->event_loop_thread
, NULL
);
238 free_all_timers(timers_context_
);
239 ordered_list_destroy(timers_context_
->timer_list
);
241 if (pthread_mutex_destroy(&(timers_context_
->timer_list_lock
)) != 0) {
244 "%s: Trying to teardown the timers, cannot destroy the mutex",
248 pceplib_free(PCEPLIB_INFRA
, timers_context_
);
249 timers_context_
= NULL
;
255 int get_next_timer_id(void)
257 if (timer_id_
== INT_MAX
) {
264 int create_timer(uint16_t sleep_seconds
, void *data
)
266 if (timers_context_
== NULL
) {
269 "%s: Trying to create a timer: the timers have not been initialized",
274 pcep_timer
*timer
= pceplib_malloc(PCEPLIB_INFRA
, sizeof(pcep_timer
));
275 memset(timer
, 0, sizeof(pcep_timer
));
277 timer
->sleep_seconds
= sleep_seconds
;
278 timer
->expire_time
= time(NULL
) + sleep_seconds
;
280 pthread_mutex_lock(&timers_context_
->timer_list_lock
);
281 timer
->timer_id
= get_next_timer_id();
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
);
289 "%s: Trying to create a timer, cannot add the timer to the timer list",
295 pthread_mutex_unlock(&timers_context_
->timer_list_lock
);
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
);
303 return timer
->timer_id
;
307 bool cancel_timer(int timer_id
)
309 static pcep_timer compare_timer
;
311 if (timers_context_
== NULL
) {
314 "%s: Trying to cancel a timer: the timers have not been initialized",
319 pthread_mutex_lock(&timers_context_
->timer_list_lock
);
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
);
329 "%s: Trying to cancel a timer [%d] that does not exist",
334 pthread_mutex_unlock(&timers_context_
->timer_list_lock
);
336 if (timers_context_
->timer_cancel_func
) {
337 timers_context_
->timer_cancel_func(
338 &timer_toRemove
->external_timer
);
341 pceplib_free(PCEPLIB_INFRA
, timer_toRemove
);
347 bool reset_timer(int timer_id
)
349 static pcep_timer compare_timer
;
351 if (timers_context_
== NULL
) {
354 "%s: Trying to reset a timer: the timers have not been initialized",
360 pthread_mutex_lock(&timers_context_
->timer_list_lock
);
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",
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",
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
);
394 ordered_list_remove_node2(timers_context_
->timer_list
,
395 timer_to_reset_node
);
397 timer_to_reset
->expire_time
= expire_time
;
398 if (ordered_list_add_node(timers_context_
->timer_list
, timer_to_reset
)
400 pceplib_free(PCEPLIB_INFRA
, timer_to_reset
);
401 pthread_mutex_unlock(&timers_context_
->timer_list_lock
);
404 "%s: Trying to reset a timer, cannot add the timer to the timer list",
410 pthread_mutex_unlock(&timers_context_
->timer_list_lock
);
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.*/
416 pcep_log(LOG_DEBUG
, "%s: Resetting timer [%d] with callback",
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
;
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
);
439 void pceplib_external_timer_expire_handler(void *data
)
441 if (timers_context_
== NULL
) {
444 "%s: External timer expired but timers_context is not initialized",
449 if (timers_context_
->expire_handler
== NULL
) {
452 "%s: External timer expired but expire_handler is not initialized",
458 pcep_log(LOG_WARNING
,
459 "%s: External timer expired with NULL data", __func__
);
463 pcep_timer
*timer
= (pcep_timer
*)data
;
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
);
470 /* Remove timer from list */
472 ordered_list_remove_node2(timers_context_
->timer_list
,
475 pthread_mutex_unlock(&timers_context_
->timer_list_lock
);
477 /* Cannot continue if the timer does not exist */
478 if (timer_node
== NULL
) {
481 "%s: pceplib_external_timer_expire_handler timer [%p] id [%d] does not exist",
482 __func__
, timer
, timer
->timer_id
);
486 timers_context_
->expire_handler(timer
->data
, timer
->timer_id
);
488 pceplib_free(PCEPLIB_INFRA
, timer
);