gmain: use Linux eventfd() for main context wake up

The Linux eventfd() call is basically tailor made for the main loop
wake up pipe - all we want is a threadsafe way to write to a file
descriptor, and wake up the context on the other end; we don't care
about the content at all.

The eventfd manual page basically explains the benefits:

       Applications can use an eventfd file descriptor instead of a
       pipe (see pipe(2)) in all cases where a pipe is used simply to
       signal events.  The kernel overhead of an eventfd file
       descriptor is much lower than that of a pipe, and only one file
       descriptor is required (versus the two required for a pipe).

When writing my multithreaded spawn test case I actually hit the 1024
file descriptor limit quickly, because we used 2 fds per main context.
This brings that down to 1.

https://bugzilla.gnome.org/show_bug.cgi?id=653140
This commit is contained in:
Colin Walters 2011-06-21 19:01:10 -04:00
parent 29bb7638a5
commit 3904c8761a
2 changed files with 83 additions and 6 deletions

View File

@ -2604,6 +2604,26 @@ main (void)
AC_MSG_RESULT(no)
])
AC_MSG_CHECKING([for eventfd(2) system call])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
#include <sys/eventfd.h>
#include <unistd.h>
],[
int
main (void)
{
eventfd (0, EFD_CLOEXEC);
return 0;
}
])],
[
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_EVENTFD, 1, [we have the eventfd(2) system call])
],
[
AC_MSG_RESULT(no)
])
dnl ****************************************
dnl *** GLib POLL* compatibility defines ***
dnl ****************************************

View File

@ -49,6 +49,9 @@
#ifdef G_OS_UNIX
#include "glib-unix.h"
#ifdef HAVE_EVENTFD
#include <sys/eventfd.h>
#endif
#endif
#include <signal.h>
@ -397,6 +400,14 @@ static GSList *main_contexts_without_pipe = NULL;
#ifndef G_OS_WIN32
#ifdef HAVE_EVENTFD
typedef struct {
guint checked_eventfd : 1;
guint have_eventfd : 1;
} EventFdState;
EventFdState event_fd_state = { 0, 0 };
#endif
/* The UNIX signal pipe contains a single byte specifying which
* signal was received.
*/
@ -529,8 +540,10 @@ g_main_context_unref (GMainContext *context)
if (g_thread_supported())
{
#ifndef G_OS_WIN32
close (context->wake_up_pipe[0]);
close (context->wake_up_pipe[1]);
if (context->wake_up_pipe[0] != -1)
close (context->wake_up_pipe[0]);
if (context->wake_up_pipe[1] != -1)
close (context->wake_up_pipe[1]);
#else
CloseHandle (context->wake_up_semaphore);
#endif
@ -556,8 +569,32 @@ g_main_context_init_pipe (GMainContext *context)
if (context->wake_up_pipe[0] != -1)
return;
#ifdef HAVE_EVENTFD
if (!event_fd_state.checked_eventfd
|| event_fd_state.have_eventfd)
{
int efd;
event_fd_state.checked_eventfd = TRUE;
efd = eventfd (0, EFD_CLOEXEC);
if (efd == -1 && errno == ENOSYS)
{
event_fd_state.have_eventfd = FALSE;
if (!g_unix_open_pipe (context->wake_up_pipe, FD_CLOEXEC, &error))
g_error ("Cannot create pipe main loop wake-up: %s", error->message);
}
else if (efd >= 0)
{
event_fd_state.have_eventfd = TRUE;
context->wake_up_pipe[0] = efd;
}
else
g_error ("Cannot create eventfd for main loop wake-up: %s", g_strerror (errno));
}
#else
if (!g_unix_open_pipe (context->wake_up_pipe, FD_CLOEXEC, &error))
g_error ("Cannot create pipe main loop wake-up: %s", error->message);
#endif
context->wake_up_rec.fd = context->wake_up_pipe[0];
context->wake_up_rec.events = G_IO_IN;
@ -580,7 +617,9 @@ g_main_context_init_pipe (GMainContext *context)
void
_g_main_thread_init (void)
{
GSList *curr = main_contexts_without_pipe;
GSList *curr;
curr = main_contexts_without_pipe;
while (curr)
{
g_main_context_init_pipe ((GMainContext *)curr->data);
@ -2942,8 +2981,18 @@ g_main_context_check (GMainContext *context,
if (!context->poll_waiting)
{
#ifndef G_OS_WIN32
gchar a;
read (context->wake_up_pipe[0], &a, 1);
#ifdef HAVE_EVENTFD
if (event_fd_state.have_eventfd)
{
guint64 buf;
read (context->wake_up_pipe[0], &buf, sizeof(guint64));
}
else
#endif
{
gchar a;
read (context->wake_up_pipe[0], &a, 1);
}
#endif
}
else
@ -3794,7 +3843,15 @@ g_main_context_wakeup_unlocked (GMainContext *context)
{
context->poll_waiting = FALSE;
#ifndef G_OS_WIN32
write (context->wake_up_pipe[1], "A", 1);
#ifdef HAVE_EVENTFD
if (event_fd_state.have_eventfd)
{
guint64 buf = 1;
write (context->wake_up_pipe[0], &buf, sizeof(buf));
}
else
#endif
write (context->wake_up_pipe[1], "A", 1);
#else
ReleaseSemaphore (context->wake_up_semaphore, 1, NULL);
#endif