Merge pull request #2310 from lambdageek/dev/bug-36305
[mono.git] / mono / metadata / threadpool-ms-io-poll.c
index 88bdc9f839be81a2bdcff84a7f8ae9207143ca55..4adae56791b40af7ffab900966b10c267a908000 100644 (file)
@@ -1,5 +1,5 @@
 
-#define POLL_NEVENTS 1024
+#include "utils/mono-poll.h"
 
 static mono_pollfd *poll_fds;
 static guint poll_fds_capacity;
@@ -16,15 +16,14 @@ POLL_INIT_FD (mono_pollfd *poll_fd, gint fd, gint events)
 static gboolean
 poll_init (gint wakeup_pipe_fd)
 {
-       guint i;
+       g_assert (wakeup_pipe_fd >= 0);
 
        poll_fds_size = 1;
-       poll_fds_capacity = POLL_NEVENTS;
+       poll_fds_capacity = 64;
+
        poll_fds = g_new0 (mono_pollfd, poll_fds_capacity);
 
-       POLL_INIT_FD (poll_fds, wakeup_pipe_fd, MONO_POLLIN);
-       for (i = 1; i < poll_fds_capacity; ++i)
-               POLL_INIT_FD (poll_fds + i, -1, 0);
+       POLL_INIT_FD (&poll_fds [0], wakeup_pipe_fd, MONO_POLLIN);
 
        return TRUE;
 }
@@ -35,91 +34,131 @@ poll_cleanup (void)
        g_free (poll_fds);
 }
 
+static void
+poll_register_fd (gint fd, gint events, gboolean is_new)
+{
+       gint i;
+       gint poll_event;
+
+       g_assert (fd >= 0);
+       g_assert (poll_fds_size <= poll_fds_capacity);
+
+       g_assert ((events & ~(EVENT_IN | EVENT_OUT)) == 0);
+
+       poll_event = 0;
+       if (events & EVENT_IN)
+               poll_event |= MONO_POLLIN;
+       if (events & EVENT_OUT)
+               poll_event |= MONO_POLLOUT;
+
+       for (i = 0; i < poll_fds_size; ++i) {
+               if (poll_fds [i].fd == fd) {
+                       g_assert (!is_new);
+                       POLL_INIT_FD (&poll_fds [i], fd, poll_event);
+                       return;
+               }
+       }
+
+       g_assert (is_new);
+
+       for (i = 0; i < poll_fds_size; ++i) {
+               if (poll_fds [i].fd == -1) {
+                       POLL_INIT_FD (&poll_fds [i], fd, poll_event);
+                       return;
+               }
+       }
+
+       poll_fds_size += 1;
+
+       if (poll_fds_size > poll_fds_capacity) {
+               poll_fds_capacity *= 2;
+               g_assert (poll_fds_size <= poll_fds_capacity);
+
+               poll_fds = (mono_pollfd *)g_renew (mono_pollfd, poll_fds, poll_fds_capacity);
+       }
+
+       POLL_INIT_FD (&poll_fds [poll_fds_size - 1], fd, poll_event);
+}
+
+static void
+poll_remove_fd (gint fd)
+{
+       gint i;
+
+       g_assert (fd >= 0);
+
+       for (i = 0; i < poll_fds_size; ++i) {
+               if (poll_fds [i].fd == fd) {
+                       POLL_INIT_FD (&poll_fds [i], -1, 0);
+                       break;
+               }
+       }
+
+       /* if we don't find the fd in poll_fds,
+        * it means we try to delete it twice */
+       g_assert (i < poll_fds_size);
+
+       /* if we find it again, it means we added
+        * it twice */
+       for (; i < poll_fds_size; ++i)
+               g_assert (poll_fds [i].fd != fd);
+
+       /* reduce the value of poll_fds_size so we
+        * do not keep it too big */
+       while (poll_fds_size > 1 && poll_fds [poll_fds_size - 1].fd == -1)
+               poll_fds_size -= 1;
+}
+
 static inline gint
 poll_mark_bad_fds (mono_pollfd *poll_fds, gint poll_fds_size)
 {
-       gint i;
-       gint ret;
-       gint ready = 0;
-       mono_pollfd *poll_fd;
+       gint i, ready = 0;
 
        for (i = 0; i < poll_fds_size; i++) {
-               poll_fd = poll_fds + i;
-               if (poll_fd->fd == -1)
+               if (poll_fds [i].fd == -1)
                        continue;
 
-               ret = mono_poll (poll_fd, 1, 0);
-               if (ret == 1)
+               switch (mono_poll (&poll_fds [i], 1, 0)) {
+               case 1:
                        ready++;
-               if (ret == -1) {
+                       break;
+               case -1:
 #if !defined(HOST_WIN32)
                        if (errno == EBADF)
 #else
                        if (WSAGetLastError () == WSAEBADF)
 #endif
                        {
-                               poll_fd->revents |= MONO_POLLNVAL;
+                               poll_fds [i].revents |= MONO_POLLNVAL;
                                ready++;
                        }
+                       break;
                }
        }
 
        return ready;
 }
 
-static void
-poll_register_fd (gint fd, gint events, gboolean is_new)
+static gint
+poll_event_wait (void (*callback) (gint fd, gint events, gpointer user_data), gpointer user_data)
 {
-       gboolean found = FALSE;
-       gint j, k;
+       gint i, ready;
 
-       for (j = 1; j < poll_fds_size; ++j) {
-               mono_pollfd *poll_fd = poll_fds + j;
-               if (poll_fd->fd == fd) {
-                       found = TRUE;
-                       break;
-               }
-       }
+       for (i = 0; i < poll_fds_size; ++i)
+               poll_fds [i].revents = 0;
 
-       if (events == 0) {
-               if (found)
-                       POLL_INIT_FD (poll_fds + j, -1, 0);
-               return;
-       }
+       mono_gc_set_skip_thread (TRUE);
 
-       if (!found) {
-               for (j = 1; j < poll_fds_capacity; ++j) {
-                       mono_pollfd *poll_fd = poll_fds + j;
-                       if (poll_fd->fd == -1)
-                               break;
-               }
-       }
-
-       if (j == poll_fds_capacity) {
-               poll_fds_capacity += POLL_NEVENTS;
-               poll_fds = g_renew (mono_pollfd, poll_fds, poll_fds_capacity);
-               for (k = j; k < poll_fds_capacity; ++k)
-                       POLL_INIT_FD (poll_fds + k, -1, 0);
-       }
-
-       POLL_INIT_FD (poll_fds + j, fd, events);
-
-       if (j >= poll_fds_size)
-               poll_fds_size = j + 1;
-}
+       ready = mono_poll (poll_fds, poll_fds_size, -1);
 
-static gint
-poll_event_wait (void)
-{
-       gint ready;
+       mono_gc_set_skip_thread (FALSE);
 
-       ready = mono_poll (poll_fds, poll_fds_size, -1);
        if (ready == -1) {
                /*
                 * Apart from EINTR, we only check EBADF, for the rest:
                 *  EINVAL: mono_poll() 'protects' us from descriptor
                 *      numbers above the limit if using select() by marking
-                *      then as MONO_POLLERR.  If a system poll() is being
+                *      then as POLLERR.  If a system poll() is being
                 *      used, the number of descriptor we're passing will not
                 *      be over sysconf(_SC_OPEN_MAX), as the error would have
                 *      happened when opening.
@@ -139,53 +178,66 @@ poll_event_wait (void)
 #else
                case WSAEINTR:
 #endif
+               {
                        mono_thread_internal_check_for_interruption_critical (mono_thread_internal_current ());
                        ready = 0;
                        break;
+               }
 #if !defined(HOST_WIN32)
                case EBADF:
 #else
                case WSAEBADF:
 #endif
+               {
                        ready = poll_mark_bad_fds (poll_fds, poll_fds_size);
                        break;
+               }
                default:
 #if !defined(HOST_WIN32)
-                       g_warning ("poll_event_wait: mono_poll () failed, error (%d) %s", errno, g_strerror (errno));
+                       g_error ("poll_event_wait: mono_poll () failed, error (%d) %s", errno, g_strerror (errno));
 #else
-                       g_warning ("poll_event_wait: mono_poll () failed, error (%d)\n", WSAGetLastError ());
+                       g_error ("poll_event_wait: mono_poll () failed, error (%d)\n", WSAGetLastError ());
 #endif
                        break;
                }
        }
 
-       return ready;
-}
+       if (ready == -1)
+               return -1;
+       if (ready == 0)
+               return 0;
 
-static gint
-poll_event_get_fd_at (gint i, gint *events)
-{
-       g_assert (events);
+       g_assert (ready > 0);
 
-       *events = ((poll_fds [i].revents & (MONO_POLLIN | MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL)) ? MONO_POLLIN : 0)
-                   | ((poll_fds [i].revents & (MONO_POLLOUT | MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL)) ? MONO_POLLOUT : 0);
+       for (i = 0; i < poll_fds_size; ++i) {
+               gint fd, events = 0;
 
-       /* if nothing happened on the fd, then just return
-        * an invalid fd number so it is discarded */
-       return poll_fds [i].revents == 0 ? -1 : poll_fds [i].fd;
-}
+               if (poll_fds [i].fd == -1)
+                       continue;
+               if (poll_fds [i].revents == 0)
+                       continue;
 
-static gint
-poll_event_get_fd_max (void)
-{
-       return poll_fds_size;
+               fd = poll_fds [i].fd;
+               if (poll_fds [i].revents & (MONO_POLLIN | MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL))
+                       events |= EVENT_IN;
+               if (poll_fds [i].revents & (MONO_POLLOUT | MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL))
+                       events |= EVENT_OUT;
+               if (poll_fds [i].revents & (MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL))
+                       events |= EVENT_ERR;
+
+               callback (fd, events, user_data);
+
+               if (--ready == 0)
+                       break;
+       }
+
+       return 0;
 }
 
 static ThreadPoolIOBackend backend_poll = {
        .init = poll_init,
        .cleanup = poll_cleanup,
        .register_fd = poll_register_fd,
+       .remove_fd = poll_remove_fd,
        .event_wait = poll_event_wait,
-       .event_get_fd_max = poll_event_get_fd_max,
-       .event_get_fd_at = poll_event_get_fd_at,
 };