/*
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 multiio_poll_invoke_handlers(struct multiio_poll_travinfo *travinfo, struct multiio_socket_type_handler_info *handler_info)
{
short event;
event = travinfo->pollfd->revents & 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;
int ret;
ret = poll(context->pollfds, context->nfds, -1);
if(ret == -1)
{
perror("poll");
}
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)&multiio_poll_invoke_handlers,
LIST_FRNT | LIST_SAVE);
context->pollfds[counter].revents = 0;
}
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;
memset(&pollfds[context->nfds], 0, sizeof(struct pollfd));
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;
}