summaryrefslogtreecommitdiff
path: root/rts/posix/itimer/Pthread.c
diff options
context:
space:
mode:
authorBen Gamari <bgamari.foss@gmail.com>2016-05-01 13:39:23 +0200
committerBen Gamari <ben@smart-cactus.org>2016-05-01 23:29:49 +0200
commit65e13f66c595ad75bd6e9a55496f1372ead2731d (patch)
tree91155c792ab9eda72f24c712e6c4e2641514451f /rts/posix/itimer/Pthread.c
parent16a51a6c2f265f8670355be03d42b773d93e0684 (diff)
downloadhaskell-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
Diffstat (limited to 'rts/posix/itimer/Pthread.c')
-rw-r--r--rts/posix/itimer/Pthread.c187
1 files changed, 187 insertions, 0 deletions
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;
+}