1 // SPDX-License-Identifier: LGPL-2.1-or-later
3 * This file is part of the PCEPlib, a PCEP protocol library.
5 * Copyright (C) 2020 Volta Networks https://voltanet.io/
7 * Author : Brady Johnson <brady@voltanet.io>
13 * Implementation of public API timer functions.
26 #include "pcep_timers.h"
27 #include "pcep_utils_logging.h"
28 #include "pcep_utils_memory.h"
29 #include "pcep_utils_ordered_list.h"
31 static pcep_timers_context
*timers_context_
= NULL
;
32 static int timer_id_
= 0;
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
)
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
;
48 /* simple compare method callback used by pcep_utils_ordered_list
49 * ordered_list_remove_first_node_equals2 to remove a timer based on
51 int timer_list_node_timer_id_compare(void *list_entry
, void *new_entry
)
53 return ((pcep_timer
*)new_entry
)->timer_id
54 - ((pcep_timer
*)list_entry
)->timer_id
;
57 /* simple compare method callback used by pcep_utils_ordered_list
58 * ordered_list_remove_first_node_equals2 to remove a timer based on
60 int timer_list_node_timer_ptr_compare(void *list_entry
, void *new_entry
)
62 return ((char *)new_entry
- (char *)list_entry
);
65 /* internal util method */
66 static pcep_timers_context
*create_timers_context_(void)
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;
75 return timers_context_
;
79 /* Internal util function */
80 static bool initialize_timers_common(timer_expire_handler expire_handler
)
82 if (expire_handler
== NULL
) {
83 /* Cannot have a NULL handler function */
87 timers_context_
= create_timers_context_();
89 if (timers_context_
->active
== true) {
90 /* already initialized */
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
;
99 if (pthread_mutex_init(&(timers_context_
->timer_list_lock
), NULL
)
103 "%s: ERROR initializing timers, cannot initialize the mutex",
111 bool initialize_timers(timer_expire_handler expire_handler
)
113 if (initialize_timers_common(expire_handler
) == false) {
117 if (pthread_create(&(timers_context_
->event_loop_thread
), NULL
,
118 event_loop
, timers_context_
)) {
121 "%s: ERROR initializing timers, cannot initialize the thread",
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
)
134 if (initialize_timers_common(expire_handler
) == false) {
138 if (thread_create_func
!= NULL
) {
139 if (thread_create_func(&(timers_context_
->event_loop_thread
),
140 NULL
, event_loop
, timers_context_
,
144 "%s: Cannot initialize external timers thread.",
149 if (pthread_create(&(timers_context_
->event_loop_thread
), NULL
,
150 event_loop
, timers_context_
)) {
153 "%s: ERROR initializing timers, cannot initialize the thread",
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
;
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().
171 void free_all_timers(pcep_timers_context
*timers_context
)
173 pthread_mutex_lock(&timers_context
->timer_list_lock
);
175 ordered_list_node
*timer_node
= timers_context
->timer_list
->head
;
177 while (timer_node
!= NULL
) {
178 if (timer_node
->data
!= NULL
) {
179 pceplib_free(PCEPLIB_INFRA
, timer_node
->data
);
181 timer_node
= timer_node
->next_node
;
184 pthread_mutex_unlock(&timers_context
->timer_list_lock
);
188 bool teardown_timers(void)
190 if (timers_context_
== NULL
) {
193 "%s: Trying to teardown the timers, but they are not initialized",
198 if (timers_context_
->active
== false) {
201 "%s: Trying to teardown the timers, but they are not active",
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
211 * is blocked, try joining for at most 1 second.
213 clock_gettime(CLOCK_REALTIME, &ts);
216 pthread_timedjoin_np(timers_context_->event_loop_thread, NULL,
217 &ts); if (retval != 0)
219 pcep_log(LOG_WARNING, "%s: thread did not stop after 1
220 second waiting on it.", __func__);
223 pthread_join(timers_context_
->event_loop_thread
, NULL
);
226 free_all_timers(timers_context_
);
227 ordered_list_destroy(timers_context_
->timer_list
);
229 if (pthread_mutex_destroy(&(timers_context_
->timer_list_lock
)) != 0) {
232 "%s: Trying to teardown the timers, cannot destroy the mutex",
236 pceplib_free(PCEPLIB_INFRA
, timers_context_
);
237 timers_context_
= NULL
;
243 int get_next_timer_id(void)
245 if (timer_id_
== INT_MAX
) {
252 int create_timer(uint16_t sleep_seconds
, void *data
)
254 if (timers_context_
== NULL
) {
257 "%s: Trying to create a timer: the timers have not been initialized",
262 pcep_timer
*timer
= pceplib_malloc(PCEPLIB_INFRA
, sizeof(pcep_timer
));
263 memset(timer
, 0, sizeof(pcep_timer
));
265 timer
->sleep_seconds
= sleep_seconds
;
266 timer
->expire_time
= time(NULL
) + sleep_seconds
;
268 pthread_mutex_lock(&timers_context_
->timer_list_lock
);
269 timer
->timer_id
= get_next_timer_id();
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
);
277 "%s: Trying to create a timer, cannot add the timer to the timer list",
283 pthread_mutex_unlock(&timers_context_
->timer_list_lock
);
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
);
291 return timer
->timer_id
;
295 bool cancel_timer(int timer_id
)
297 static pcep_timer compare_timer
;
299 if (timers_context_
== NULL
) {
302 "%s: Trying to cancel a timer: the timers have not been initialized",
307 pthread_mutex_lock(&timers_context_
->timer_list_lock
);
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
);
317 "%s: Trying to cancel a timer [%d] that does not exist",
322 pthread_mutex_unlock(&timers_context_
->timer_list_lock
);
324 if (timers_context_
->timer_cancel_func
) {
325 timers_context_
->timer_cancel_func(
326 &timer_toRemove
->external_timer
);
329 pceplib_free(PCEPLIB_INFRA
, timer_toRemove
);
335 bool reset_timer(int timer_id
)
337 static pcep_timer compare_timer
;
339 if (timers_context_
== NULL
) {
342 "%s: Trying to reset a timer: the timers have not been initialized",
348 pthread_mutex_lock(&timers_context_
->timer_list_lock
);
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",
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",
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
);
382 ordered_list_remove_node2(timers_context_
->timer_list
,
383 timer_to_reset_node
);
385 timer_to_reset
->expire_time
= expire_time
;
386 if (ordered_list_add_node(timers_context_
->timer_list
, timer_to_reset
)
388 pceplib_free(PCEPLIB_INFRA
, timer_to_reset
);
389 pthread_mutex_unlock(&timers_context_
->timer_list_lock
);
392 "%s: Trying to reset a timer, cannot add the timer to the timer list",
398 pthread_mutex_unlock(&timers_context_
->timer_list_lock
);
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
;
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
);
427 void pceplib_external_timer_expire_handler(void *data
)
429 if (timers_context_
== NULL
) {
432 "%s: External timer expired but timers_context is not initialized",
437 if (timers_context_
->expire_handler
== NULL
) {
440 "%s: External timer expired but expire_handler is not initialized",
446 pcep_log(LOG_WARNING
,
447 "%s: External timer expired with NULL data", __func__
);
451 pcep_timer
*timer
= (pcep_timer
*)data
;
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
);
458 /* Remove timer from list */
460 ordered_list_remove_node2(timers_context_
->timer_list
,
463 pthread_mutex_unlock(&timers_context_
->timer_list_lock
);
465 /* Cannot continue if the timer does not exist */
466 if (timer_node
== NULL
) {
469 "%s: pceplib_external_timer_expire_handler timer [%p] id [%d] does not exist",
470 __func__
, timer
, timer
->timer_id
);
474 timers_context_
->expire_handler(timer
->data
, timer
->timer_id
);
476 pceplib_free(PCEPLIB_INFRA
, timer
);