/* IRCfs - IRC FileServ for *nix. * Copyright (C) 2002 Nick 'Zaf' Clifford * For licensing details, refer to the LICENSE file in the source * code directory. */ #include #include #include #include #include "runtime.h" #include "module.h" #include "select.h" #include "list.h" #include "timeutil.h" #include "debug.h" /* TODO: Change to numhash */ MODULE_NAME("select"); MODULE_INIT(select_init); MODULE_DESTROY(select_destroy); MODULE_DEPENDS(); MODULE_REGISTER(select); static struct list *wait_list; static struct list *post_funcs; static struct list *pre_funcs; static struct list *inter_funcs; static int select_comp_item(struct select_wait_item *a, struct select_wait_item *b); static void select_cleanup_items(); static int select_cleanup_item(struct list *list, struct select_wait_item *item, void *n); const int select_timeout_dont_care=-1; static POOL pool; static DEBUG select_debug; static time_t select_timeout; int select_init() { select_debug = debug_register("select"); debug_off(select_debug); pool = pool_new(NULL); wait_list = list_create(pool); post_funcs = list_create(pool); pre_funcs = list_create(pool); inter_funcs = list_create(pool); return 0; } int select_destroy() { return 0; } time_t select_set_timeout(time_t when) { time_t last; last = select_timeout; select_timeout = when; return last; } void register_wait_list(const struct select_wait_item *item) { int i; struct select_wait_item *it; ASSERT_MODULE_LOADED(); if (!item) return; for(i = 0; item[i].func != NULL; i++) { it = XMALLOC(pool,struct select_wait_item); memcpy(it,&item[i],sizeof(struct select_wait_item)); list_add_front(wait_list,it); } } void register_wait_item(int fd, enum select_mode mode, select_wait_func func, void *data) { struct select_wait_item *it; ASSERT_MODULE_LOADED(); if (!func) return; it = XMALLOC(pool,struct select_wait_item); it->fd = fd; it->mode = mode; it->func = func; it->data = data; it->deleted = 0; list_add_front(wait_list,it); debug(select_debug,"fd(%d): registered wait item for %d", fd,mode); } int select_comp_item(struct select_wait_item *pattern, struct select_wait_item *item) { if (!item->deleted && pattern->fd == item->fd && (pattern->mode == select_mode_any || pattern->mode == item->mode)) return 0; return -1; } int select_dump_item(struct list *list, struct select_wait_item *item, void *n) { debug(select_debug,"fd(%d) mode(%d) func(%p) %s", item->fd,item->mode,item->func, item->deleted ? "deleted" : ""); return 0; } void select_dump_list() { debug(select_debug,"Start of wait list"); list_foreach(wait_list,(list_callback_t)select_dump_item,NULL); debug(select_debug,"End of wait list"); } void unregister_wait_item(int fd, enum select_mode mode) { struct select_wait_item *it; static struct select_wait_item findit; int count=0; findit.fd = fd; findit.mode = mode; findit.deleted = 0; do { it = list_find(wait_list,&findit, (list_compare_t)select_comp_item); if (it) { it->deleted = 1; count++; } } while (it); debug(select_debug,"fd(%d): unregistered %d wait item(s) for %d", fd,count,mode); } int select_cleanup_item(struct list *list, struct select_wait_item *item, void *n) { if (item->deleted) { debug(select_debug,"removing wait for fd(%d) mode(%d)", item->fd,item->mode); list_del_current(list); } return 0; } void select_cleanup_items() { list_foreach(wait_list,(list_callback_t)select_cleanup_item,NULL); } /* a post func gets called after select has polled all the unix fds, * but before it returns from select_wait. */ void register_post_func(select_post_func func) { ASSERT_MODULE_LOADED(); list_add_end(post_funcs,func); } /* a pre func gets called before select polls all of the unix fds. */ void register_pre_func(select_post_func func) { ASSERT_MODULE_LOADED(); list_add_end(pre_funcs,func); } void register_inter_func(select_inter_func func) { ASSERT_MODULE_LOADED(); list_add_end(inter_funcs,func); } struct select_state { fd_set readfds,writefds,errfds; int r; int highest, count; }; int select_wait_setup(struct list *list, struct select_wait_item *item, struct select_state *state) { if (item->deleted) return 0; state->count++; switch(item->mode) { case select_mode_read: FD_SET(item->fd,&(state->readfds)); if (item->fd > state->highest) state->highest = item->fd; break; case select_mode_write: FD_SET(item->fd,&(state->writefds)); if (item->fd > state->highest) state->highest = item->fd; break; case select_mode_error: FD_SET(item->fd,&(state->errfds)); if (item->fd > state->highest) state->highest = item->fd; break; case select_mode_any: FD_SET(item->fd,&(state->readfds)); FD_SET(item->fd,&(state->writefds)); FD_SET(item->fd,&(state->errfds)); if (item->fd > state->highest) state->highest = item->fd; break; } return 0; } int select_wait_call(struct list *list, struct select_wait_item *item, struct select_state *state) { if (item->deleted) return 0; switch(item->mode) { case select_mode_read: if (FD_ISSET(item->fd,&(state->readfds))) item->func(item->fd,item->mode,item->data); break; case select_mode_write: if (FD_ISSET(item->fd,&(state->writefds))) item->func(item->fd,item->mode,item->data); break; case select_mode_error: if (FD_ISSET(item->fd,&(state->errfds))) item->func(item->fd,item->mode,item->data); break; case select_mode_any: /* Careful, item can disappear under our feet once * we've called it. So check to make sure item->func * is still valid. The delete doesn't actually take * place till the end */ if (item->func && FD_ISSET(item->fd,&(state->errfds))) item->func(item->fd,select_mode_error,item->data); if (item->func && FD_ISSET(item->fd,&(state->writefds))) item->func(item->fd,select_mode_write,item->data); if (item->func && FD_ISSET(item->fd,&(state->readfds))) item->func(item->fd,select_mode_read,item->data); } return 0; } int select_call_func(struct list *list, select_post_func func, void *ignore) { func(); return 0; } /* Poll the unix fds. Come back on or about time_out * If there are fds waiting, goes around again unless time_out is * NULL */ int select_wait(struct timeval *timeout) { int r; struct timeval *tvp, tv,leave; static struct select_state state; ASSERT_MODULE_LOADED(); if (timeout) { gettimeofday(&leave,NULL); add_time(&leave,&leave,timeout); } while(1) { FD_ZERO(&state.readfds); FD_ZERO(&state.writefds); FD_ZERO(&state.errfds); state.highest = 0; state.count = 0; list_foreach(wait_list,(list_callback_t)select_wait_setup, &state); list_foreach(pre_funcs,(list_callback_t)select_call_func,NULL); if (timeout) { /* What time is it now */ gettimeofday(&tv,NULL); /* We have to return at "leave" * which is in "tv"-"leave" seconds. */ sub_time(&tv,&leave,&tv); if (tv.tv_sec > select_timeout) { tv.tv_sec = select_timeout; tv.tv_usec = 0; } tvp=&tv; } else { if (select_timeout == select_timeout_dont_care) { tvp = NULL; } else { tv.tv_sec = select_timeout; tv.tv_usec = 0; tvp = &tv; } } debug(select_debug,"select()'ing on %d fds", state.count); r = select(state.highest+1,&state.readfds,&state.writefds, &state.errfds,tvp); if (r == -1 && errno != EINTR) { error(select_debug,"select returned error"); return -1; } if (r == -1) { errno=0; list_foreach(inter_funcs, (list_callback_t)select_call_func, NULL); } if (r > 0) { list_foreach(wait_list, (list_callback_t)select_wait_call, &state); } list_foreach(post_funcs,(list_callback_t)select_call_func, NULL); select_cleanup_items(); // if (timeout == NULL || r == 0) // break; } return 0; }