Commit 120b9cdb authored by Sylvain HENRY's avatar Sylvain HENRY Committed by Ben Gamari

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
parent 1f3d953e
......@@ -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], [], [],
......
......@@ -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);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment