[commit: ghc] master: rts/timer: use timerfd_* on Linux instead of alarm signals (120b9cd)
git at git.haskell.org
git at git.haskell.org
Sat Mar 5 20:15:23 UTC 2016
Repository : ssh://git@git.haskell.org/ghc
On branch : master
Link : http://ghc.haskell.org/trac/ghc/changeset/120b9cdb31878ecee442c0a4bb9532a9d30c0c64/ghc
>---------------------------------------------------------------
commit 120b9cdb31878ecee442c0a4bb9532a9d30c0c64
Author: Sylvain HENRY <hsyl20 at gmail.com>
Date: Sat Mar 5 20:00:06 2016 +0100
rts/timer: use timerfd_* on Linux instead of alarm signals
Reviewers: erikd, simonmar, austin, bgamari
Reviewed By: simonmar, bgamari
Subscribers: hvr, thomie
Differential Revision: https://phabricator.haskell.org/D1947
GHC Trac Issues: #10840
>---------------------------------------------------------------
120b9cdb31878ecee442c0a4bb9532a9d30c0c64
configure.ac | 2 +-
rts/posix/Itimer.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 81 insertions(+), 12 deletions(-)
diff --git a/configure.ac b/configure.ac
index cc162e7..adc22dd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -766,7 +766,7 @@ dnl off_t, because it will affect the result of that test.
AC_SYS_LARGEFILE
dnl ** check for specific header (.h) files that we are interested in
-AC_CHECK_HEADERS([ctype.h dirent.h dlfcn.h errno.h fcntl.h grp.h limits.h locale.h nlist.h pthread.h pwd.h signal.h sys/param.h sys/mman.h sys/resource.h sys/select.h sys/time.h sys/timeb.h sys/timers.h sys/times.h sys/utsname.h sys/wait.h termios.h time.h utime.h windows.h winsock.h sched.h])
+AC_CHECK_HEADERS([ctype.h dirent.h dlfcn.h errno.h fcntl.h grp.h limits.h locale.h nlist.h pthread.h pwd.h signal.h sys/param.h sys/mman.h sys/resource.h sys/select.h sys/time.h sys/timeb.h sys/timerfd.h sys/timers.h sys/times.h sys/utsname.h sys/wait.h termios.h time.h utime.h windows.h winsock.h sched.h])
dnl sys/cpuset.h needs sys/param.h to be included first on FreeBSD 9.1; #7708
AC_CHECK_HEADERS([sys/cpuset.h], [], [],
diff --git a/rts/posix/Itimer.c b/rts/posix/Itimer.c
index 57c7741..f6c00a6 100644
--- a/rts/posix/Itimer.c
+++ b/rts/posix/Itimer.c
@@ -53,6 +53,31 @@
#define USE_PTHREAD_FOR_ITIMER
#endif
+/*
+ * On Linux in the threaded RTS we can use timerfd_* (introduced in Linux
+ * 2.6.25) and a thread instead of alarm signals. It avoids the risk of
+ * interrupting syscalls (see #10840) and the risk of being accidentally
+ * modified in user code using signals.
+ */
+#if defined(linux_HOST_OS) && defined(THREADED_RTS) && HAVE_SYS_TIMERFD_H
+#include <sys/timerfd.h>
+#include <fcntl.h>
+#define USE_PTHREAD_FOR_ITIMER
+#define USE_TIMERFD_FOR_ITIMER 1
+#undef USE_TIMER_CREATE
+#else
+#define USE_TIMERFD_FOR_ITIMER 0
+#endif
+
+/*
+ * TFD_CLOEXEC has been added in Linux 2.6.26.
+ * If it is not available, we use fcntl(F_SETFD).
+ */
+#ifndef TFD_CLOEXEC
+#define TFD_CLOEXEC 0
+#endif
+
+
#if defined(USE_PTHREAD_FOR_ITIMER)
#include <pthread.h>
#include <unistd.h>
@@ -150,15 +175,50 @@ static void install_vtalrm_handler(TickProc handle_tick)
#endif
#if defined(USE_PTHREAD_FOR_ITIMER)
-static volatile int itimer_enabled;
+enum ItimerState {STOPPED, RUNNING, STOPPING, EXITED};
+static volatile enum ItimerState itimer_state = STOPPED;
static void *itimer_thread_func(void *_handle_tick)
{
TickProc handle_tick = _handle_tick;
+ uint64_t nticks;
+ int timerfd = -1;
+
+#if USE_TIMERFD_FOR_ITIMER
+ struct itimerspec it;
+ it.it_value.tv_sec = TimeToSeconds(itimer_interval);
+ it.it_value.tv_nsec = TimeToNS(itimer_interval) % 1000000000;
+ it.it_interval = it.it_value;
+
+ timerfd = timerfd_create(CLOCK_MONOTONIC,TFD_CLOEXEC);
+ if (timerfd == -1) {
+ sysErrorBelch("timerfd_create");
+ stg_exit(EXIT_FAILURE);
+ }
+ if (!TFD_CLOEXEC) {
+ fcntl(timerfd, F_SETFD, FD_CLOEXEC);
+ }
+ timerfd_settime(timerfd,0,&it,NULL);
+#endif
+
while (1) {
- usleep(TimeToUS(itimer_interval));
- switch (itimer_enabled) {
- case 1: handle_tick(0); break;
- case 2: itimer_enabled = 0;
+ if (USE_TIMERFD_FOR_ITIMER) {
+ read(timerfd, &nticks, sizeof(nticks));
+ } else {
+ usleep(TimeToUS(itimer_interval));
+ }
+ switch (itimer_state) {
+ case RUNNING:
+ handle_tick(0);
+ break;
+ case STOPPED:
+ break;
+ case STOPPING:
+ itimer_state = STOPPED;
+ break;
+ case EXITED:
+ if (USE_TIMERFD_FOR_ITIMER)
+ close(timerfd);
+ return NULL;
}
}
return NULL;
@@ -172,7 +232,13 @@ initTicker (Time interval, TickProc handle_tick)
#if defined(USE_PTHREAD_FOR_ITIMER)
pthread_t tid;
- pthread_create(&tid, NULL, itimer_thread_func, (void*)handle_tick);
+ int r = pthread_create(&tid, NULL, itimer_thread_func, (void*)handle_tick);
+ if (!r) {
+ pthread_detach(tid);
+#if HAVE_PTHREAD_SETNAME_NP
+ pthread_setname_np(tid, "ghc_ticker");
+#endif
+ }
#elif defined(USE_TIMER_CREATE)
{
struct sigevent ev;
@@ -198,7 +264,7 @@ void
startTicker(void)
{
#if defined(USE_PTHREAD_FOR_ITIMER)
- itimer_enabled = 1;
+ itimer_state = RUNNING;
#elif defined(USE_TIMER_CREATE)
{
struct itimerspec it;
@@ -232,10 +298,11 @@ void
stopTicker(void)
{
#if defined(USE_PTHREAD_FOR_ITIMER)
- if (itimer_enabled == 1) {
- itimer_enabled = 2;
+ if (itimer_state == RUNNING) {
+ itimer_state = STOPPING;
/* Wait for the thread to confirm it won't generate another tick. */
- while (itimer_enabled != 0)
+ write_barrier();
+ while (itimer_state != STOPPED)
sched_yield();
}
#elif defined(USE_TIMER_CREATE)
@@ -266,7 +333,9 @@ stopTicker(void)
void
exitTicker (rtsBool wait STG_UNUSED)
{
-#if defined(USE_TIMER_CREATE)
+#if defined(USE_PTHREAD_FOR_ITIMER)
+ itimer_state = EXITED;
+#elif defined(USE_TIMER_CREATE)
// Before deleting the timer set the signal to ignore to avoid the
// possibility of the signal being delivered after the timer is deleted.
signal(ITIMER_SIGNAL, SIG_IGN);
More information about the ghc-commits
mailing list