Files @ d73a30892ea0
Branch filter:

Location: DistRen/src/common/multiio.c

binki
merge
/*
  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 <http://www.gnu.org/licenses/>.

*/

#include "common/config.h"

#include "common/multiio.h"

#include <list.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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, int timeout)
{
  size_t counter;
  struct multiio_poll_travinfo travinfo;

  int ret;

  ret = poll(context->pollfds, context->nfds, timeout);
  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;
}