From f449f278dd3c70e479a035f50a9bb817a9b433ba Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 17:24:08 +0200 Subject: Adding upstream version 3.2.6. Signed-off-by: Daniel Baumann --- src/knot/common/evsched.c | 268 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 src/knot/common/evsched.c (limited to 'src/knot/common/evsched.c') diff --git a/src/knot/common/evsched.c b/src/knot/common/evsched.c new file mode 100644 index 0000000..0d65c6a --- /dev/null +++ b/src/knot/common/evsched.c @@ -0,0 +1,268 @@ +/* Copyright (C) 2022 CZ.NIC, z.s.p.o. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "libknot/libknot.h" +#include "knot/server/dthreads.h" +#include "knot/common/evsched.h" + +/*! \brief Some implementations of timercmp >= are broken, this is for compat.*/ +static inline int timercmp_ge(struct timeval *a, struct timeval *b) { + return !timercmp(a, b, <); +} + +static int compare_event_heap_nodes(void *e1, void *e2) +{ + if (timercmp(&((event_t *)e1)->tv, &((event_t *)e2)->tv, <)) return -1; + if (timercmp(&((event_t *)e1)->tv, &((event_t *)e2)->tv, >)) return 1; + return 0; +} + +/*! + * \brief Get time T (now) + dt milliseconds. + */ +static struct timeval timeval_in(uint32_t dt) +{ + struct timeval tv = { 0 }; + gettimeofday(&tv, NULL); + + /* Add number of seconds. */ + tv.tv_sec += dt / 1000; + + /* Add the number of microseconds. */ + tv.tv_usec += (dt % 1000) * 1000; + + /* Check for overflow. */ + while (tv.tv_usec > 999999) { + tv.tv_sec += 1; + tv.tv_usec -= 1 * 1000 * 1000; + } + + return tv; +} + +/*! \brief Event scheduler loop. */ +static int evsched_run(dthread_t *thread) +{ + evsched_t *sched = (evsched_t*)thread->data; + if (sched == NULL) { + return KNOT_EINVAL; + } + + /* Run event loop. */ + pthread_mutex_lock(&sched->heap_lock); + while (!dt_is_cancelled(thread)) { + if (!!EMPTY_HEAP(&sched->heap) || sched->paused) { + pthread_cond_wait(&sched->notify, &sched->heap_lock); + continue; + } + + /* Get current time. */ + struct timeval dt; + gettimeofday(&dt, 0); + + /* Get next event. */ + event_t *ev = *((event_t**)HHEAD(&sched->heap)); + assert(ev != NULL); + + if (timercmp_ge(&dt, &ev->tv)) { + heap_delmin(&sched->heap); + ev->cb(ev); + } else { + /* Wait for next event or interrupt. Unlock calendar. */ + struct timespec ts; + ts.tv_sec = ev->tv.tv_sec; + ts.tv_nsec = ev->tv.tv_usec * 1000L; + pthread_cond_timedwait(&sched->notify, &sched->heap_lock, &ts); + } + } + pthread_mutex_unlock(&sched->heap_lock); + + return KNOT_EOK; +} + +int evsched_init(evsched_t *sched, void *ctx) +{ + memset(sched, 0, sizeof(evsched_t)); + sched->ctx = ctx; + + /* Initialize event calendar. */ + pthread_mutex_init(&sched->heap_lock, 0); + pthread_cond_init(&sched->notify, 0); + heap_init(&sched->heap, compare_event_heap_nodes, 0); + + sched->thread = dt_create(1, evsched_run, NULL, sched); + + if (sched->thread == NULL) { + evsched_deinit(sched); + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +void evsched_deinit(evsched_t *sched) +{ + if (sched == NULL) { + return; + } + + /* Deinitialize event calendar. */ + pthread_mutex_destroy(&sched->heap_lock); + pthread_cond_destroy(&sched->notify); + + while (!EMPTY_HEAP(&sched->heap)) { + event_t *e = (event_t *)*HHEAD(&sched->heap); + heap_delmin(&sched->heap); + evsched_event_free(e); + } + + heap_deinit(&sched->heap); + + if (sched->thread != NULL) { + dt_delete(&sched->thread); + } + + /* Clear the structure. */ + memset(sched, 0, sizeof(evsched_t)); +} + +event_t *evsched_event_create(evsched_t *sched, event_cb_t cb, void *data) +{ + /* Create event. */ + if (sched == NULL) { + return NULL; + } + + /* Allocate. */ + event_t *e = malloc(sizeof(event_t)); + if (e == NULL) { + return NULL; + } + + /* Initialize. */ + memset(e, 0, sizeof(event_t)); + e->sched = sched; + e->cb = cb; + e->data = data; + e->hpos.pos = 0; + + return e; +} + +void evsched_event_free(event_t *ev) +{ + if (ev == NULL) { + return; + } + + free(ev); +} + +int evsched_schedule(event_t *ev, uint32_t dt) +{ + if (ev == NULL || ev->sched == NULL) { + return KNOT_EINVAL; + } + + struct timeval new_time = timeval_in(dt); + + evsched_t *sched = ev->sched; + + /* Lock calendar. */ + pthread_mutex_lock(&sched->heap_lock); + + ev->tv = new_time; + + /* Make sure it's not already enqueued. */ + int found = heap_find(&sched->heap, (heap_val_t *)ev); + if (found > 0) { + /* "Replacing" with itself -- just repositioning it. */ + heap_replace(&sched->heap, found, (heap_val_t *)ev); + } else { + heap_insert(&sched->heap, (heap_val_t *)ev); + } + + /* Unlock calendar. */ + pthread_cond_signal(&sched->notify); + pthread_mutex_unlock(&sched->heap_lock); + + return KNOT_EOK; +} + +int evsched_cancel(event_t *ev) +{ + if (ev == NULL || ev->sched == NULL) { + return KNOT_EINVAL; + } + + evsched_t *sched = ev->sched; + + /* Lock calendar. */ + pthread_mutex_lock(&sched->heap_lock); + + int found = heap_find(&sched->heap, (heap_val_t *)ev); + if (found > 0) { + heap_delete(&sched->heap, found); + pthread_cond_signal(&sched->notify); + } + + /* Unlock calendar. */ + pthread_mutex_unlock(&sched->heap_lock); + + /* Reset event timer. */ + memset(&ev->tv, 0, sizeof(struct timeval)); + + return KNOT_EOK; +} + +void evsched_start(evsched_t *sched) +{ + dt_start(sched->thread); +} + +void evsched_stop(evsched_t *sched) +{ + pthread_mutex_lock(&sched->heap_lock); + dt_stop(sched->thread); + pthread_cond_signal(&sched->notify); + pthread_mutex_unlock(&sched->heap_lock); +} + +void evsched_join(evsched_t *sched) +{ + dt_join(sched->thread); +} + +void evsched_pause(evsched_t *sched) +{ + pthread_mutex_lock(&sched->heap_lock); + sched->paused = true; + pthread_mutex_unlock(&sched->heap_lock); +} + +void evsched_resume(evsched_t *sched) +{ + pthread_mutex_lock(&sched->heap_lock); + sched->paused = false; + pthread_cond_signal(&sched->notify); + pthread_mutex_unlock(&sched->heap_lock); +} -- cgit v1.2.3