2 * event.c Non-thread-safe event handling, specific to a RADIUS
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * Copyright 2007 The FreeRADIUS server project
22 * Copyright 2007 Alan DeKok <aland@ox.org>
25 #include <freeradius-devel/ident.h>
28 #include <freeradius-devel/libradius.h>
29 #include <freeradius-devel/heap.h>
30 #include <freeradius-devel/event.h>
32 typedef struct fr_event_fd_t {
34 fr_event_fd_handler_t handler;
38 #define FR_EV_MAX_FDS (256)
40 #define USEC (1000000)
42 struct fr_event_list_t {
49 fr_event_status_t status;
55 fr_event_fd_t readers[FR_EV_MAX_FDS];
59 * Internal structure for managing events.
62 fr_event_callback_t callback;
70 static int fr_event_list_time_cmp(const void *one, const void *two)
72 const fr_event_t *a = one;
73 const fr_event_t *b = two;
75 if (a->when.tv_sec < b->when.tv_sec) return -1;
76 if (a->when.tv_sec > b->when.tv_sec) return +1;
78 if (a->when.tv_usec < b->when.tv_usec) return -1;
79 if (a->when.tv_usec > b->when.tv_usec) return +1;
85 void fr_event_list_free(fr_event_list_t *el)
91 while ((ev = fr_heap_peek(el->times)) != NULL) {
92 fr_event_delete(el, &ev);
95 fr_heap_delete(el->times);
100 fr_event_list_t *fr_event_list_create(fr_event_status_t status)
105 el = malloc(sizeof(*el));
106 if (!el) return NULL;
107 memset(el, 0, sizeof(*el));
109 el->times = fr_heap_create(fr_event_list_time_cmp,
110 offsetof(fr_event_t, heap));
112 fr_event_list_free(el);
116 for (i = 0; i < FR_EV_MAX_FDS; i++) {
117 el->readers[i].fd = -1;
121 el->changed = 1; /* force re-set of fds's */
126 int fr_event_list_num_elements(fr_event_list_t *el)
130 return fr_heap_num_elements(el->times);
134 int fr_event_delete(fr_event_list_t *el, fr_event_t **ev_p)
138 if (!el || !ev_p || !*ev_p) return 0;
141 if (ev->ev_p) *(ev->ev_p) = NULL;
144 fr_heap_extract(el->times, ev);
151 int fr_event_insert(fr_event_list_t *el,
152 fr_event_callback_t callback,
153 void *ctx, struct timeval *when,
158 if (!el || !callback | !when || (when->tv_usec > USEC)) return 0;
160 if (ev_p && *ev_p) fr_event_delete(el, ev_p);
162 ev = malloc(sizeof(*ev));
164 memset(ev, 0, sizeof(*ev));
166 ev->callback = callback;
171 if (!fr_heap_insert(el->times, ev)) {
176 if (ev_p) *ev_p = ev;
181 int fr_event_run(fr_event_list_t *el, struct timeval *when)
183 fr_event_callback_t callback;
189 if (fr_heap_num_elements(el->times) == 0) {
195 ev = fr_heap_peek(el->times);
203 * See if it's time to do this one.
205 if ((ev->when.tv_sec > when->tv_sec) ||
206 ((ev->when.tv_sec == when->tv_sec) &&
207 (ev->when.tv_usec > when->tv_usec))) {
212 callback = ev->callback;
216 * Delete the event before calling it.
218 fr_event_delete(el, &ev);
225 int fr_event_now(fr_event_list_t *el, struct timeval *when)
229 if (el && el->dispatch) {
232 gettimeofday(when, NULL);
239 int fr_event_fd_insert(fr_event_list_t *el, int type, int fd,
240 fr_event_fd_handler_t handler, void *ctx)
245 if (!el || (fd < 0) || !handler || !ctx) return 0;
247 if (type != 0) return 0;
249 if (el->max_readers >= FR_EV_MAX_FDS) return 0;
252 for (i = 0; i <= el->max_readers; i++) {
254 * Be fail-safe on multiple inserts.
256 if (el->readers[i].fd == fd) {
257 if ((el->readers[i].handler != handler) ||
258 (el->readers[i].ctx != ctx)) {
268 if (el->readers[i].fd < 0) {
269 ef = &el->readers[i];
271 if (i == el->max_readers) el->max_readers = i + 1;
278 ef->handler = handler;
287 int fr_event_fd_delete(fr_event_list_t *el, int type, int fd)
291 if (!el || (fd < 0)) return 0;
293 if (type != 0) return 0;
295 for (i = 0; i < el->max_readers; i++) {
296 if (el->readers[i].fd == fd) {
297 el->readers[i].fd = -1;
298 if ((i + 1) == el->max_readers) el->max_readers = i;
308 void fr_event_loop_exit(fr_event_list_t *el, int code)
316 int fr_event_loop(fr_event_list_t *el)
318 int i, rcode, maxfd = 0;
319 struct timeval when, *wake;
320 fd_set read_fds, master_fds;
328 * Cache the list of FD's to watch.
331 FD_ZERO(&master_fds);
333 for (i = 0; i < el->max_readers; i++) {
334 if (el->readers[i].fd < 0) continue;
336 if (el->readers[i].fd > maxfd) {
337 maxfd = el->readers[i].fd;
339 FD_SET(el->readers[i].fd, &master_fds);
346 * Find the first event. If there's none, we wait
347 * on the socket forever.
352 if (fr_heap_num_elements(el->times) > 0) {
355 ev = fr_heap_peek(el->times);
358 gettimeofday(&el->now, NULL);
360 if (timercmp(&el->now, &ev->when, <)) {
362 when.tv_sec -= el->now.tv_sec;
364 if (when.tv_sec > 0) {
366 when.tv_usec += USEC;
368 when.tv_usec -= el->now.tv_usec;
369 if (when.tv_usec > USEC) {
370 when.tv_usec -= USEC;
373 } else { /* we've passed the event time */
384 * Tell someone what the status is.
386 if (el->status) el->status(wake);
388 read_fds = master_fds;
389 rcode = select(maxfd + 1, &read_fds, NULL, NULL, wake);
390 if ((rcode < 0) && (errno != EINTR)) {
395 if (fr_heap_num_elements(el->times) > 0) {
397 gettimeofday(&el->now, NULL);
399 } while (fr_event_run(el, &when) == 1);
402 if (rcode <= 0) continue;
404 for (i = 0; i < el->max_readers; i++) {
405 fr_event_fd_t *ef = &el->readers[i];
407 if (ef->fd < 0) continue;
409 if (!FD_ISSET(ef->fd, &read_fds)) continue;
411 ef->handler(el, ef->fd, ef->ctx);
413 if (el->changed) break;
425 * cc -g -I .. -c rbtree.c -o rbtree.o && cc -g -I .. -c isaac.c -o isaac.o && cc -DTESTING -I .. -c event.c -o event_mine.o && cc event_mine.o rbtree.o isaac.o -o event
429 * And hit CTRL-S to stop the output, CTRL-Q to continue.
430 * It normally alternates printing the time and sleeping,
431 * but when you hit CTRL-S/CTRL-Q, you should see a number
432 * of events run right after each other.
436 * valgrind --tool=memcheck --leak-check=full --show-reachable=yes ./event
439 static void print_time(void *ctx)
441 struct timeval *when = ctx;
443 printf("%d.%06d\n", when->tv_sec, when->tv_usec);
447 static fr_randctx rand_pool;
449 static uint32_t event_rand(void)
453 num = rand_pool.randrsl[rand_pool.randcnt++];
454 if (rand_pool.randcnt == 256) {
455 fr_isaac(&rand_pool);
456 rand_pool.randcnt = 0;
464 int main(int argc, char **argv)
467 struct timeval array[MAX];
468 struct timeval now, when;
471 el = fr_event_list_create();
474 memset(&rand_pool, 0, sizeof(rand_pool));
475 rand_pool.randrsl[1] = time(NULL);
477 fr_randinit(&rand_pool, 1);
478 rand_pool.randcnt = 0;
480 gettimeofday(&array[0], NULL);
481 for (i = 1; i < MAX; i++) {
482 array[i] = array[i - 1];
484 array[i].tv_usec += event_rand() & 0xffff;
485 if (array[i].tv_usec > 1000000) {
486 array[i].tv_usec -= 1000000;
489 fr_event_insert(el, print_time, &array[i], &array[i]);
492 while (fr_event_list_num_elements(el)) {
493 gettimeofday(&now, NULL);
495 if (!fr_event_run(el, &when)) {
496 int delay = (when.tv_sec - now.tv_sec) * 1000000;
497 delay += when.tv_usec;
498 delay -= now.tv_usec;
500 printf("\tsleep %d\n", delay);
506 fr_event_list_free(el);