From 0a77fd04b7ee902d3542b12986731a5b4d3e6d51 Mon Sep 17 00:00:00 2001 From: normal Date: Fri, 24 Aug 2018 19:19:01 +0000 Subject: thread_pthread.c: use eventfd instead of pipe on Linux Based on r64478, any regular user creating more than 1024 pipes on Linux will end up with tiny pipes with only a single page capacity. So avoid wasting user resources and use lighter eventfd on Linux. [ruby-core:88563] [Misc #15011] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64527 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- configure.ac | 2 ++ thread.c | 13 ++++++++++--- thread_pthread.c | 50 ++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/configure.ac b/configure.ac index a48eda1532..0cbdc4c7f6 100644 --- a/configure.ac +++ b/configure.ac @@ -983,6 +983,7 @@ AC_CHECK_HEADERS(pwd.h) AC_CHECK_HEADERS(setjmpex.h) AC_CHECK_HEADERS(stdalign.h) AC_CHECK_HEADERS(sys/attr.h) +AC_CHECK_HEADERS(sys/eventfd.h) AC_CHECK_HEADERS(sys/fcntl.h) AC_CHECK_HEADERS(sys/file.h) AC_CHECK_HEADERS(sys/id.h) @@ -1755,6 +1756,7 @@ AC_CHECK_FUNCS(dup) AC_CHECK_FUNCS(dup3) AC_CHECK_FUNCS(eaccess) AC_CHECK_FUNCS(endgrent) +AC_CHECK_FUNCS(eventfd) AC_CHECK_FUNCS(fchmod) AC_CHECK_FUNCS(fchown) AC_CHECK_FUNCS(fcntl) diff --git a/thread.c b/thread.c index 9757f9c0e9..34a8daf589 100644 --- a/thread.c +++ b/thread.c @@ -398,6 +398,10 @@ ubf_sigwait(void *ignore) # define BUSY_WAIT_SIGNALS (0) #endif +#ifndef USE_EVENTFD +# define USE_EVENTFD (0) +#endif + #if THREAD_DEBUG static int debug_mutex_initialized = 1; static rb_nativethread_lock_t debug_mutex; @@ -4271,9 +4275,12 @@ async_bug_fd(const char *mesg, int errno_arg, int fd) static int consume_communication_pipe(int fd) { -#define CCP_READ_BUFF_SIZE 1024 +#if USE_EVENTFD + uint64_t buff[1]; +#else /* buffer can be shared because no one refers to them. */ - static char buff[CCP_READ_BUFF_SIZE]; + static char buff[1024]; +#endif ssize_t result; int ret = FALSE; /* for rb_sigwait_sleep */ @@ -4289,7 +4296,7 @@ consume_communication_pipe(int fd) result = read(fd, buff, sizeof(buff)); if (result > 0) { ret = TRUE; - if (result < (ssize_t)sizeof(buff)) { + if (USE_EVENTFD || result < (ssize_t)sizeof(buff)) { return ret; } } diff --git a/thread_pthread.c b/thread_pthread.c index 64b2684c22..52e2a049f1 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -37,6 +37,13 @@ #include #include +#if defined(HAVE_SYS_EVENTFD_H) && defined(HAVE_EVENTFD) +# define USE_EVENTFD (1) +# include +#else +# define USE_EVENTFD (0) +#endif + #if defined(SIGVTALRM) && !defined(__CYGWIN__) # define USE_UBF_LIST 1 #endif @@ -1386,13 +1393,17 @@ static struct { static void rb_thread_wakeup_timer_thread_fd(int fd) { +#if USE_EVENTFD + const uint64_t buff = 1; +#else + const char buff = '!'; +#endif ssize_t result; /* already opened */ if (fd >= 0) { - static const char buff[1] = {'!'}; retry: - if ((result = write(fd, buff, 1)) <= 0) { + if ((result = write(fd, &buff, sizeof(buff))) <= 0) { int e = errno; switch (e) { case EINTR: goto retry; @@ -1505,8 +1516,8 @@ rb_thread_wakeup_timer_thread(int sig) } } -#define CLOSE_INVALIDATE(expr) \ - close_invalidate(&expr,"close_invalidate: "#expr) +#define CLOSE_INVALIDATE_PAIR(expr) \ + close_invalidate_pair(expr,"close_invalidate: "#expr) static void close_invalidate(int *fdp, const char *msg) { @@ -1518,6 +1529,19 @@ close_invalidate(int *fdp, const char *msg) } } +static void +close_invalidate_pair(int fds[2], const char *msg) +{ + if (USE_EVENTFD && fds[0] == fds[1]) { + close_invalidate(&fds[0], msg); + fds[1] = -1; + } + else { + close_invalidate(&fds[0], msg); + close_invalidate(&fds[1], msg); + } +} + static void set_nonblock(int fd) { @@ -1545,6 +1569,18 @@ setup_communication_pipe_internal(int pipes[2]) return 0; } + /* + * Don't bother with eventfd on ancient Linux 2.6.22..2.6.26 which were + * missing EFD_* flags, they can fall back to pipe + */ +#if USE_EVENTFD && defined(EFD_NONBLOCK) && defined(EFD_CLOEXEC) + pipes[0] = pipes[1] = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC); + if (pipes[0] >= 0) { + rb_update_max_fd(pipes[0]); + return 0; + } +#endif + err = rb_cloexec_pipe(pipes); if (err != 0) { rb_warn("pipe creation failed for timer: %s, scheduling broken", @@ -1612,8 +1648,7 @@ static void ubf_timer_invalidate(void) { #if UBF_TIMER == UBF_TIMER_PTHREAD - CLOSE_INVALIDATE(timer_pthread.low[0]); - CLOSE_INVALIDATE(timer_pthread.low[1]); + CLOSE_INVALIDATE_PAIR(timer_pthread.low); #endif } @@ -1669,8 +1704,7 @@ rb_thread_create_timer_thread(void) rb_pid_t owner = signal_self_pipe.owner_process; if (owner && owner != current) { - CLOSE_INVALIDATE(signal_self_pipe.normal[0]); - CLOSE_INVALIDATE(signal_self_pipe.normal[1]); + CLOSE_INVALIDATE_PAIR(signal_self_pipe.normal); ubf_timer_invalidate(); } -- cgit v1.2.3