summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSylvain Henry <sylvain@haskus.fr>2021-11-04 16:30:33 +0100
committerBen Gamari <ben@smart-cactus.org>2022-02-17 11:27:16 -0500
commitdbccfb4cde8386bb54777b2e37e2e194952bf6bb (patch)
tree3c5b05862f87240bf57241b3c6e4877e3532e3a8
parent2fce808996ecabb1c14369d950451a74f02de8ce (diff)
downloadhaskell-dbccfb4cde8386bb54777b2e37e2e194952bf6bb.tar.gz
RTS: open timerfd synchronously (#20618)
(cherry picked from commit 2929850f0139778c579fc7144831c88e11353a9b)
-rw-r--r--rts/posix/itimer/Pthread.c51
1 files changed, 32 insertions, 19 deletions
diff --git a/rts/posix/itimer/Pthread.c b/rts/posix/itimer/Pthread.c
index 71a8622181..9fb4fef3c9 100644
--- a/rts/posix/itimer/Pthread.c
+++ b/rts/posix/itimer/Pthread.c
@@ -99,29 +99,13 @@ static Condition start_cond;
static Mutex mutex;
static OSThreadId thread;
+// file descriptor for the timer (Linux only)
+static int timerfd = -1;
+
static void *itimer_thread_func(void *_handle_tick)
{
TickProc handle_tick = _handle_tick;
uint64_t nticks;
- int timerfd = -1;
-
-#if defined(USE_TIMERFD_FOR_ITIMER) && 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) {
- barf("timerfd_create: %s", strerror(errno));
- }
- if (!TFD_CLOEXEC) {
- fcntl(timerfd, F_SETFD, FD_CLOEXEC);
- }
- if (timerfd_settime(timerfd, 0, &it, NULL)) {
- barf("timerfd_settime: %s", strerror(errno));
- }
-#endif
// Relaxed is sufficient: If we don't see that exited was set in one iteration we will
// see it next time.
@@ -180,6 +164,35 @@ initTicker (Time interval, TickProc handle_tick)
initCondition(&start_cond);
initMutex(&mutex);
+ /* Open the file descriptor for the timer synchronously.
+ *
+ * We used to do it in itimer_thread_func (i.e. in the timer thread) but it
+ * meant that some user code could run before it and get confused by the
+ * allocation of the timerfd.
+ *
+ * See hClose002 which unsafely closes a file descriptor twice expecting an
+ * exception the second time: it sometimes failed when the second call to
+ * "close" closed our own timerfd which inadvertently reused the same file
+ * descriptor closed by the first call! (see #20618)
+ */
+#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) {
+ barf("timerfd_create: %s", strerror(errno));
+ }
+ if (!TFD_CLOEXEC) {
+ fcntl(timerfd, F_SETFD, FD_CLOEXEC);
+ }
+ if (timerfd_settime(timerfd, 0, &it, NULL)) {
+ barf("timerfd_settime: %s", strerror(errno));
+ }
+#endif
+
/*
* We can't use the RTS's createOSThread here as we need to remain attached
* to the thread we create so we can later join to it if requested