/* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2003-2004, Jouni Malinen * Sun elects to license this software under the BSD license. * See README for more details. */ #include #include #include #include #include #include #include #include #include "eloop.h" static struct eloop_data eloop; /* * Initialize global event loop data - must be called before any other eloop_* * function. user_data is a pointer to global data structure and will be passed * as eloop_ctx to signal handlers. */ void eloop_init(void *user_data) { (void) memset(&eloop, 0, sizeof (eloop)); eloop.user_data = user_data; } /* * Register handler for read event */ int eloop_register_read_sock(int sock, void (*handler)(int sock, void *eloop_ctx, void *sock_ctx), void *eloop_data, void *user_data) { struct eloop_sock *tmp; tmp = (struct eloop_sock *)realloc(eloop.readers, (eloop.reader_count + 1) * sizeof (struct eloop_sock)); if (tmp == NULL) return (-1); tmp[eloop.reader_count].sock = sock; tmp[eloop.reader_count].eloop_data = eloop_data; tmp[eloop.reader_count].user_data = user_data; tmp[eloop.reader_count].handler = handler; eloop.reader_count++; eloop.readers = tmp; if (sock > eloop.max_sock) eloop.max_sock = sock; return (0); } void eloop_unregister_read_sock(int sock) { int i; if (eloop.readers == NULL || eloop.reader_count == 0) return; for (i = 0; i < eloop.reader_count; i++) { if (eloop.readers[i].sock == sock) break; } if (i == eloop.reader_count) return; if (i != eloop.reader_count - 1) { (void) memmove(&eloop.readers[i], &eloop.readers[i + 1], (eloop.reader_count - i - 1) * sizeof (struct eloop_sock)); } eloop.reader_count--; } /* * Register timeout routines */ int eloop_register_timeout(unsigned int secs, unsigned int usecs, void (*handler)(void *eloop_ctx, void *timeout_ctx), void *eloop_data, void *user_data) { struct eloop_timeout *timeout, *tmp, *prev; timeout = (struct eloop_timeout *)malloc(sizeof (*timeout)); if (timeout == NULL) return (-1); (void) gettimeofday(&timeout->time, NULL); timeout->time.tv_sec += secs; timeout->time.tv_usec += usecs; while (timeout->time.tv_usec >= 1000000) { timeout->time.tv_sec++; timeout->time.tv_usec -= 1000000; } timeout->eloop_data = eloop_data; timeout->user_data = user_data; timeout->handler = handler; timeout->next = NULL; if (eloop.timeout == NULL) { eloop.timeout = timeout; return (0); } prev = NULL; tmp = eloop.timeout; while (tmp != NULL) { if (timercmp(&timeout->time, &tmp->time, < /* */)) break; prev = tmp; tmp = tmp->next; } if (prev == NULL) { timeout->next = eloop.timeout; eloop.timeout = timeout; } else { timeout->next = prev->next; prev->next = timeout; } return (0); } /* * Cancel timeouts matching . * ELOOP_ALL_CTX can be used as a wildcard for cancelling all timeouts * regardless of eloop_data/user_data. */ void eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), void *eloop_data, void *user_data) { struct eloop_timeout *timeout, *prev, *next; prev = NULL; timeout = eloop.timeout; while (timeout != NULL) { next = timeout->next; if (timeout->handler == handler && (timeout->eloop_data == eloop_data || eloop_data == ELOOP_ALL_CTX) && (timeout->user_data == user_data || user_data == ELOOP_ALL_CTX)) { if (prev == NULL) eloop.timeout = next; else prev->next = next; free(timeout); } else prev = timeout; timeout = next; } } static void eloop_handle_signal(int sig) { int i; eloop.signaled++; for (i = 0; i < eloop.signal_count; i++) { if (eloop.signals[i].sig == sig) { eloop.signals[i].signaled++; break; } } } static void eloop_process_pending_signals(void) { int i; if (eloop.signaled == 0) return; eloop.signaled = 0; for (i = 0; i < eloop.signal_count; i++) { if (eloop.signals[i].signaled) { eloop.signals[i].signaled = 0; eloop.signals[i].handler(eloop.signals[i].sig, eloop.user_data, eloop.signals[i].user_data); } } } /* * Register handler for signal. * Note: signals are 'global' events and there is no local eloop_data pointer * like with other handlers. The (global) pointer given to eloop_init() will be * used as eloop_ctx for signal handlers. */ int eloop_register_signal(int sig, void (*handler)(int sig, void *eloop_ctx, void *signal_ctx), void *user_data) { struct eloop_signal *tmp; tmp = (struct eloop_signal *) realloc(eloop.signals, (eloop.signal_count + 1) * sizeof (struct eloop_signal)); if (tmp == NULL) return (-1); tmp[eloop.signal_count].sig = sig; tmp[eloop.signal_count].user_data = user_data; tmp[eloop.signal_count].handler = handler; tmp[eloop.signal_count].signaled = 0; eloop.signal_count++; eloop.signals = tmp; (void) signal(sig, eloop_handle_signal); return (0); } /* * Start event loop and continue running as long as there are any registered * event handlers. */ void eloop_run(void) { struct pollfd pfds[MAX_POLLFDS]; /* array of polled fd */ int i, res; int default_t, t; struct timeval tv, now; default_t = 5 * 1000; /* 5 seconds */ while (!eloop.terminate && (eloop.timeout || eloop.reader_count > 0)) { if (eloop.timeout) { (void) gettimeofday(&now, NULL); if (timercmp(&now, &eloop.timeout->time, < /* */)) timersub(&eloop.timeout->time, &now, &tv); else tv.tv_sec = tv.tv_usec = 0; } t = (eloop.timeout == NULL ? default_t : (tv.tv_sec * 1000 + tv.tv_usec / 1000)); for (i = 0; i < eloop.reader_count; i++) { pfds[i].fd = eloop.readers[i].sock; pfds[i].events = POLLIN | POLLPRI; } res = poll(pfds, eloop.reader_count, t); if (res < 0 && errno != EINTR) return; eloop_process_pending_signals(); /* check if some registered timeouts have occurred */ if (eloop.timeout) { struct eloop_timeout *tmp; (void) gettimeofday(&now, NULL); if (!timercmp(&now, &eloop.timeout->time, < /* */)) { tmp = eloop.timeout; eloop.timeout = eloop.timeout->next; tmp->handler(tmp->eloop_data, tmp->user_data); free(tmp); } } if (res <= 0) continue; for (i = 0; i < eloop.reader_count; i++) { if (pfds[i].revents) { eloop.readers[i].handler( eloop.readers[i].sock, eloop.readers[i].eloop_data, eloop.readers[i].user_data); } } } } /* * Terminate event loop even if there are registered events. */ void eloop_terminate(void) { eloop.terminate = 1; } /* * Free any reserved resources. After calling eloop_destoy(), other eloop_* * functions must not be called before re-running eloop_init(). */ void eloop_destroy(void) { struct eloop_timeout *timeout, *prev; timeout = eloop.timeout; while (timeout != NULL) { prev = timeout; timeout = timeout->next; free(prev); } free(eloop.readers); free(eloop.signals); }