#include "owl.h" static const char fileIdent[] = "$Id: select.c 894 2008-01-17 07:13:44Z asedeno $"; int _owl_select_timer_cmp(owl_timer *t1, owl_timer *t2, void *data) { return t1->time - t2->time; } int _owl_select_timer_eq(owl_timer *t1, owl_timer *t2, void *data) { return t1 == t2; } owl_timer *owl_select_add_timer(int after, int interval, void (*cb)(struct _owl_timer *, void *), void *data) { owl_timer *t = owl_malloc(sizeof(owl_timer)); GSequence *timers = owl_global_get_timerlist(&g); t->time = time(NULL) + after; t->interval = interval; t->callback = cb; t->data = data; g_sequence_insert_sorted(timers, t, (GCompareDataFunc)_owl_select_timer_cmp, NULL); return t; } void owl_select_remove_timer(owl_timer *t) { GSequenceIter *it = g_sequence_search(owl_global_get_timerlist(&g), t, (GCompareDataFunc)_owl_select_timer_eq, NULL); if(!g_sequence_iter_is_end(it) && g_sequence_get(it) == t) { owl_free(t); g_sequence_remove(it); } } void owl_select_process_timers(struct timeval *timeout) { time_t now = time(NULL); GSequenceIter *it = g_sequence_get_begin_iter(owl_global_get_timerlist(&g)); while(!g_sequence_iter_is_end(it)) { owl_timer *t = g_sequence_get(it); int remove = 0; if(t->time > now) break; /* Reschedule if appropriate */ if(t->interval > 0) { t->time = now + t->interval; g_sequence_sort_changed(it, (GCompareDataFunc)_owl_select_timer_cmp, NULL); } else { g_sequence_remove(it); remove = 1; } /* Do the callback */ t->callback(t, t->data); if (remove) { owl_free(t); } it = g_sequence_get_begin_iter(owl_global_get_timerlist(&g)); } if(g_sequence_iter_is_end(it)) { timeout->tv_sec = 60; } else { owl_timer *t = g_sequence_get(it); timeout->tv_sec = t->time - now; } timeout->tv_usec = 0; } /* Returns the index of the dispatch for the file descriptor. */ int owl_select_find_dispatch(int fd) { int i, len; owl_list *dl; owl_dispatch *d; dl = owl_global_get_dispatchlist(&g); len = owl_list_get_size(dl); for(i = 0; i < len; i++) { d = (owl_dispatch*)owl_list_get_element(dl, i); if (d->fd == fd) return i; } return -1; } /* Adds a new owl_dispatch to the list, replacing existing ones if needed. */ void owl_select_add_dispatch(owl_dispatch *d) { int elt; owl_list *dl; elt = owl_select_find_dispatch(d->fd); dl = owl_global_get_dispatchlist(&g); if (elt != -1) { /* If we have a dispatch for this FD */ owl_dispatch *d_old; d_old = (owl_dispatch*)owl_list_get_element(dl, elt); /* Ignore if we're adding the same dispatch again. Otherwise replace the old dispatch. */ if (d_old != d) { owl_list_replace_element(dl, elt, d); owl_free(d_old); } } else { owl_list_append_element(dl, d); } } /* Removes an owl_dispatch to the list, based on it's file descriptor. */ void owl_select_remove_dispatch(int fd) { int elt; owl_list *dl; elt = owl_select_find_dispatch(fd); dl = owl_global_get_dispatchlist(&g); if (elt != -1) { owl_dispatch *d; d = (owl_dispatch*)owl_list_get_element(dl, elt); owl_list_remove_element(dl, elt); if (d->pfunc) { owl_perlconfig_dispatch_free(d); } owl_free(d); } } int owl_select_dispatch_count() { return owl_list_get_size(owl_global_get_dispatchlist(&g)); } int owl_select_add_perl_dispatch(int fd, SV *cb) { int elt; owl_dispatch *d; elt = owl_select_find_dispatch(fd); if (elt != -1) { d = (owl_dispatch*)owl_list_get_element(owl_global_get_dispatchlist(&g), elt); if (d->pfunc == NULL) { /* don't mess with non-perl dispatch functions from here. */ return 1; } } d = malloc(sizeof(owl_dispatch)); d->fd = fd; d->cfunc = NULL; d->pfunc = cb; owl_select_add_dispatch(d); return 0; } int owl_select_remove_perl_dispatch(int fd) { int elt; owl_dispatch *d; elt = owl_select_find_dispatch(fd); if (elt != -1) { d = (owl_dispatch*)owl_list_get_element(owl_global_get_dispatchlist(&g), elt); if (d->pfunc != NULL) { owl_select_remove_dispatch(fd); return 0; } } return 1; } int owl_select_dispatch_prepare_fd_sets(fd_set *r, fd_set *e) { int i, len, max_fd; owl_dispatch *d; owl_list *dl; dl = owl_global_get_dispatchlist(&g); FD_ZERO(r); FD_ZERO(e); max_fd = 0; len = owl_select_dispatch_count(g); for(i = 0; i < len; i++) { d = (owl_dispatch*)owl_list_get_element(dl, i); FD_SET(d->fd, r); FD_SET(d->fd, e); if (max_fd < d->fd) max_fd = d->fd; } return max_fd + 1; } void owl_select_dispatch(fd_set *fds, int max_fd) { int i, len; owl_dispatch *d; owl_list *dl; dl = owl_global_get_dispatchlist(&g); len = owl_select_dispatch_count(); for(i = 0; i < len; i++) { d = (owl_dispatch*)owl_list_get_element(dl, i); /* While d shouldn't normally be null, the list may be altered by * functions we dispatch to. */ if (d != NULL && FD_ISSET(d->fd, fds)) { if (d->cfunc != NULL) { (d->cfunc)(); } else if (d->pfunc != NULL) { owl_perlconfig_do_dispatch(d); } } } } int owl_select_aim_hack(fd_set *rfds, fd_set *wfds) { aim_conn_t *cur; aim_session_t *sess; int max_fd; FD_ZERO(rfds); FD_ZERO(wfds); max_fd = 0; sess = owl_global_get_aimsess(&g); for (cur = sess->connlist, max_fd = 0; cur; cur = cur->next) { if (cur->fd != -1) { FD_SET(cur->fd, rfds); if (cur->status & AIM_CONN_STATUS_INPROGRESS) { /* Yes, we're checking writable sockets here. Without it, AIM login is really slow. */ FD_SET(cur->fd, wfds); } if (cur->fd > max_fd) max_fd = cur->fd; } } return max_fd; } void owl_select() { int i, max_fd, aim_max_fd, aim_done; fd_set r; fd_set e; fd_set aim_rfds, aim_wfds; struct timeval timeout; owl_select_process_timers(&timeout); max_fd = owl_select_dispatch_prepare_fd_sets(&r, &e); /* AIM HACK: * * The problem - I'm not sure where to hook into the owl/faim * interface to keep track of when the AIM socket(s) open and * close. In particular, the bosconn thing throws me off. So, * rather than register particular dispatchers for AIM, I look up * the relevant FDs and add them to select's watch lists, then * check for them individually before moving on to the other * dispatchers. --asedeno */ aim_done = 1; FD_ZERO(&aim_rfds); FD_ZERO(&aim_wfds); if (owl_global_is_doaimevents(&g)) { aim_done = 0; aim_max_fd = owl_select_aim_hack(&aim_rfds, &aim_wfds); if (max_fd < aim_max_fd) max_fd = aim_max_fd; for(i = 0; i <= aim_max_fd; i++) { if (FD_ISSET(i, &aim_rfds)) { FD_SET(i, &r); FD_SET(i, &e); } } } /* END AIM HACK */ if ( select(max_fd+1, &r, &aim_wfds, &e, &timeout) ) { /* Merge fd_sets and clear AIM FDs. */ for(i = 0; i <= max_fd; i++) { /* Merge all interesting FDs into one set, since we have a single dispatch per FD. */ if (FD_ISSET(i, &r) || FD_ISSET(i, &aim_wfds) || FD_ISSET(i, &e)) { /* AIM HACK: no separate dispatch, just process here if needed, and only once per run through. */ if (!aim_done && (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds))) { owl_process_aim(); aim_done = 1; } else { FD_SET(i, &r); } } } /* NOTE: the same dispatch function is called for both exceptional and read ready FDs. */ owl_select_dispatch(&r, max_fd); } }