summaryrefslogtreecommitdiffstats
path: root/select.c
diff options
context:
space:
mode:
authorAndreas Eversberg2010-01-16 11:20:23 +0100
committerAndreas Eversberg2010-01-16 11:20:23 +0100
commitb0bd74e35e935aa976b68c594def4e8d2c22ef95 (patch)
tree7e7033beb3b9b1a1976d58ce4e16c6f965a3c9fc /select.c
parentAdded new option to interface.conf: "nonotify" to disable notify messages. (diff)
downloadlcr-b0bd74e35e935aa976b68c594def4e8d2c22ef95.tar.gz
lcr-b0bd74e35e935aa976b68c594def4e8d2c22ef95.tar.xz
lcr-b0bd74e35e935aa976b68c594def4e8d2c22ef95.zip
Replaced polling loop for LCR and chan_lcr with select based event loop.
Now LCR and chan_lcr will not use any CPU until there is work to do.
Diffstat (limited to 'select.c')
-rw-r--r--select.c434
1 files changed, 434 insertions, 0 deletions
diff --git a/select.c b/select.c
new file mode 100644
index 0000000..ba9b563
--- /dev/null
+++ b/select.c
@@ -0,0 +1,434 @@
+/* based on code from OpenBSC */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include "macro.h"
+#include "select.h"
+
+static int maxfd = 0;
+static int unregistered;
+static struct lcr_fd *fd_first = NULL;
+static struct timeval *nearest_timer(struct timeval *select_timer, int *work);
+static int next_work(void);
+
+int _register_fd(struct lcr_fd *fd, int when, int (*cb)(struct lcr_fd *fd, unsigned int what, void *instance, int index), void *instance, int index, const char *func)
+{
+ int flags;
+
+ if (fd->inuse)
+ FATAL("FD that is registered in function %s is already in use\n", func);
+// printf("registering fd %d %s\n", fd->fd, func);
+
+ /* make FD nonblocking */
+ flags = fcntl(fd->fd, F_GETFL);
+ if (flags < 0)
+ FATAL("Failed to F_GETFL\n");
+ flags |= O_NONBLOCK;
+ flags = fcntl(fd->fd, F_SETFL, flags);
+ if (flags < 0)
+ FATAL("Failed to F_SETFL O_NONBLOCK\n");
+
+ /* Register FD */
+ if (fd->fd > maxfd)
+ maxfd = fd->fd;
+
+ /* append to list */
+ fd->inuse = 1;
+ fd->when = when;
+ fd->cb = cb;
+ fd->cb_instance = instance;
+ fd->cb_index = index;
+ fd->next = fd_first;
+ fd_first = fd;
+
+ return 0;
+}
+
+void _unregister_fd(struct lcr_fd *fd, const char *func)
+{
+ struct lcr_fd **lcr_fdp;
+
+ /* find pointer to fd */
+ lcr_fdp = &fd_first;
+ while(*lcr_fdp) {
+ if (*lcr_fdp == fd)
+ break;
+ lcr_fdp = &((*lcr_fdp)->next);
+ }
+ if (!*lcr_fdp) {
+ FATAL("FD unregistered in function %s not in list\n", func);
+ }
+
+ /* remove fd from list */
+ fd->inuse = 0;
+ *lcr_fdp = fd->next;
+ unregistered = 1;
+}
+
+
+int select_main(int polling, int *global_change, void (*lock)(void), void (*unlock)(void))
+{
+ struct lcr_fd *lcr_fd;
+ fd_set readset, writeset, exceptset;
+ int work = 0, temp, rc;
+ struct timeval no_time = {0, 0};
+ struct timeval select_timer, *timer;
+
+ /* goto again;
+ *
+ * this ensures that select is only called until:
+ * - no work event exists
+ * - and no timeout occurred
+ *
+ * if no future timeout exists, select will wait infinit.
+ */
+
+again:
+ /* process all work events */
+ if (next_work()) {
+ work = 1;
+ goto again;
+ }
+
+ /* process timer events and get timeout for next timer event */
+ temp = 0;
+ timer = nearest_timer(&select_timer, &temp);
+ if (temp) {
+ work = 1;
+ goto again;
+ }
+ if (polling)
+ timer = &no_time;
+#warning TESTING
+ if (!timer)
+ printf("wait till infinity ..."); fflush(stdout);
+
+ FD_ZERO(&readset);
+ FD_ZERO(&writeset);
+ FD_ZERO(&exceptset);
+
+ /* prepare read and write fdsets */
+ lcr_fd = fd_first;
+ while(lcr_fd) {
+ if (lcr_fd->when & LCR_FD_READ)
+ FD_SET(lcr_fd->fd, &readset);
+ if (lcr_fd->when & LCR_FD_WRITE)
+ FD_SET(lcr_fd->fd, &writeset);
+ if (lcr_fd->when & LCR_FD_EXCEPT)
+ FD_SET(lcr_fd->fd, &exceptset);
+ lcr_fd = lcr_fd->next;
+ }
+
+ if (unlock)
+ unlock();
+ rc = select(maxfd+1, &readset, &writeset, &exceptset, timer);
+ if (lock)
+ lock();
+#warning TESTING
+ if (!timer)
+ printf("interrupted.\n");
+ if (rc < 0)
+ return 0;
+ if (global_change && *global_change) {
+ *global_change = 0;
+ return 1;
+ }
+
+ /* fire timers */
+#if 0
+ bsc_update_timers();
+#endif
+
+ /* call registered callback functions */
+restart:
+ unregistered = 0;
+ lcr_fd = fd_first;
+ while(lcr_fd) {
+ int flags = 0;
+
+ if (FD_ISSET(lcr_fd->fd, &readset)) {
+ flags |= LCR_FD_READ;
+ FD_CLR(lcr_fd->fd, &readset);
+ }
+ if (FD_ISSET(lcr_fd->fd, &writeset)) {
+ flags |= LCR_FD_WRITE;
+ FD_CLR(lcr_fd->fd, &writeset);
+ }
+ if (FD_ISSET(lcr_fd->fd, &exceptset)) {
+ flags |= LCR_FD_EXCEPT;
+ FD_CLR(lcr_fd->fd, &exceptset);
+ }
+ if (flags) {
+ work = 1;
+ lcr_fd->cb(lcr_fd, flags, lcr_fd->cb_instance, lcr_fd->cb_index);
+ if (unregistered)
+ goto restart;
+ return 1;
+ }
+ lcr_fd = lcr_fd->next;
+ }
+ return work;
+}
+
+
+static struct lcr_timer *timer_first = NULL;
+
+int _add_timer(struct lcr_timer *timer, int (*cb)(struct lcr_timer *timer, void *instance, int index), void *instance, int index, const char *func)
+{
+ if (timer->inuse) {
+ FATAL("timer that is registered in function %s is already in use\n", func);
+ }
+
+#if 0
+ struct lcr_timer *test = timer_first;
+ while(test) {
+ if (test == timer)
+ FATAL("Timer already in list %s\n", func);
+ test = test->next;
+ }
+#endif
+
+ timer->inuse = 1;
+ timer->active = 0;
+ timer->timeout.tv_sec = 0;
+ timer->timeout.tv_usec = 0;
+ timer->cb = cb;
+ timer->cb_instance = instance;
+ timer->cb_index = index;
+ timer->next = timer_first;
+ timer_first = timer;
+
+ return 0;
+}
+
+void _del_timer(struct lcr_timer *timer, const char *func)
+{
+ struct lcr_timer **lcr_timerp;
+
+ /* find pointer to timer */
+ lcr_timerp = &timer_first;
+ while(*lcr_timerp) {
+ if (*lcr_timerp == timer)
+ break;
+ lcr_timerp = &((*lcr_timerp)->next);
+ }
+ if (!*lcr_timerp) {
+ FATAL("timer deleted in function %s not in list\n", func);
+ }
+
+ /* remove timer from list */
+ timer->inuse = 0;
+ *lcr_timerp = timer->next;
+}
+
+void schedule_timer(struct lcr_timer *timer, int seconds, int microseconds)
+{
+ struct timeval current_time;
+
+ if (!timer->inuse) {
+ FATAL("Timer not added\n");
+ }
+
+ gettimeofday(&current_time, NULL);
+ unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec;
+ currentTime += seconds * MICRO_SECONDS + microseconds;
+ timer->timeout.tv_sec = currentTime / MICRO_SECONDS;
+ timer->timeout.tv_usec = currentTime % MICRO_SECONDS;
+ timer->active = 1;
+}
+
+void unsched_timer(struct lcr_timer *timer)
+{
+ timer->active = 0;
+}
+
+/* if a timeout is reached, process timer, if not, return timer value for select */
+static struct timeval *nearest_timer(struct timeval *select_timer, int *work)
+{
+ struct timeval current;
+ struct timeval *nearest = NULL;
+ struct lcr_timer *lcr_timer, *lcr_nearest = NULL;
+
+ /* find nearest timer, or NULL, if no timer active */
+ lcr_timer = timer_first;
+ while(lcr_timer) {
+ if (lcr_timer->active && (!nearest || TIME_SMALLER(&lcr_timer->timeout, nearest))) {
+ nearest = &lcr_timer->timeout;
+ lcr_nearest = lcr_timer;
+ }
+ lcr_timer = lcr_timer->next;
+ }
+
+ select_timer->tv_sec = 0;
+ select_timer->tv_usec = 0;
+
+ if (!nearest)
+ return NULL; /* wait until infinity */
+
+ gettimeofday(&current, NULL);
+ unsigned long long nearestTime = nearest->tv_sec * MICRO_SECONDS + nearest->tv_usec;
+ unsigned long long currentTime = current.tv_sec * MICRO_SECONDS + current.tv_usec;
+
+ if (nearestTime > currentTime) {
+ select_timer->tv_sec = (nearestTime - currentTime) / MICRO_SECONDS;
+ select_timer->tv_usec = (nearestTime - currentTime) % MICRO_SECONDS;
+ return select_timer;
+ } else {
+ lcr_nearest->active = 0;
+ (*lcr_nearest->cb)(lcr_nearest, lcr_nearest->cb_instance, lcr_nearest->cb_index);
+ /* don't wait so we can process the queues, indicate "work=1" */
+ select_timer->tv_sec = 0;
+ select_timer->tv_usec = 0;
+ *work = 1;
+ return select_timer;
+ }
+}
+
+
+static struct lcr_work *work_first = NULL; /* chain of work */
+static struct lcr_work *first_event = NULL, *last_event = NULL; /* chain of active events */
+
+#ifdef DEBUG_WORK
+void show_chain(const char *func)
+{
+ struct lcr_work *work = first_event;
+ printf("chain:%s\n", func);
+ while(work) {
+ printf("%p - %p - %p\n", work->prev_event, work, work->next_event);
+ work = work->next_event;
+ }
+}
+#endif
+
+int _add_work(struct lcr_work *work, int (*cb)(struct lcr_work *work, void *instance, int index), void *instance, int index, const char *func)
+{
+ if (work->inuse) {
+ FATAL("work that is registered in function %s is already in use\n", func);
+ }
+
+#ifdef DEBUG_WORK
+ printf("add work %p from function %s\n", work, func);
+ show_chain("before add");
+#endif
+ work->inuse = 1;
+ work->active = 0;
+ work->cb = cb;
+ work->cb_instance = instance;
+ work->cb_index = index;
+ work->next = work_first;
+ work_first = work;
+#ifdef DEBUG_WORK
+ show_chain("after add");
+#endif
+
+ return 0;
+}
+
+void _del_work(struct lcr_work *work, const char *func)
+{
+ struct lcr_work **lcr_workp;
+
+#ifdef DEBUG_WORK
+ show_chain("before detach");
+#endif
+ if (work->active) {
+ /* first event removed */
+ if (!work->prev_event)
+ first_event = work->next_event;
+ else
+ work->prev_event->next_event = work->next_event;
+ /* last event removed */
+ if (!work->next_event)
+ last_event = work->prev_event;
+ else
+ work->next_event->prev_event = work->prev_event;
+ }
+#ifdef DEBUG_WORK
+ show_chain("after detach");
+#endif
+
+ /* find pointer to work */
+ lcr_workp = &work_first;
+ while(*lcr_workp) {
+ if (*lcr_workp == work)
+ break;
+ lcr_workp = &((*lcr_workp)->next);
+ }
+ if (!*lcr_workp) {
+ FATAL("work deleted by '%s' not in list\n", func);
+ }
+
+ /* remove work from list */
+ work->inuse = 0;
+ *lcr_workp = work->next;
+#ifdef DEBUG_WORK
+ show_chain("after delete");
+#endif
+}
+
+void trigger_work(struct lcr_work *work)
+{
+ if (!work->inuse) {
+ FATAL("Work not added\n");
+ }
+
+ /* event already triggered */
+ if (work->active)
+ return;
+
+#ifdef DEBUG_WORK
+ show_chain("before trigger");
+#endif
+ /* append to tail of chain */
+ if (last_event)
+ last_event->next_event = work;
+ work->prev_event = last_event;
+ work->next_event = NULL;
+ last_event = work;
+ if (!first_event)
+ first_event = work;
+#ifdef DEBUG_WORK
+ show_chain("after trigger");
+#endif
+
+ work->active = 1;
+}
+
+/* get first work and remove from event chain */
+static int next_work(void)
+{
+ struct lcr_work *lcr_work;
+
+ if (!first_event)
+ return 0;
+
+#ifdef DEBUG_WORK
+ show_chain("before next_work");
+#endif
+ if (!first_event->inuse) {
+ FATAL("Work not added\n");
+ }
+
+ /* detach from event chain */
+ lcr_work = first_event;
+ first_event = lcr_work->next_event;
+ if (!first_event)
+ last_event = NULL;
+ else
+ first_event->prev_event = NULL;
+
+#ifdef DEBUG_WORK
+ show_chain("after next_work");
+#endif
+ lcr_work->active = 0;
+
+ (*lcr_work->cb)(lcr_work, lcr_work->cb_instance, lcr_work->cb_index);
+
+ return 1;
+}
+