[threadpool] Prevent high CPU usage in certain conditions
authorMarek Habersack <grendel@twistedcode.net>
Mon, 14 Sep 2015 11:13:46 +0000 (13:13 +0200)
committerLudovic Henry <ludovic@xamarin.com>
Fri, 18 Sep 2015 17:26:34 +0000 (18:26 +0100)
Threadpool uses poll(2) to watch the file descriptors for activity but
it can sometimes miss that an fd went into error. When that happens the
poll call is interrupted with EAGAIN since the erroneous fd's events have
already been read and there's nothing left in the queue for it. That, in turn,
causes mono to run the loop so quickly that it uses 100% of the CPU. It happens,
for instance, with Xamarin Studio which can utilize two CPU cores to the full while
idling on the opening screen.

This commit makes sure to remove the descriptors with errors from the poll set and
thus preventing the syscall from being interrupted and returning EAGAIN.

mono/metadata/threadpool-ms-io.c

index 47bceb56464c68f39643255cc427a2fe03be6c0d..add3be6086c8c0a38d5056d25ed107bc4d1a1c86 100644 (file)
@@ -290,6 +290,7 @@ wait_callback (gint fd, gint events, gpointer user_data)
                MonoGHashTable *states;
                MonoMList *list = NULL;
                gpointer k;
+               gboolean remove_fd = FALSE;
 
                g_assert (user_data);
                states = user_data;
@@ -311,14 +312,23 @@ wait_callback (gint fd, gint events, gpointer user_data)
                                mono_threadpool_ms_enqueue_work_item (((MonoObject*) sockares)->vtable->domain, (MonoObject*) sockares);
                }
 
-               mono_g_hash_table_replace (states, GINT_TO_POINTER (fd), list);
+               remove_fd = (events & EVENT_ERR) == EVENT_ERR;
+               if (!remove_fd) {
+                       mono_g_hash_table_replace (states, GINT_TO_POINTER (fd), list);
 
-               events = get_events (list);
+                       events = get_events (list);
 
-               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: res fd %3d, events = %2s | %2s",
-                       fd, (events & EVENT_IN) ? "RD" : "..", (events & EVENT_OUT) ? "WR" : "..");
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: res fd %3d, events = %2s | %2s | %2s",
+                               fd, (events & EVENT_IN) ? "RD" : "..", (events & EVENT_OUT) ? "WR" : "..", (events & EVENT_ERR) ? "ERR" : "...");
 
-               threadpool_io->backend.register_fd (fd, events, FALSE);
+                       threadpool_io->backend.register_fd (fd, events, FALSE);
+               } else {
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: err fd %d", fd);
+
+                       mono_g_hash_table_remove (states, GINT_TO_POINTER (fd));
+
+                       threadpool_io->backend.remove_fd (fd);
+               }
        }
 }
 
@@ -368,8 +378,8 @@ selector_thread (gpointer data)
 
                                events = get_events (list);
 
-                               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: %3s fd %3d, events = %2s | %2s",
-                                       exists ? "mod" : "add", fd, (events & EVENT_IN) ? "RD" : "..", (events & EVENT_OUT) ? "WR" : "..");
+                               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: %3s fd %3d, events = %2s | %2s | %2s",
+                                       exists ? "mod" : "add", fd, (events & EVENT_IN) ? "RD" : "..", (events & EVENT_OUT) ? "WR" : "..", (events & EVENT_ERR) ? "ERR" : "...");
 
                                threadpool_io->backend.register_fd (fd, events, !exists);