text: /*
   * tpool-epoll.c: epoll related stuff
   *
   * Authors:
   *   Dietmar Maurer (dietmar@ximian.com)
   *   Gonzalo Paniagua Javier (gonzalo@ximian.com)
   *
   * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
   * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
   * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
   */
  
  struct _tp_epoll_data {
  	int epollfd;
  };
  
  typedef struct _tp_epoll_data tp_epoll_data;
  static void tp_epoll_modify (gpointer event_data, int fd, int operation, int events, gboolean is_new);
  static void tp_epoll_shutdown (gpointer event_data);
  static void tp_epoll_wait (gpointer event_data);
  
  static gpointer
  tp_epoll_init (SocketIOData *data)
  {
  	tp_epoll_data *result;
  
  	result = g_new0 (tp_epoll_data, 1);
  #ifdef EPOLL_CLOEXEC
  	result->epollfd = epoll_create1 (EPOLL_CLOEXEC);
  #else
  	result->epollfd = epoll_create (256); /* The number does not really matter */
  	fcntl (result->epollfd, F_SETFD, FD_CLOEXEC);
  #endif
  	if (result->epollfd == -1) {
  		int err = errno;
  		if (g_getenv ("MONO_DEBUG")) {
  #ifdef EPOLL_CLOEXEC
  			g_message ("epoll_create1(EPOLL_CLOEXEC) failed: %d %s", err, g_strerror (err));
  #else
  			g_message ("epoll_create(256) failed: %d %s", err, g_strerror (err));
  #endif
  		}
  
  		return NULL;
  	}
  
  	data->shutdown = tp_epoll_shutdown;
  	data->modify = tp_epoll_modify;
  	data->wait = tp_epoll_wait;
  	return result;
  }
  
  static void
  tp_epoll_modify (gpointer event_data, int fd, int operation, int events, gboolean is_new)
  {
  	tp_epoll_data *data = event_data;
  	struct epoll_event evt;
  	int epoll_op;
  
  	memset (&evt, 0, sizeof (evt));
  	evt.data.fd = fd;
  	if ((events & MONO_POLLIN) != 0)
  		evt.events |= EPOLLIN;
  	if ((events & MONO_POLLOUT) != 0)
  		evt.events |= EPOLLOUT;
  
  	epoll_op = (is_new) ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;
  	if (epoll_ctl (data->epollfd, epoll_op, fd, &evt) == -1) {
  		int err = errno;
  		if (epoll_op == EPOLL_CTL_ADD && err == EEXIST) {
  			epoll_op = EPOLL_CTL_MOD;
  			if (epoll_ctl (data->epollfd, epoll_op, fd, &evt) == -1) {
  				g_message ("epoll_ctl(MOD): %d %s", err, g_strerror (err));
  			}
  		}
  	}
  }
  
  static void
  tp_epoll_shutdown (gpointer event_data)
  {
  	tp_epoll_data *data = event_data;
  
  	close (data->epollfd);
  	g_free (data);
  }
  
  #define EPOLL_ERRORS (EPOLLERR | EPOLLHUP)
  #define EPOLL_NEVENTS	128
  static void
  tp_epoll_wait (gpointer p)
  {
  	SocketIOData *socket_io_data;
  	int epollfd;
  	MonoInternalThread *thread;
  	struct epoll_event *events, *evt;
  	int ready = 0, i;
  	gpointer async_results [EPOLL_NEVENTS * 2]; // * 2 because each loop can add up to 2 results here
  	gint nresults;
  	tp_epoll_data *data;
  
  	socket_io_data = p;
  	data = socket_io_data->event_data;
  	epollfd = data->epollfd;
  	thread = mono_thread_internal_current ();
  	events = g_new0 (struct epoll_event, EPOLL_NEVENTS);
  
  	while (1) {
  		mono_gc_set_skip_thread (TRUE);
  
  		do {
  			if (ready == -1) {
  				if (THREAD_WANTS_A_BREAK (thread))
  					mono_thread_interruption_checkpoint ();
  			}
  			ready = epoll_wait (epollfd, events, EPOLL_NEVENTS, -1);
  		} while (ready == -1 && errno == EINTR);
  
  		mono_gc_set_skip_thread (FALSE);
  
  		if (ready == -1) {
  			int err = errno;
  			g_free (events);
  			if (err != EBADF)
  				g_warning ("epoll_wait: %d %s", err, g_strerror (err));
  
  			return;
  		}
  
  		EnterCriticalSection (&socket_io_data->io_lock);
  		if (socket_io_data->inited == 3) {
  			g_free (events);
  			LeaveCriticalSection (&socket_io_data->io_lock);
  			return; /* cleanup called */
  		}
  
  		nresults = 0;
  		for (i = 0; i < ready; i++) {
  			int fd;
  			MonoMList *list;
  			MonoObject *ares;
  
  			evt = &events [i];
  			fd = evt->data.fd;
  			list = mono_g_hash_table_lookup (socket_io_data->sock_to_state, GINT_TO_POINTER (fd));
  			if (list != NULL && (evt->events & (EPOLLIN | EPOLL_ERRORS)) != 0) {
  				ares = get_io_event (&list, MONO_POLLIN);
  				if (ares != NULL)
  					async_results [nresults++] = ares;
  			}
  
  			if (list != NULL && (evt->events & (EPOLLOUT | EPOLL_ERRORS)) != 0) {
  				ares = get_io_event (&list, MONO_POLLOUT);
  				if (ares != NULL)
  					async_results [nresults++] = ares;
  			}
  
  			if (list != NULL) {
  				int p;
  
  				mono_g_hash_table_replace (socket_io_data->sock_to_state, GINT_TO_POINTER (fd), list);
  				p = get_events_from_list (list);
  				evt->events = (p & MONO_POLLOUT) ? EPOLLOUT : 0;
  				evt->events |= (p & MONO_POLLIN) ? EPOLLIN : 0;
  				if (epoll_ctl (epollfd, EPOLL_CTL_MOD, fd, evt) == -1) {
  					if (epoll_ctl (epollfd, EPOLL_CTL_ADD, fd, evt) == -1) {
  						int err = errno;
  						g_message ("epoll(ADD): %d %s", err, g_strerror (err));
  					}
  				}
  			} else {
  				mono_g_hash_table_remove (socket_io_data->sock_to_state, GINT_TO_POINTER (fd));
  				epoll_ctl (epollfd, EPOLL_CTL_DEL, fd, evt);
  			}
  		}
  		LeaveCriticalSection (&socket_io_data->io_lock);
  		threadpool_append_jobs (&async_io_tp, (MonoObject **) async_results, nresults);
  		mono_gc_bzero (async_results, sizeof (gpointer) * nresults);
  	}
  }
  #undef EPOLL_NEVENTS
  #undef EPOLL_ERRORS
  