summaryrefslogtreecommitdiffstats
path: root/pceplib/pcep_timers.c
diff options
context:
space:
mode:
Diffstat (limited to 'pceplib/pcep_timers.c')
-rw-r--r--pceplib/pcep_timers.c489
1 files changed, 489 insertions, 0 deletions
diff --git a/pceplib/pcep_timers.c b/pceplib/pcep_timers.c
new file mode 100644
index 0000000..b0f3e70
--- /dev/null
+++ b/pceplib/pcep_timers.c
@@ -0,0 +1,489 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Implementation of public API timer functions.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <limits.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "pcep_timers.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+#include "pcep_utils_ordered_list.h"
+
+static pcep_timers_context *timers_context_ = NULL;
+static int timer_id_ = 0;
+
+
+/* simple compare method callback used by pcep_utils_ordered_list
+ * for ordered list insertion. */
+int timer_list_node_compare(void *list_entry, void *new_entry)
+{
+ /* return:
+ * < 0 if new_entry < list_entry
+ * == 0 if new_entry == list_entry (new_entry will be inserted after
+ * list_entry) > 0 if new_entry > list_entry */
+ return ((pcep_timer *)new_entry)->expire_time
+ - ((pcep_timer *)list_entry)->expire_time;
+}
+
+
+/* simple compare method callback used by pcep_utils_ordered_list
+ * ordered_list_remove_first_node_equals2 to remove a timer based on
+ * its timer_id. */
+int timer_list_node_timer_id_compare(void *list_entry, void *new_entry)
+{
+ return ((pcep_timer *)new_entry)->timer_id
+ - ((pcep_timer *)list_entry)->timer_id;
+}
+
+/* simple compare method callback used by pcep_utils_ordered_list
+ * ordered_list_remove_first_node_equals2 to remove a timer based on
+ * its address. */
+int timer_list_node_timer_ptr_compare(void *list_entry, void *new_entry)
+{
+ return ((char *)new_entry - (char *)list_entry);
+}
+
+/* internal util method */
+static pcep_timers_context *create_timers_context_(void)
+{
+ if (timers_context_ == NULL) {
+ timers_context_ = pceplib_malloc(PCEPLIB_INFRA,
+ sizeof(pcep_timers_context));
+ memset(timers_context_, 0, sizeof(pcep_timers_context));
+ timers_context_->active = false;
+ }
+
+ return timers_context_;
+}
+
+
+/* Internal util function */
+static bool initialize_timers_common(timer_expire_handler expire_handler)
+{
+ if (expire_handler == NULL) {
+ /* Cannot have a NULL handler function */
+ return false;
+ }
+
+ timers_context_ = create_timers_context_();
+
+ if (timers_context_->active == true) {
+ /* already initialized */
+ return false;
+ }
+
+ timers_context_->active = true;
+ timers_context_->timer_list =
+ ordered_list_initialize(timer_list_node_compare);
+ timers_context_->expire_handler = expire_handler;
+
+ if (pthread_mutex_init(&(timers_context_->timer_list_lock), NULL)
+ != 0) {
+ pcep_log(
+ LOG_ERR,
+ "%s: ERROR initializing timers, cannot initialize the mutex",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+bool initialize_timers(timer_expire_handler expire_handler)
+{
+ if (initialize_timers_common(expire_handler) == false) {
+ return false;
+ }
+
+ if (pthread_create(&(timers_context_->event_loop_thread), NULL,
+ event_loop, timers_context_)) {
+ pcep_log(
+ LOG_ERR,
+ "%s: ERROR initializing timers, cannot initialize the thread",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+bool initialize_timers_external_infra(
+ timer_expire_handler expire_handler, void *external_timer_infra_data,
+ ext_timer_create timer_create_func, ext_timer_cancel timer_cancel_func,
+ ext_pthread_create_callback thread_create_func)
+{
+ if (initialize_timers_common(expire_handler) == false) {
+ return false;
+ }
+
+ if (thread_create_func != NULL) {
+ if (thread_create_func(&(timers_context_->event_loop_thread),
+ NULL, event_loop, timers_context_,
+ "pceplib_timers")) {
+ pcep_log(
+ LOG_ERR,
+ "%s: Cannot initialize external timers thread.",
+ __func__);
+ return false;
+ }
+ } else {
+ if (pthread_create(&(timers_context_->event_loop_thread), NULL,
+ event_loop, timers_context_)) {
+ pcep_log(
+ LOG_ERR,
+ "%s: ERROR initializing timers, cannot initialize the thread",
+ __func__);
+ return false;
+ }
+ }
+
+ timers_context_->external_timer_infra_data = external_timer_infra_data;
+ timers_context_->timer_create_func = timer_create_func;
+ timers_context_->timer_cancel_func = timer_cancel_func;
+
+ return true;
+}
+
+/*
+ * This function is only used to tear_down the timer data.
+ * Only the timer data is deleted, not the list itself,
+ * which is deleted by ordered_list_destroy().
+ */
+void free_all_timers(pcep_timers_context *timers_context)
+{
+ pthread_mutex_lock(&timers_context->timer_list_lock);
+
+ ordered_list_node *timer_node = timers_context->timer_list->head;
+
+ while (timer_node != NULL) {
+ if (timer_node->data != NULL) {
+ pceplib_free(PCEPLIB_INFRA, timer_node->data);
+ }
+ timer_node = timer_node->next_node;
+ }
+
+ pthread_mutex_unlock(&timers_context->timer_list_lock);
+}
+
+
+bool teardown_timers()
+{
+ if (timers_context_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to teardown the timers, but they are not initialized",
+ __func__);
+ return false;
+ }
+
+ if (timers_context_->active == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to teardown the timers, but they are not active",
+ __func__);
+ return false;
+ }
+
+ timers_context_->active = false;
+ if (timers_context_->event_loop_thread != 0) {
+ /* TODO this does not build
+ * Instead of calling pthread_join() which could block if the
+ thread
+ * is blocked, try joining for at most 1 second.
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += 1;
+ int retval =
+ pthread_timedjoin_np(timers_context_->event_loop_thread, NULL,
+ &ts); if (retval != 0)
+ {
+ pcep_log(LOG_WARNING, "%s: thread did not stop after 1
+ second waiting on it.", __func__);
+ }
+ */
+ pthread_join(timers_context_->event_loop_thread, NULL);
+ }
+
+ free_all_timers(timers_context_);
+ ordered_list_destroy(timers_context_->timer_list);
+
+ if (pthread_mutex_destroy(&(timers_context_->timer_list_lock)) != 0) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to teardown the timers, cannot destroy the mutex",
+ __func__);
+ }
+
+ pceplib_free(PCEPLIB_INFRA, timers_context_);
+ timers_context_ = NULL;
+
+ return true;
+}
+
+
+int get_next_timer_id()
+{
+ if (timer_id_ == INT_MAX) {
+ timer_id_ = 0;
+ }
+
+ return timer_id_++;
+}
+
+int create_timer(uint16_t sleep_seconds, void *data)
+{
+ if (timers_context_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to create a timer: the timers have not been initialized",
+ __func__);
+ return -1;
+ }
+
+ pcep_timer *timer = pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_timer));
+ memset(timer, 0, sizeof(pcep_timer));
+ timer->data = data;
+ timer->sleep_seconds = sleep_seconds;
+ timer->expire_time = time(NULL) + sleep_seconds;
+
+ pthread_mutex_lock(&timers_context_->timer_list_lock);
+ timer->timer_id = get_next_timer_id();
+
+ /* implemented in pcep_utils_ordered_list.c */
+ if (ordered_list_add_node(timers_context_->timer_list, timer) == NULL) {
+ pceplib_free(PCEPLIB_INFRA, timer);
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to create a timer, cannot add the timer to the timer list",
+ __func__);
+
+ return -1;
+ }
+
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+
+ if (timers_context_->timer_create_func) {
+ timers_context_->timer_create_func(
+ timers_context_->external_timer_infra_data,
+ &timer->external_timer, sleep_seconds, timer);
+ }
+
+ return timer->timer_id;
+}
+
+
+bool cancel_timer(int timer_id)
+{
+ static pcep_timer compare_timer;
+
+ if (timers_context_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to cancel a timer: the timers have not been initialized",
+ __func__);
+ return false;
+ }
+
+ pthread_mutex_lock(&timers_context_->timer_list_lock);
+
+ compare_timer.timer_id = timer_id;
+ pcep_timer *timer_toRemove = ordered_list_remove_first_node_equals2(
+ timers_context_->timer_list, &compare_timer,
+ timer_list_node_timer_id_compare);
+ if (timer_toRemove == NULL) {
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to cancel a timer [%d] that does not exist",
+ __func__, timer_id);
+ return false;
+ }
+
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+
+ if (timers_context_->timer_cancel_func) {
+ timers_context_->timer_cancel_func(
+ &timer_toRemove->external_timer);
+ }
+
+ pceplib_free(PCEPLIB_INFRA, timer_toRemove);
+
+ return true;
+}
+
+
+bool reset_timer(int timer_id)
+{
+ static pcep_timer compare_timer;
+
+ if (timers_context_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to reset a timer: the timers have not been initialized",
+ __func__);
+
+ return false;
+ }
+
+ pthread_mutex_lock(&timers_context_->timer_list_lock);
+
+ compare_timer.timer_id = timer_id;
+ ordered_list_node *timer_to_reset_node =
+ ordered_list_find2(timers_context_->timer_list, &compare_timer,
+ timer_list_node_timer_id_compare);
+ if (timer_to_reset_node == NULL) {
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ pcep_log(LOG_WARNING,
+ "%s: Trying to reset a timer node that does not exist",
+ __func__);
+
+ return false;
+ }
+
+ pcep_timer *timer_to_reset = timer_to_reset_node->data;
+ if (timer_to_reset == NULL) {
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ pcep_log(LOG_WARNING,
+ "%s: Trying to reset a timer that does not exist",
+ __func__);
+
+ return false;
+ }
+
+ /* First check if the timer to reset already has the same expire time,
+ * which means multiple reset_timer() calls were made on the same timer
+ * in the same second */
+ time_t expire_time = time(NULL) + timer_to_reset->sleep_seconds;
+ if (timer_to_reset->expire_time == expire_time) {
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ return true;
+ }
+
+ ordered_list_remove_node2(timers_context_->timer_list,
+ timer_to_reset_node);
+
+ timer_to_reset->expire_time = expire_time;
+ if (ordered_list_add_node(timers_context_->timer_list, timer_to_reset)
+ == NULL) {
+ pceplib_free(PCEPLIB_INFRA, timer_to_reset);
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to reset a timer, cannot add the timer to the timer list",
+ __func__);
+
+ return false;
+ }
+
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+
+ if (timers_context_->timer_cancel_func) {
+ /* Keeping this log for now, since in older versions of FRR the
+ * timer cancellation was blocking. This allows us to see how
+ * long the it takes.*/
+ pcep_log(LOG_DEBUG, "%s: Resetting timer [%d] with callback",
+ __func__, timer_to_reset->timer_id);
+ timers_context_->timer_cancel_func(
+ &timer_to_reset->external_timer);
+ timer_to_reset->external_timer = NULL;
+ }
+
+ if (timers_context_->timer_create_func) {
+ timers_context_->timer_create_func(
+ timers_context_->external_timer_infra_data,
+ &timer_to_reset->external_timer,
+ timer_to_reset->sleep_seconds, timer_to_reset);
+ /* Keeping this log for now, since in older versions of FRR the
+ * timer cancellation was blocking. This allows us to see how
+ * long the it takes.*/
+ pcep_log(LOG_DEBUG, "%s: Reset timer [%d] with callback",
+ __func__, timer_to_reset->timer_id);
+ }
+
+ return true;
+}
+
+
+void pceplib_external_timer_expire_handler(void *data)
+{
+ if (timers_context_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: External timer expired but timers_context is not initialized",
+ __func__);
+ return;
+ }
+
+ if (timers_context_->expire_handler == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: External timer expired but expire_handler is not initialized",
+ __func__);
+ return;
+ }
+
+ if (data == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: External timer expired with NULL data", __func__);
+ return;
+ }
+
+ pcep_timer *timer = (pcep_timer *)data;
+
+ pthread_mutex_lock(&timers_context_->timer_list_lock);
+ ordered_list_node *timer_node =
+ ordered_list_find2(timers_context_->timer_list, timer,
+ timer_list_node_timer_ptr_compare);
+
+ /* Remove timer from list */
+ if (timer_node)
+ ordered_list_remove_node2(timers_context_->timer_list,
+ timer_node);
+
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+
+ /* Cannot continue if the timer does not exist */
+ if (timer_node == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: pceplib_external_timer_expire_handler timer [%p] id [%d] does not exist",
+ __func__, timer, timer->timer_id);
+ return;
+ }
+
+ timers_context_->expire_handler(timer->data, timer->timer_id);
+
+ pceplib_free(PCEPLIB_INFRA, timer);
+}