diff --git a/src/common/multiio.c b/src/common/multiio.c new file mode 100644 --- /dev/null +++ b/src/common/multiio.c @@ -0,0 +1,378 @@ +/* + Copyright 2010 Nathan Phillip Brink + + This file is a part of DistRen. + + DistRen is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DistRen is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with DistRen. If not, see . + +*/ + +#include "common/multiio.h" + +#include +#include +#include +#include +#include +#include + +struct multiio_socket_info +{ + /* the type of socket */ + multiio_socket_type_t socket_type; + /* to be passed to the socket handler */ + void *socket_data; +}; + +struct multiio_socket_type_info +{ + /* type: struct multiio_socket_type_handler_info */ + list_t type_handlers; +}; + +/* + The basic concept is that pollfds is an array. Thus, we have to + have another array reflecting that array to hold socket-specific + information. + */ +struct multiio_context +{ + /* the array passed to poll() */ + struct pollfd *pollfds; + /* the number of entries pollfds could hold */ + nfds_t pollfds_len; + /* the number of entries that pollfds does hold */ + nfds_t nfds; + + /* + an array whose order mirrors pollfds containing information + about the fd mentioned in pollfds. + */ + struct multiio_socket_info *socket_infos; + + /* the number of socket types registered (equivilent to the type ID of the next registered type) */ + multiio_socket_type_t num_socket_types; + /* the information about each individual socket type keyed by the type ID */ + struct multiio_socket_type_info *socket_types; +}; + +/* + stores information about a handler associated with a socket type/class. + */ +struct multiio_socket_type_handler_info +{ + /* the poll() event to which this handler responds */ + short event; + /* the handler function to call when the event is matched */ + multiio_event_handler_func_t handler; + /* the pointer passed to multiio_event_handler_register() */ + void *handler_data; +}; + +/** + Finds the index in the context->pollfds array of a particular socket. + + @param context the multiio context + @param fd the socket to search for + @param index a pointer which will be filled with the index of the socket if found + @return 0 if the pollfds entry is found, 1 if the entry is not found + */ +int multiio_pollfd_index_by_fd(const multiio_context_t context, int fd, size_t *index); + +multiio_context_t multiio_context_new() +{ + struct multiio_context *context; + + context = malloc(sizeof(struct multiio_context)); + if(!context) + return NULL; + + context->pollfds = malloc(sizeof(struct pollfd) * 2); + if(!context->pollfds) + { + free(context); + return NULL; + } + context->pollfds_len = 2; + context->nfds = 0; + context->socket_infos = malloc(sizeof(struct multiio_socket_info) * 2); + + context->num_socket_types = 0; + context->socket_types = NULL; + + if(!context->pollfds + || !context->socket_infos) + { + free(context->pollfds); + free(context->socket_infos); + free(context); + + return NULL; + } + + return context; +} + +int multiio_context_free(multiio_context_t context) +{ + size_t counter; + /* + clean up pollfds and socket_infos + */ + free(context->pollfds); + free(context->socket_infos); + + /* + wipe up the socket_types + */ + for(counter = 0; counter < context->num_socket_types; counter ++) + list_free(context->socket_types[counter].type_handlers, LIST_DEALLOC); + free(context->socket_types); + + /* + bye! + */ + free(context); + + return 0; +} + +struct multiio_poll_travinfo +{ + multiio_context_t multiio; + struct multiio_socket_info *socket_info; + struct pollfd *pollfd; +}; +/** + list traverser for multiio_poll() + */ +int mulitiio_poll_invoke_handlers(struct multiio_poll_travinfo *travinfo, struct multiio_socket_type_handler_info *handler_info) +{ + short event; + + event = travinfo->pollfd->events & handler_info->event; + if(!event) + return TRUE; + + handler_info->handler(travinfo->multiio, + travinfo->pollfd->fd, + event, + handler_info->handler_data, + travinfo->socket_info->socket_data); + + return TRUE; +} + +int multiio_poll(multiio_context_t context) +{ + size_t counter; + struct multiio_poll_travinfo travinfo; + + poll(context->pollfds, context->nfds, -1); + + for(counter = 0; counter < context->nfds; counter ++) + if(context->pollfds[counter].revents) + { + travinfo.multiio = context; + travinfo.socket_info = &context->socket_infos[counter]; + travinfo.pollfd = &context->pollfds[counter]; + list_traverse(context->socket_types[context->socket_infos[counter].socket_type].type_handlers, + &travinfo, + (list_traverse_func_t)&mulitiio_poll_invoke_handlers, + LIST_FRNT | LIST_SAVE); + } + + return 0; +} + +int multiio_socket_type_register(multiio_context_t context, multiio_socket_type_t *new_type) +{ + struct multiio_socket_type_info *new_socket_types; + + new_socket_types = malloc(sizeof(struct multiio_socket_type_info) * (context->num_socket_types + 1)); + if(!new_socket_types) + return 1; + + *new_type = context->num_socket_types; + new_socket_types[*new_type].type_handlers = list_init(); + if(!new_socket_types[*new_type].type_handlers) + { + free(new_socket_types); + return 2; + } + + + if(context->num_socket_types) + { + memcpy(new_socket_types, context->socket_types, sizeof(struct multiio_socket_type_info) * context->num_socket_types); + free(context->socket_types); + } + + context->socket_types = new_socket_types; + context->num_socket_types ++; + + return 0; +} + +int multiio_event_handler_register(multiio_context_t context, multiio_socket_type_t socket_type, short event, multiio_event_handler_func_t handler_func, void *handler_data) +{ + struct multiio_socket_type_handler_info handler_info; + + if(socket_type >= context->num_socket_types) + return 1; + + if(!event) + return 2; + + handler_info.event = event; + handler_info.handler = handler_func; + handler_info.handler_data = handler_data; + + list_insert_after(context->socket_types[socket_type].type_handlers, &handler_info, sizeof(struct multiio_socket_type_handler_info)); + + return 0; +} + +int multiio_socket_add(multiio_context_t context, int fd, multiio_socket_type_t socket_type, void *socket_data, short events) +{ + struct pollfd *pollfds; + struct multiio_socket_info *socket_infos; + size_t new_pollfds_len; + + int fdstatus; + int tmp; + + fdstatus = fcntl(fd, F_GETFL); + fdstatus |= O_NONBLOCK; + tmp = fcntl(fd, F_SETFL, fdstatus); + if(tmp == -1) + { + perror("fcntl"); + return 1; + } + + if(socket_type >= context->num_socket_types) + return 1; + + /** + extend sockfds array + */ + if(context->nfds >= context->pollfds_len) + { + new_pollfds_len = (context->pollfds_len + 1) * 2; + + pollfds = malloc(sizeof(struct pollfd) * new_pollfds_len); + socket_infos = malloc(sizeof(struct multiio_socket_info) * new_pollfds_len); + + if(!pollfds + || !socket_infos) + return 1; + + memcpy(pollfds, context->pollfds, sizeof(struct pollfd) * context->pollfds_len); + memcpy(socket_infos, context->socket_infos, sizeof(struct multiio_socket_info) * context->pollfds_len); + + free(context->pollfds); + free(context->socket_infos); + + context->pollfds = pollfds; + context->socket_infos = socket_infos; + context->pollfds_len = new_pollfds_len; + } + + pollfds = context->pollfds; + socket_infos = context->socket_infos; + + pollfds[context->nfds].fd = fd; + pollfds[context->nfds].events = events; + socket_infos[context->nfds].socket_type = socket_type; + socket_infos[context->nfds].socket_data = socket_data; + + context->nfds ++; + + return 0; +} + +int multiio_socket_del(multiio_context_t context, int fd) +{ + size_t index; + int tmp; + + tmp = multiio_pollfd_index_by_fd(context, fd, &index); + if(tmp) + return 1; + + /** + * Special case: one left, if we tried filling in holes... segfault ;-) + */ + if(context->nfds == 1) + { + context->nfds --; + return 0; + } + + /** + * For now we'll unconditionally fill in holes. + * In fact, it looks simple enough that this method + * may be what we always use ;-). + */ + memcpy(&context->pollfds[index], &context->pollfds[context->nfds - 1], sizeof(struct pollfd)); + memcpy(&context->socket_infos[index], &context->socket_infos[context->nfds - 1], sizeof(struct multiio_socket_info)); + context->nfds --; + + return 0; +} + +int multiio_socket_event_enable(multiio_context_t context, int fd, short event) +{ + size_t index; + int tmp; + + tmp = multiio_pollfd_index_by_fd(context, fd, &index); + if(tmp) + return 1; + + context->pollfds[index].events |= event; + + return 0; +} + +int multiio_socket_event_disable(multiio_context_t context, int fd, short event) +{ + size_t index; + int tmp; + + tmp = multiio_pollfd_index_by_fd(context, fd, &index); + if(tmp) + return 1; + + context->pollfds[index].events &= ~event; + + return 0; +} + + + +int multiio_pollfd_index_by_fd(const multiio_context_t context, int fd, size_t *index) +{ + size_t counter; + + for(counter = 0; counter < context->nfds; counter ++) + if(context->pollfds[counter].fd == fd) + { + *index = counter; + return 0; + } + + return 1; +}