summaryrefslogtreecommitdiff
path: root/src/common/evsched.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/evsched.c')
-rw-r--r--src/common/evsched.c309
1 files changed, 309 insertions, 0 deletions
diff --git a/src/common/evsched.c b/src/common/evsched.c
new file mode 100644
index 0000000..4e56028
--- /dev/null
+++ b/src/common/evsched.c
@@ -0,0 +1,309 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/time.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "common/evsched.h"
+
+/*!
+ * \brief Set event timer to T (now) + dt miliseconds.
+ */
+static void evsched_settimer(event_t *e, uint32_t dt)
+{
+ if (!e) {
+ return;
+ }
+
+ /* Get absolute time T. */
+ gettimeofday(&e->tv, 0);
+
+ /* Add number of seconds. */
+ e->tv.tv_sec += dt / 1000;
+
+ /* Add the number of microseconds. */
+ e->tv.tv_usec += (dt % 1000) * 1000;
+
+ /* Check for overflow. */
+ while (e->tv.tv_usec > 999999) {
+ e->tv.tv_sec += 1;
+ e->tv.tv_usec -= 1 * 1000 * 1000;
+ }
+}
+
+/*! \brief Singleton application-wide event scheduler. */
+evsched_t *s_evsched = 0;
+
+evsched_t *evsched_new()
+{
+ evsched_t *s = malloc(sizeof(evsched_t));
+ if (!s) {
+ return 0;
+ }
+
+ /* Initialize event calendar. */
+ pthread_mutex_init(&s->rl, 0);
+ pthread_mutex_init(&s->mx, 0);
+ pthread_cond_init(&s->notify, 0);
+ pthread_mutex_init(&s->cache.lock, 0);
+ slab_cache_init(&s->cache.alloc, sizeof(event_t));
+ init_list(&s->calendar);
+ return s;
+}
+
+void evsched_delete(evsched_t **s)
+{
+ if (!s) {
+ return;
+ }
+ if (!*s) {
+ return;
+ }
+
+ /* Deinitialize event calendar. */
+ pthread_mutex_destroy(&(*s)->rl);
+ pthread_mutex_destroy(&(*s)->mx);
+ pthread_cond_destroy(&(*s)->notify);
+ node *n = 0, *nxt = 0;
+ WALK_LIST_DELSAFE(n, nxt, (*s)->calendar) {
+ evsched_event_free((*s), (event_t*)n);
+ }
+
+ /* Free allocator. */
+ slab_cache_destroy(&(*s)->cache.alloc);
+ pthread_mutex_destroy(&(*s)->cache.lock);
+
+ /* Free scheduler. */
+ free(*s);
+ *s = 0;
+}
+
+event_t *evsched_event_new(evsched_t *s, int type)
+{
+ if (!s) {
+ return 0;
+ }
+
+ /* Allocate. */
+ pthread_mutex_lock(&s->cache.lock);
+ event_t *e = slab_cache_alloc(&s->cache.alloc);
+ pthread_mutex_unlock(&s->cache.lock);
+
+ /* Initialize. */
+ memset(e, 0, sizeof(event_t));
+ e->type = type;
+ return e;
+}
+
+void evsched_event_free(evsched_t *s, event_t *ev)
+{
+ if (!s || !ev) {
+ return;
+ }
+
+ pthread_mutex_lock(&s->cache.lock);
+ slab_free(ev);
+ pthread_mutex_unlock(&s->cache.lock);
+}
+
+event_t* evsched_next(evsched_t *s)
+{
+ /* Check. */
+ if (!s) {
+ return 0;
+ }
+
+ /* Lock calendar. */
+ pthread_mutex_lock(&s->mx);
+
+ while(1) {
+
+ /* Check event queue. */
+ if (!EMPTY_LIST(s->calendar)) {
+
+ /* Get current time. */
+ struct timeval dt;
+ gettimeofday(&dt, 0);
+
+ /* Get next event. */
+ event_t *next_ev = HEAD(s->calendar);
+
+ /* Immediately return. */
+ if (timercmp(&dt, &next_ev->tv, >=)) {
+ rem_node(&next_ev->n);
+ pthread_mutex_unlock(&s->mx);
+ pthread_mutex_lock(&s->rl);
+ s->current = next_ev;
+ return next_ev;
+ }
+
+ /* Wait for next event or interrupt. Unlock calendar. */
+ struct timespec ts;
+ ts.tv_sec = next_ev->tv.tv_sec;
+ ts.tv_nsec = next_ev->tv.tv_usec * 1000L;
+ pthread_cond_timedwait(&s->notify, &s->mx, &ts);
+ } else {
+ /* Block until an event is scheduled. Unlock calendar.*/
+ pthread_cond_wait(&s->notify, &s->mx);
+ }
+ }
+
+ /* Unlock calendar, this shouldn't happen. */
+ pthread_mutex_unlock(&s->mx);
+ return 0;
+
+}
+
+int evsched_event_finished(evsched_t *s)
+{
+ if (!s) {
+ return -1;
+ }
+
+ /* Mark as finished. */
+ if (s->current) {
+ s->current = 0;
+ pthread_mutex_unlock(&s->rl);
+ return 0;
+ }
+
+ /* Finished event is not current. */
+ return -1;
+}
+
+int evsched_schedule(evsched_t *s, event_t *ev, uint32_t dt)
+{
+ if (!s || !ev || dt < 0) {
+ return -1;
+ }
+
+ /* Update event timer. */
+ evsched_settimer(ev, dt);
+
+ /* Lock calendar. */
+ pthread_mutex_lock(&s->mx);
+
+ /* Schedule event. */
+ node *n = 0, *prev = 0;
+ if (!EMPTY_LIST(s->calendar)) {
+ WALK_LIST(n, s->calendar) {
+ event_t* cur = (event_t *)n;
+ if (timercmp(&cur->tv, &ev->tv, <)) {
+ prev = n;
+ } else {
+ break;
+ }
+ }
+ }
+
+ /* Append to list. */
+ ev->parent = s;
+ if (prev) {
+ insert_node(&ev->n, prev);
+ } else {
+ add_head(&s->calendar, &ev->n);
+ }
+
+
+ /* Unlock calendar. */
+ pthread_cond_signal(&s->notify);
+ pthread_mutex_unlock(&s->mx);
+
+ return 0;
+}
+
+event_t* evsched_schedule_cb(evsched_t *s, event_cb_t cb, void *data, uint32_t dt)
+{
+ if (!s) {
+ return 0;
+ }
+
+ /* Create event. */
+ event_t *e = evsched_event_new(s, EVSCHED_CB);
+ if (!e) {
+ return 0;
+ }
+ e->cb = cb;
+ e->data = data;
+
+ /* Schedule. */
+ if (evsched_schedule(s, e, dt) != 0) {
+ evsched_event_free(s, e);
+ e = 0;
+ }
+
+ return e;
+}
+
+event_t* evsched_schedule_term(evsched_t *s, uint32_t dt)
+{
+ if (!s) {
+ return 0;
+ }
+
+ /* Create event. */
+ event_t *e = evsched_event_new(s, EVSCHED_TERM);
+ if (!e) {
+ return 0;
+ }
+
+ /* Schedule. */
+ if (evsched_schedule(s, e, dt) != 0) {
+ evsched_event_free(s, e);
+ e = 0;
+ }
+
+ return e;
+}
+
+int evsched_cancel(evsched_t *s, event_t *ev)
+{
+ if (!s || !ev) {
+ return -1;
+ }
+
+ /* Lock calendar. */
+ pthread_mutex_lock(&s->mx);
+
+ /* Make sure not running. */
+ pthread_mutex_lock(&s->rl);
+
+ /* Find in list. */
+ event_t *n = 0;
+ int found = 0;
+ WALK_LIST(n, s->calendar) {
+ if (n == ev) {
+ found = 1;
+ break;
+ }
+ }
+
+ /* Remove from list. */
+ if (found) {
+ rem_node(&ev->n);
+ }
+
+ /* Enable running events. */
+ pthread_mutex_unlock(&s->rl);
+
+ /* Unlock calendar. */
+ pthread_cond_signal(&s->notify);
+ pthread_mutex_unlock(&s->mx);
+
+ return 0;
+}
+