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