diff options
author | Ben Gamari <bgamari.foss@gmail.com> | 2016-05-01 13:39:23 +0200 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2016-05-01 23:29:49 +0200 |
commit | 65e13f66c595ad75bd6e9a55496f1372ead2731d (patch) | |
tree | 91155c792ab9eda72f24c712e6c4e2641514451f | |
parent | 16a51a6c2f265f8670355be03d42b773d93e0684 (diff) | |
download | haskell-65e13f66c595ad75bd6e9a55496f1372ead2731d.tar.gz |
rts: Split up Itimer.c
This shouldn't have any functional changes. It merely splits up what are
essentially three distinct codepaths which are melding together with
CPP.
At the moment I merely #include the implementation to use with CPP
although this really feels very yucky.
Reviewers: erikd, austin, simonmar
Reviewed By: simonmar
Subscribers: thomie
Differential Revision: https://phabricator.haskell.org/D2130
-rw-r--r-- | rts/posix/Itimer.c | 305 | ||||
-rw-r--r-- | rts/posix/Signals.c | 29 | ||||
-rw-r--r-- | rts/posix/Signals.h | 4 | ||||
-rw-r--r-- | rts/posix/itimer/Pthread.c | 187 | ||||
-rw-r--r-- | rts/posix/itimer/Setitimer.c | 88 | ||||
-rw-r--r-- | rts/posix/itimer/TimerCreate.c | 93 |
6 files changed, 407 insertions, 299 deletions
diff --git a/rts/posix/Itimer.c b/rts/posix/Itimer.c index 770ff202ea..ee93dd7719 100644 --- a/rts/posix/Itimer.c +++ b/rts/posix/Itimer.c @@ -13,37 +13,13 @@ * Solaris 2.3 only seems to provide support for @CLOCK_REAL@, whereas we're * keen on getting access to @CLOCK_VIRTUAL@. * - * Hence, we use the old-fashioned @setitimer@ that just about everyone seems - * to support. So much for standards. + * Hence, we often use the old-fashioned @setitimer@ that just about everyone + * seems to support. So much for standards. */ #include "PosixSource.h" #include "Rts.h" -#include "Ticker.h" -#include "Itimer.h" -#include "Proftimer.h" -#include "Schedule.h" -#include "Clock.h" - -/* As recommended in the autoconf manual */ -# ifdef TIME_WITH_SYS_TIME -# include <sys/time.h> -# include <time.h> -# else -# ifdef HAVE_SYS_TIME_H -# include <sys/time.h> -# else -# include <time.h> -# endif -# endif - -#ifdef HAVE_SIGNAL_H -# include <signal.h> -#endif - -#include <string.h> - /* * timer_create doesn't exist and setitimer doesn't fire on iOS, so we're using * a pthreads-based implementation. It may be to do with interference with the @@ -60,58 +36,10 @@ * 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> -#endif - -/* - * We use a realtime timer by default. I found this much more - * reliable than a CPU timer: - * - * Experiments with different frequences: using - * CLOCK_REALTIME/CLOCK_MONOTONIC on Linux 2.6.32, - * 1000us has <1% impact on runtime - * 100us has ~2% impact on runtime - * 10us has ~40% impact on runtime - * - * using CLOCK_PROCESS_CPUTIME_ID on Linux 2.6.32, - * I cannot get it to tick faster than 10ms (10000us) - * which isn't great for profiling. - * - * In the threaded RTS, we can't tick in CPU time because the thread - * which has the virtual timer might be idle, so the tick would never - * fire. Therfore we used to tick in realtime in the threaded RTS and - * in CPU time otherwise, but now we always tick in realtime, for - * several reasons: - * - * - resolution (see above) - * - consistency (-threaded is the same as normal) - * - more consistency: Windows only has a realtime timer - * - * Note we want to use CLOCK_MONOTONIC rather than CLOCK_REALTIME, - * because the latter may jump around (NTP adjustments, leap seconds - * etc.). - */ - #if defined(solaris2_HOST_OS) /* USE_TIMER_CREATE is usually disabled for Solaris. In fact it is supported well on this OS, but requires additional privilege. When @@ -130,232 +58,11 @@ ghc-stage2: timer_create: Not owner #undef USE_TIMER_CREATE #endif /* solaris2_HOST_OS */ -#if defined(USE_TIMER_CREATE) -# define ITIMER_SIGNAL SIGVTALRM -#elif defined(HAVE_SETITIMER) -# define ITIMER_SIGNAL SIGALRM - // Using SIGALRM can leads to problems, see #850. But we have no - // option if timer_create() is not available. -#else -# error No way to set an interval timer. -#endif - -#if defined(USE_TIMER_CREATE) -static timer_t timer; -#endif - -static Time itimer_interval = DEFAULT_TICK_INTERVAL; - -#if !defined(USE_PTHREAD_FOR_ITIMER) -static void install_vtalrm_handler(TickProc handle_tick) -{ - struct sigaction action; - - action.sa_handler = handle_tick; - - sigemptyset(&action.sa_mask); - -#ifdef SA_RESTART - // specify SA_RESTART. One consequence if we don't do this is - // that readline gets confused by the -threaded RTS. It seems - // that if a SIGALRM handler is installed without SA_RESTART, - // readline installs its own SIGALRM signal handler (see - // readline's signals.c), and this somehow causes readline to go - // wrong when the input exceeds a single line (try it). - action.sa_flags = SA_RESTART; -#else - action.sa_flags = 0; -#endif - - if (sigaction(ITIMER_SIGNAL, &action, NULL) == -1) { - sysErrorBelch("sigaction"); - stg_exit(EXIT_FAILURE); - } -} -#endif - +// Select the variant to use #if defined(USE_PTHREAD_FOR_ITIMER) -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); - } - int ret = timerfd_settime(timerfd, 0, &it, NULL); -#endif - - while (1) { - if (USE_TIMERFD_FOR_ITIMER) { - if (read(timerfd, &nticks, sizeof(nticks)) != sizeof(nticks)) { - if (errno != EINTR) { - sysErrorBelch("Itimer: read(timerfd) failed"); - } - } - } else { - if (usleep(TimeToUS(itimer_interval)) != 0 && errno != EINTR) { - sysErrorBelch("usleep(TimeToUS(itimer_interval) failed"); - } - } - 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; // Never reached. -} -#endif - -void -initTicker (Time interval, TickProc handle_tick) -{ - itimer_interval = interval; - -#if defined(USE_PTHREAD_FOR_ITIMER) - pthread_t tid; - 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; - - // Keep programs like valgrind happy - memset(&ev, 0, sizeof(ev)); - - ev.sigev_notify = SIGEV_SIGNAL; - ev.sigev_signo = ITIMER_SIGNAL; - - if (timer_create(CLOCK_ID, &ev, &timer) != 0) { - sysErrorBelch("timer_create"); - stg_exit(EXIT_FAILURE); - } - } - install_vtalrm_handler(handle_tick); -#else - install_vtalrm_handler(handle_tick); -#endif -} - -void -startTicker(void) -{ -#if defined(USE_PTHREAD_FOR_ITIMER) - // sanity check - if (itimer_state == EXITED) { - sysErrorBelch("ITimer: Tried to start a dead timer!\n"); - stg_exit(EXIT_FAILURE); - } - itimer_state = RUNNING; +#include "itimer/Pthread.c" #elif defined(USE_TIMER_CREATE) - { - 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; - - if (timer_settime(timer, 0, &it, NULL) != 0) { - sysErrorBelch("timer_settime"); - stg_exit(EXIT_FAILURE); - } - } -#else - { - struct itimerval it; - - it.it_value.tv_sec = TimeToSeconds(itimer_interval); - it.it_value.tv_usec = TimeToUS(itimer_interval) % 1000000; - it.it_interval = it.it_value; - - if (setitimer(ITIMER_REAL, &it, NULL) != 0) { - sysErrorBelch("setitimer"); - stg_exit(EXIT_FAILURE); - } - } -#endif -} - -void -stopTicker(void) -{ -#if defined(USE_PTHREAD_FOR_ITIMER) - if (itimer_state == RUNNING) { - itimer_state = STOPPING; - // Note that the timer may fire once more, but that's okay; - // handle_tick is only called when itimer_state == RUNNING - } -#elif defined(USE_TIMER_CREATE) - struct itimerspec it; - - it.it_value.tv_sec = 0; - it.it_value.tv_nsec = 0; - it.it_interval = it.it_value; - - if (timer_settime(timer, 0, &it, NULL) != 0) { - sysErrorBelch("timer_settime"); - stg_exit(EXIT_FAILURE); - } +#include "itimer/TimerCreate.c" #else - struct itimerval it; - - it.it_value.tv_sec = 0; - it.it_value.tv_usec = 0; - it.it_interval = it.it_value; - - if (setitimer(ITIMER_REAL, &it, NULL) != 0) { - sysErrorBelch("setitimer"); - stg_exit(EXIT_FAILURE); - } +#include "itimer/Setitimer.c" #endif -} - -void -exitTicker (rtsBool wait STG_UNUSED) -{ -#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); - timer_delete(timer); - // ignore errors - we don't really care if it fails. -#endif -} - -int -rtsTimerSignal(void) -{ - return ITIMER_SIGNAL; -} diff --git a/rts/posix/Signals.c b/rts/posix/Signals.c index bfc7e82e15..7fb854b894 100644 --- a/rts/posix/Signals.c +++ b/rts/posix/Signals.c @@ -14,6 +14,7 @@ #include "Signals.h" #include "RtsUtils.h" #include "Prelude.h" +#include "Ticker.h" #include "Stable.h" #include "Libdw.h" @@ -626,6 +627,34 @@ set_sigtstp_action (rtsBool handle) } } +/* Used by ItimerTimerCreate and ItimerSetitimer implementations */ +void +install_vtalrm_handler(int sig, TickProc handle_tick) +{ + struct sigaction action; + + action.sa_handler = handle_tick; + + sigemptyset(&action.sa_mask); + +#ifdef SA_RESTART + // specify SA_RESTART. One consequence if we don't do this is + // that readline gets confused by the -threaded RTS. It seems + // that if a SIGALRM handler is installed without SA_RESTART, + // readline installs its own SIGALRM signal handler (see + // readline's signals.c), and this somehow causes readline to go + // wrong when the input exceeds a single line (try it). + action.sa_flags = SA_RESTART; +#else + action.sa_flags = 0; +#endif + + if (sigaction(sig, &action, NULL) == -1) { + sysErrorBelch("sigaction"); + stg_exit(EXIT_FAILURE); + } +} + /* ----------------------------------------------------------------------------- * Install default signal handlers. * diff --git a/rts/posix/Signals.h b/rts/posix/Signals.h index 3100d39537..bb9a7b58df 100644 --- a/rts/posix/Signals.h +++ b/rts/posix/Signals.h @@ -13,6 +13,8 @@ # include <signal.h> #endif +#include "Ticker.h" + #include "BeginPrivate.h" rtsBool anyUserHandlers(void); @@ -24,6 +26,8 @@ extern siginfo_t *next_pending_handler; void startSignalHandlers(Capability *cap); #endif +void install_vtalrm_handler(int sig, TickProc handle_tick); + void ioManagerStartCap (/* inout */ Capability **cap); extern StgInt *signal_handlers; diff --git a/rts/posix/itimer/Pthread.c b/rts/posix/itimer/Pthread.c new file mode 100644 index 0000000000..e84a53a058 --- /dev/null +++ b/rts/posix/itimer/Pthread.c @@ -0,0 +1,187 @@ +/* ----------------------------------------------------------------------------- + * + * (c) The GHC Team, 1995-2007 + * + * Interval timer for profiling and pre-emptive scheduling. + * + * ---------------------------------------------------------------------------*/ + +/* + * We use a realtime timer by default. I found this much more + * reliable than a CPU timer: + * + * Experiments with different frequences: using + * CLOCK_REALTIME/CLOCK_MONOTONIC on Linux 2.6.32, + * 1000us has <1% impact on runtime + * 100us has ~2% impact on runtime + * 10us has ~40% impact on runtime + * + * using CLOCK_PROCESS_CPUTIME_ID on Linux 2.6.32, + * I cannot get it to tick faster than 10ms (10000us) + * which isn't great for profiling. + * + * In the threaded RTS, we can't tick in CPU time because the thread + * which has the virtual timer might be idle, so the tick would never + * fire. Therfore we used to tick in realtime in the threaded RTS and + * in CPU time otherwise, but now we always tick in realtime, for + * several reasons: + * + * - resolution (see above) + * - consistency (-threaded is the same as normal) + * - more consistency: Windows only has a realtime timer + * + * Note we want to use CLOCK_MONOTONIC rather than CLOCK_REALTIME, + * because the latter may jump around (NTP adjustments, leap seconds + * etc.). + */ + +#include "PosixSource.h" +#include "Rts.h" + +#include "Ticker.h" +#include "posix/Itimer.h" +#include "Proftimer.h" +#include "Schedule.h" +#include "posix/Clock.h" + +/* As recommended in the autoconf manual */ +# ifdef TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +# else +# ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +# endif + +#ifdef HAVE_SIGNAL_H +# include <signal.h> +#endif + +#include <string.h> + +#include <pthread.h> +#include <unistd.h> +#include <fcntl.h> + +#if HAVE_SYS_TIMERFD_H +#include <sys/timerfd.h> +#define USE_TIMERFD_FOR_ITIMER 1 +#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 + +static Time itimer_interval = DEFAULT_TICK_INTERVAL; +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); + } + int ret = timerfd_settime(timerfd, 0, &it, NULL); +#endif + + while (1) { + if (USE_TIMERFD_FOR_ITIMER) { + if (read(timerfd, &nticks, sizeof(nticks)) != sizeof(nticks)) { + if (errno != EINTR) { + sysErrorBelch("Itimer: read(timerfd) failed"); + } + } + } else { + if (usleep(TimeToUS(itimer_interval)) != 0 && errno != EINTR) { + sysErrorBelch("usleep(TimeToUS(itimer_interval) failed"); + } + } + 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; // Never reached. +} + +void +initTicker (Time interval, TickProc handle_tick) +{ + itimer_interval = interval; + + pthread_t tid; + 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 + } +} + +void +startTicker(void) +{ + // sanity check + if (itimer_state == EXITED) { + sysErrorBelch("ITimer: Tried to start a dead timer!\n"); + stg_exit(EXIT_FAILURE); + } + itimer_state = RUNNING; +} + +void +stopTicker(void) +{ + if (itimer_state == RUNNING) { + itimer_state = STOPPING; + // Note that the timer may fire once more, but that's okay; + // handle_tick is only called when itimer_state == RUNNING + } +} + +void +exitTicker (rtsBool wait STG_UNUSED) +{ + itimer_state = EXITED; +} + +int +rtsTimerSignal(void) +{ + return SIGALRM; +} diff --git a/rts/posix/itimer/Setitimer.c b/rts/posix/itimer/Setitimer.c new file mode 100644 index 0000000000..c44e8d8c22 --- /dev/null +++ b/rts/posix/itimer/Setitimer.c @@ -0,0 +1,88 @@ +/* ----------------------------------------------------------------------------- + * + * (c) The GHC Team, 1995-2007 + * + * Interval timer for profiling and pre-emptive scheduling. + * + * ---------------------------------------------------------------------------*/ + +#include "PosixSource.h" +#include "Rts.h" + +#include "Ticker.h" +#include "posix/Itimer.h" +#include "Proftimer.h" +#include "Schedule.h" +#include "posix/Clock.h" +#include "posix/Signals.h" + +/* As recommended in the autoconf manual */ +# ifdef TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +# else +# ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +# endif + +#ifdef HAVE_SIGNAL_H +# include <signal.h> +#endif + +#include <string.h> + +static Time itimer_interval = DEFAULT_TICK_INTERVAL; + +void +initTicker (Time interval, TickProc handle_tick) +{ + itimer_interval = interval; + install_vtalrm_handler(SIGALRM, handle_tick); +} + +void +startTicker(void) +{ + struct itimerval it; + + it.it_value.tv_sec = TimeToSeconds(itimer_interval); + it.it_value.tv_usec = TimeToUS(itimer_interval) % 1000000; + it.it_interval = it.it_value; + + if (setitimer(ITIMER_REAL, &it, NULL) != 0) { + sysErrorBelch("setitimer"); + stg_exit(EXIT_FAILURE); + } +} + +void +stopTicker(void) +{ + struct itimerval it; + + it.it_value.tv_sec = 0; + it.it_value.tv_usec = 0; + it.it_interval = it.it_value; + + if (setitimer(ITIMER_REAL, &it, NULL) != 0) { + sysErrorBelch("setitimer"); + stg_exit(EXIT_FAILURE); + } +} + +void +exitTicker (rtsBool wait STG_UNUSED) +{ + return; +} + +int +rtsTimerSignal(void) +{ + return SIGALRM; + // Using SIGALRM can leads to problems, see #850. But we have no + // option if timer_create() is not available. +} diff --git a/rts/posix/itimer/TimerCreate.c b/rts/posix/itimer/TimerCreate.c new file mode 100644 index 0000000000..a4fb2b5b58 --- /dev/null +++ b/rts/posix/itimer/TimerCreate.c @@ -0,0 +1,93 @@ +/* ----------------------------------------------------------------------------- + * + * (c) The GHC Team, 1995-2007 + * + * Interval timer for profiling and pre-emptive scheduling. + * + * ---------------------------------------------------------------------------*/ + +#include "PosixSource.h" +#include "Rts.h" + +#include "Ticker.h" +#include "posix/Itimer.h" +#include "Proftimer.h" +#include "Schedule.h" +#include "posix/Clock.h" +#include "posix/Signals.h" + +#ifdef HAVE_SIGNAL_H +# include <signal.h> +#endif + +#include <string.h> + +static Time itimer_interval = DEFAULT_TICK_INTERVAL; +static timer_t timer; + +void +initTicker (Time interval, TickProc handle_tick) +{ + itimer_interval = interval; + + struct sigevent ev; + + // Keep programs like valgrind happy + memset(&ev, 0, sizeof(ev)); + + ev.sigev_notify = SIGEV_SIGNAL; + ev.sigev_signo = SIGVTALRM; + + if (timer_create(CLOCK_ID, &ev, &timer) != 0) { + sysErrorBelch("timer_create"); + stg_exit(EXIT_FAILURE); + } + + install_vtalrm_handler(SIGVTALRM, handle_tick); +} + +void +startTicker(void) +{ + 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; + + if (timer_settime(timer, 0, &it, NULL) != 0) { + sysErrorBelch("timer_settime"); + stg_exit(EXIT_FAILURE); + } +} + +void +stopTicker(void) +{ + struct itimerspec it; + + it.it_value.tv_sec = 0; + it.it_value.tv_nsec = 0; + it.it_interval = it.it_value; + + if (timer_settime(timer, 0, &it, NULL) != 0) { + sysErrorBelch("timer_settime"); + stg_exit(EXIT_FAILURE); + } +} + +void +exitTicker (rtsBool wait STG_UNUSED) +{ + // Before deleting the timer set the signal to ignore to avoid the + // possibility of the signal being delivered after the timer is deleted. + signal(SIGVTALRM, SIG_IGN); + timer_delete(timer); + // ignore errors - we don't really care if it fails. +} + +int +rtsTimerSignal(void) +{ + return SIGVTALRM; +} |