[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 4 commits: Enable tables next to code for LoongArch64

Marge Bot (@marge-bot) gitlab at gitlab.haskell.org
Fri Feb 3 04:43:05 UTC 2023



Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC


Commits:
b6d40c6d by lrzlin at 2023-02-02T23:42:46-05:00
Enable tables next to code for LoongArch64

- - - - -
c46cff0c by Wander Hillen at 2023-02-02T23:42:48-05:00
Move pthread and timerfd ticker implementations to separate files

- - - - -
3b2bbe50 by Ben Gamari at 2023-02-02T23:42:49-05:00
base: Fix Note references in GHC.IO.Handle.Types

- - - - -
9ad7c495 by Bodigrim at 2023-02-02T23:42:50-05:00
Bump submodule containers to 0.6.7

Metric Decrease:
    ManyConstructors
    T10421
    T12425
    T12707
    T13035
    T13379
    T15164
    T1969
    T783
    T9198
    T9961
    WWRec

- - - - -


9 changed files:

- compiler/GHC/CmmToLlvm/Mangler.hs
- compiler/ghc.cabal.in
- libraries/base/GHC/IO/Handle/Types.hs
- libraries/containers
- libraries/ghci/GHCi/InfoTable.hsc
- m4/ghc_tables_next_to_code.m4
- rts/posix/Ticker.c
- rts/posix/ticker/Pthread.c
- + rts/posix/ticker/TimerFd.c


Changes:

=====================================
compiler/GHC/CmmToLlvm/Mangler.hs
=====================================
@@ -38,7 +38,7 @@ llvmFixupAsm platform f1 f2 = {-# SCC "llvm_mangler" #-}
 
 -- | These are the rewrites that the mangler will perform
 rewrites :: [Rewrite]
-rewrites = [rewriteSymType, rewriteAVX, rewriteCall]
+rewrites = [rewriteSymType, rewriteAVX, rewriteCall, rewriteJump]
 
 type Rewrite = Platform -> B.ByteString -> Maybe B.ByteString
 
@@ -123,6 +123,29 @@ rewriteCall platform l
         removePlt = replaceOnce (B.pack "@plt") (B.pack "")
         appendInsn i = (`B.append` B.pack ("\n\t" ++ i))
 
+-- | This rewrites bl and b jump inst to avoid creating PLT entries for
+-- functions on loongarch64, because there is no separate call instruction
+-- for function calls in loongarch64. Also, this replacement will load
+-- the function address from the GOT, which is resolved to point to the
+-- real address of the function.
+rewriteJump :: Rewrite
+rewriteJump platform l
+  | not isLoongArch64 = Nothing
+  | isBL l            = Just $ replaceJump "bl" "$ra" "$ra" l
+  | isB l             = Just $ replaceJump "b" "$zero" "$t0" l
+  | otherwise         = Nothing
+  where
+    isLoongArch64 = platformArch platform == ArchLoongArch64
+    isBL = B.isPrefixOf (B.pack "bl\t")
+    isB = B.isPrefixOf (B.pack "b\t")
+
+    replaceJump jump rd rj l =
+        appendInsn ("jirl" ++ "\t" ++ rd ++ ", " ++ rj ++ ", 0") $ removeBracket $
+        replaceOnce (B.pack (jump ++ "\t%plt(")) (B.pack ("la\t" ++ rj ++ ", ")) l
+      where
+        removeBracket = replaceOnce (B.pack ")") (B.pack "")
+        appendInsn i = (`B.append` B.pack ("\n\t" ++ i))
+
 -- | @replaceOnce match replace bs@ replaces the first occurrence of the
 -- substring @match@ in @bs@ with @replace at .
 replaceOnce :: B.ByteString -> B.ByteString -> B.ByteString -> B.ByteString


=====================================
compiler/ghc.cabal.in
=====================================
@@ -557,6 +557,7 @@ Library
         GHC.Platform.ARM
         GHC.Platform.AArch64
         GHC.Platform.Constants
+        GHC.Platform.LoongArch64
         GHC.Platform.NoRegs
         GHC.Platform.PPC
         GHC.Platform.Profile
@@ -564,7 +565,6 @@ Library
         GHC.Platform.Reg.Class
         GHC.Platform.Regs
         GHC.Platform.RISCV64
-        GHC.Platform.LoongArch64
         GHC.Platform.S390X
         GHC.Platform.Wasm32
         GHC.Platform.Ways


=====================================
libraries/base/GHC/IO/Handle/Types.hs
=====================================
@@ -124,11 +124,11 @@ data Handle__
     Handle__ {
       haDevice      :: !dev,
       haType        :: HandleType,           -- type (read/write/append etc.)
-      haByteBuffer  :: !(IORef (Buffer Word8)), -- See [note Buffering Implementation]
+      haByteBuffer  :: !(IORef (Buffer Word8)), -- See Note [Buffering Implementation]
       haBufferMode  :: BufferMode,
       haLastDecode  :: !(IORef (dec_state, Buffer Word8)),
       -- ^ The byte buffer just  before we did our last batch of decoding.
-      haCharBuffer  :: !(IORef (Buffer CharBufElem)), -- See [note Buffering Implementation]
+      haCharBuffer  :: !(IORef (Buffer CharBufElem)), -- See Note [Buffering Implementation]
       haBuffers     :: !(IORef (BufferList CharBufElem)),  -- spare buffers
       haEncoder     :: Maybe (TextEncoder enc_state),
       haDecoder     :: Maybe (TextDecoder dec_state),
@@ -261,13 +261,13 @@ data BufferMode
             )
 
 {-
-[note Buffering Implementation]
-
+Note [Buffering Implementation]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Each Handle has two buffers: a byte buffer (haByteBuffer) and a Char
 buffer (haCharBuffer).
 
-[note Buffered Reading]
-
+Note [Buffered Reading]
+~~~~~~~~~~~~~~~~~~~~~~~
 For read Handles, bytes are read into the byte buffer, and immediately
 decoded into the Char buffer (see
 GHC.IO.Handle.Internals.readTextDevice).  The only way there might be
@@ -279,8 +279,8 @@ reading data into a Handle.  When reading, we can always just read all
 the data there is available without blocking, decode it into the Char
 buffer, and then provide it immediately to the caller.
 
-[note Buffered Writing]
-
+Note [Buffered Writing]
+~~~~~~~~~~~~~~~~~~~~~~~
 Characters are written into the Char buffer by e.g. hPutStr.  At the
 end of the operation, or when the char buffer is full, the buffer is
 decoded to the byte buffer (see writeCharBuffer).  This is so that we
@@ -288,8 +288,8 @@ can detect encoding errors at the right point.
 
 Hence, the Char buffer is always empty between Handle operations.
 
-[note Buffer Sizing]
-
+Note [Buffer Sizing]
+~~~~~~~~~~~~~~~~~~~~
 The char buffer is always a default size (dEFAULT_CHAR_BUFFER_SIZE).
 The byte buffer size is chosen by the underlying device (via its
 IODevice.newBuffer).  Hence the size of these buffers is not under
@@ -322,8 +322,8 @@ writeCharBuffer, which checks whether the buffer should be flushed
 according to the current buffering mode.  Additionally, we look for
 newlines and flush if the mode is LineBuffering.
 
-[note Buffer Flushing]
-
+Note [Buffer Flushing]
+~~~~~~~~~~~~~~~~~~~~~~
 ** Flushing the Char buffer
 
 We must be able to flush the Char buffer, in order to implement


=====================================
libraries/containers
=====================================
@@ -1 +1 @@
-Subproject commit fbafcf704f0febd9ddac84dbe00ae3787af43550
+Subproject commit 9f4a93604c66a5e605ce46fc30003b71802b3cfd


=====================================
libraries/ghci/GHCi/InfoTable.hsc
=====================================
@@ -228,6 +228,15 @@ mkJumpToAddr a = case hostPlatformArch of
                  , fromIntegral w64
                  , fromIntegral (w64 `shiftR` 32) ]
 
+    ArchLoongArch64 -> pure $
+        let w64 = fromIntegral (funPtrToInt a) :: Word64
+        in Right [ 0x1c00000c          -- pcaddu12i $t0,0
+                 , 0x28c0418c          -- ld.d      $t0,$t0,16
+                 , 0x4c000180          -- jr        $t0
+                 , 0x03400000          -- nop
+                 , fromIntegral w64
+                 , fromIntegral (w64 `shiftR` 32) ]
+
     arch ->
       -- The arch isn't supported. You either need to add your architecture as a
       -- distinct case, or use non-TABLES_NEXT_TO_CODE mode.


=====================================
m4/ghc_tables_next_to_code.m4
=====================================
@@ -17,7 +17,7 @@ AC_DEFUN([GHC_TABLES_NEXT_TO_CODE],
   case "$Unregisterised" in
       NO)
           case "$TargetArch" in
-              ia64|powerpc64|powerpc64le|s390x|wasm32|loongarch64)
+              ia64|powerpc64|powerpc64le|s390x|wasm32)
                   TablesNextToCodeDefault=NO
                   AC_MSG_RESULT([no])
                   ;;


=====================================
rts/posix/Ticker.c
=====================================
@@ -65,13 +65,17 @@
  * On Linux 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.
+ * modified in user code using signals. NetBSD has also added timerfd
+ * support since version 10.
+ *
+ * For older version of linux/netbsd without timerfd we fall back to the
+ * pthread based implementation.
  */
-#if defined(linux_HOST_OS) && HAVE_SYS_TIMERFD_H
-#define USE_PTHREAD_FOR_ITIMER
+#if HAVE_SYS_TIMERFD_H
+#define USE_TIMERFD_FOR_ITIMER
 #endif
 
-#if defined(freebsd_HOST_OS)
+#if defined(linux_HOST_OS)
 #define USE_PTHREAD_FOR_ITIMER
 #endif
 
@@ -79,6 +83,10 @@
 #define USE_PTHREAD_FOR_ITIMER
 #endif
 
+#if defined(freebsd_HOST_OS)
+#define USE_PTHREAD_FOR_ITIMER
+#endif
+
 #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
@@ -98,7 +106,9 @@ ghc-stage2: timer_create: Not owner
 #endif /* solaris2_HOST_OS */
 
 // Select the variant to use
-#if defined(USE_PTHREAD_FOR_ITIMER)
+#if defined(USE_TIMERFD_FOR_ITIMER)
+#include "ticker/TimerFd.c"
+#elif defined(USE_PTHREAD_FOR_ITIMER)
 #include "ticker/Pthread.c"
 #elif defined(USE_TIMER_CREATE)
 #include "ticker/TimerCreate.c"


=====================================
rts/posix/ticker/Pthread.c
=====================================
@@ -63,13 +63,6 @@
 #include <unistd.h>
 #include <fcntl.h>
 
-#if defined(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).
@@ -93,61 +86,16 @@ static Condition start_cond;
 static Mutex mutex;
 static OSThreadId thread;
 
-// file descriptor for the timer (Linux only)
-static int timerfd = -1;
-
-// pipe for signaling exit
-static int pipefds[2];
-
 static void *itimer_thread_func(void *_handle_tick)
 {
     TickProc handle_tick = _handle_tick;
-    uint64_t nticks;
-    ssize_t r = 0;
-    struct pollfd pollfds[2];
-
-#if USE_TIMERFD_FOR_ITIMER
-    pollfds[0].fd = pipefds[0];
-    pollfds[0].events = POLLIN;
-    pollfds[1].fd = timerfd;
-    pollfds[1].events = POLLIN;
-#endif
 
     // Relaxed is sufficient: If we don't see that exited was set in one iteration we will
     // see it next time.
     TSAN_ANNOTATE_BENIGN_RACE(&exited, "itimer_thread_func");
     while (!RELAXED_LOAD(&exited)) {
-        if (USE_TIMERFD_FOR_ITIMER) {
-            if (poll(pollfds, 2, -1) == -1) {
-                sysErrorBelch("Ticker: poll failed: %s", strerror(errno));
-            }
-
-            // We check the pipe first, even though the timerfd may also have triggered.
-            if (pollfds[0].revents & POLLIN) {
-                // the pipe is ready for reading, the only possible reason is that we're exiting
-                exited = true; // set this again to make sure even RELAXED_LOAD will read the proper value
-                // no further action needed, skip ahead to handling the final tick and then stopping
-            }
-            else if (pollfds[1].revents & POLLIN) { // the timerfd is ready for reading
-                r = read(timerfd, &nticks, sizeof(nticks)); // this should never block now
-
-                if ((r == 0) && (errno == 0)) {
-                   /* r == 0 is expected only for non-blocking fd (in which case
-                    * errno should be EAGAIN) but we use a blocking fd.
-                    *
-                    * Due to a kernel bug (cf https://lkml.org/lkml/2019/8/16/335)
-                    * on some platforms we could see r == 0 and errno == 0.
-                    */
-                   IF_DEBUG(scheduler, debugBelch("read(timerfd) returned 0 with errno=0. This is a known kernel bug. We just ignore it."));
-                }
-                else if (r != sizeof(nticks) && errno != EINTR) {
-                   barf("Ticker: read(timerfd) failed with %s and returned %zd", strerror(errno), r);
-                }
-            }
-        } else {
-            if (rtsSleep(itimer_interval) != 0) {
-                sysErrorBelch("Ticker: sleep failed: %s", strerror(errno));
-            }
+        if (rtsSleep(itimer_interval) != 0) {
+            sysErrorBelch("Ticker: sleep failed: %s", strerror(errno));
         }
 
         // first try a cheap test
@@ -164,10 +112,6 @@ static void *itimer_thread_func(void *_handle_tick)
         }
     }
 
-    if (USE_TIMERFD_FOR_ITIMER) {
-        close(timerfd);
-    }
-
     return NULL;
 }
 
@@ -186,39 +130,6 @@ 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));
-    }
-
-    if (pipe(pipefds) < 0) {
-        barf("pipe: %s", strerror(errno));
-    }
-#endif
-
     /*
      * Create the thread with all blockable signals blocked, leaving signal
      * handling to the main and/or other threads.  This is especially useful in
@@ -269,21 +180,9 @@ exitTicker (bool wait)
 
     // wait for ticker to terminate if necessary
     if (wait) {
-#if USE_TIMERFD_FOR_ITIMER
-        // write anything to the pipe to trigger poll() in the ticker thread
-        if (write(pipefds[1], "stop", 5) < 0) {
-            sysErrorBelch("Ticker: Failed to write to pipe: %s", strerror(errno));
-        }
-#endif
         if (pthread_join(thread, NULL)) {
             sysErrorBelch("Ticker: Failed to join: %s", strerror(errno));
         }
-#if USE_TIMERFD_FOR_ITIMER
-        // These need to happen AFTER the ticker thread has finished to prevent a race condition
-        // where the ticker thread closes the read end of the pipe before we're done writing to it.
-        close(pipefds[0]);
-        close(pipefds[1]);
-#endif
         closeMutex(&mutex);
         closeCondition(&start_cond);
     } else {


=====================================
rts/posix/ticker/TimerFd.c
=====================================
@@ -0,0 +1,280 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team, 1995-2023
+ *
+ * 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 frequencies: 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.  Therefore 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 "rts/PosixSource.h"
+#include "Rts.h"
+
+#include "Ticker.h"
+#include "RtsUtils.h"
+#include "Proftimer.h"
+#include "Schedule.h"
+#include "posix/Clock.h"
+#include <sys/poll.h>
+
+#include <time.h>
+#if HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#if defined(HAVE_SIGNAL_H)
+# include <signal.h>
+#endif
+
+#include <string.h>
+
+#include <pthread.h>
+#if defined(HAVE_PTHREAD_NP_H)
+#include <pthread_np.h>
+#endif
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/timerfd.h>
+
+
+/*
+ * TFD_CLOEXEC has been added in Linux 2.6.26.
+ * If it is not available, we use fcntl(F_SETFD).
+ */
+#if !defined(TFD_CLOEXEC)
+#define TFD_CLOEXEC 0
+#endif
+
+static Time itimer_interval = DEFAULT_TICK_INTERVAL;
+
+// Should we be firing ticks?
+// Writers to this must hold the mutex below.
+static bool stopped = false;
+
+// should the ticker thread exit?
+// This can be set without holding the mutex.
+static bool exited = true;
+
+// Signaled when we want to (re)start the timer
+static Condition start_cond;
+static Mutex mutex;
+static OSThreadId thread;
+
+// file descriptor for the timer (Linux only)
+static int timerfd = -1;
+
+// pipe for signaling exit
+static int pipefds[2];
+
+static void *itimer_thread_func(void *_handle_tick)
+{
+    TickProc handle_tick = _handle_tick;
+    uint64_t nticks;
+    ssize_t r = 0;
+    struct pollfd pollfds[2];
+
+    pollfds[0].fd = pipefds[0];
+    pollfds[0].events = POLLIN;
+    pollfds[1].fd = timerfd;
+    pollfds[1].events = POLLIN;
+
+    // Relaxed is sufficient: If we don't see that exited was set in one iteration we will
+    // see it next time.
+    TSAN_ANNOTATE_BENIGN_RACE(&exited, "itimer_thread_func");
+    while (!RELAXED_LOAD(&exited)) {
+        if (poll(pollfds, 2, -1) == -1) {
+            sysErrorBelch("Ticker: poll failed: %s", strerror(errno));
+        }
+
+        // We check the pipe first, even though the timerfd may also have triggered.
+        if (pollfds[0].revents & POLLIN) {
+            // the pipe is ready for reading, the only possible reason is that we're exiting
+            exited = true; // set this again to make sure even RELAXED_LOAD will read the proper value
+            // no further action needed, skip ahead to handling the final tick and then stopping
+        }
+        else if (pollfds[1].revents & POLLIN) { // the timerfd is ready for reading
+            r = read(timerfd, &nticks, sizeof(nticks)); // this should never block now
+
+            if ((r == 0) && (errno == 0)) {
+               /* r == 0 is expected only for non-blocking fd (in which case
+                * errno should be EAGAIN) but we use a blocking fd.
+                *
+                * Due to a kernel bug (cf https://lkml.org/lkml/2019/8/16/335)
+                * on some platforms we could see r == 0 and errno == 0.
+                */
+               IF_DEBUG(scheduler, debugBelch("read(timerfd) returned 0 with errno=0. This is a known kernel bug. We just ignore it."));
+            }
+            else if (r != sizeof(nticks) && errno != EINTR) {
+               barf("Ticker: read(timerfd) failed with %s and returned %zd", strerror(errno), r);
+            }
+        }
+
+        // first try a cheap test
+        TSAN_ANNOTATE_BENIGN_RACE(&stopped, "itimer_thread_func");
+        if (RELAXED_LOAD(&stopped)) {
+            OS_ACQUIRE_LOCK(&mutex);
+            // should we really stop?
+            if (stopped) {
+                waitCondition(&start_cond, &mutex);
+            }
+            OS_RELEASE_LOCK(&mutex);
+        } else {
+            handle_tick(0);
+        }
+    }
+
+    close(timerfd);
+    return NULL;
+}
+
+void
+initTicker (Time interval, TickProc handle_tick)
+{
+    itimer_interval = interval;
+    stopped = true;
+    exited = false;
+#if defined(HAVE_SIGNAL_H)
+    sigset_t mask, omask;
+    int sigret;
+#endif
+    int ret;
+
+    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)
+     */
+    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));
+    }
+
+    if (pipe(pipefds) < 0) {
+        barf("pipe: %s", strerror(errno));
+    }
+
+    /*
+     * Create the thread with all blockable signals blocked, leaving signal
+     * handling to the main and/or other threads.  This is especially useful in
+     * the non-threaded runtime, where applications might expect sigprocmask(2)
+     * to effectively block signals.
+     */
+#if defined(HAVE_SIGNAL_H)
+    sigfillset(&mask);
+    sigret = pthread_sigmask(SIG_SETMASK, &mask, &omask);
+#endif
+    ret = createAttachedOSThread(&thread, "ghc_ticker", itimer_thread_func, (void*)handle_tick);
+#if defined(HAVE_SIGNAL_H)
+    if (sigret == 0)
+        pthread_sigmask(SIG_SETMASK, &omask, NULL);
+#endif
+
+    if (ret != 0) {
+        barf("Ticker: Failed to spawn thread: %s", strerror(errno));
+    }
+}
+
+void
+startTicker(void)
+{
+    OS_ACQUIRE_LOCK(&mutex);
+    RELAXED_STORE(&stopped, false);
+    signalCondition(&start_cond);
+    OS_RELEASE_LOCK(&mutex);
+}
+
+/* There may be at most one additional tick fired after a call to this */
+void
+stopTicker(void)
+{
+    OS_ACQUIRE_LOCK(&mutex);
+    RELAXED_STORE(&stopped, true);
+    OS_RELEASE_LOCK(&mutex);
+}
+
+/* There may be at most one additional tick fired after a call to this */
+void
+exitTicker (bool wait)
+{
+    ASSERT(!SEQ_CST_LOAD(&exited));
+    SEQ_CST_STORE(&exited, true);
+    // ensure that ticker wakes up if stopped
+    startTicker();
+
+    // wait for ticker to terminate if necessary
+    if (wait) {
+        // write anything to the pipe to trigger poll() in the ticker thread
+        if (write(pipefds[1], "stop", 5) < 0) {
+            sysErrorBelch("Ticker: Failed to write to pipe: %s", strerror(errno));
+        }
+
+        if (pthread_join(thread, NULL)) {
+            sysErrorBelch("Ticker: Failed to join: %s", strerror(errno));
+        }
+
+        // These need to happen AFTER the ticker thread has finished to prevent a race condition
+        // where the ticker thread closes the read end of the pipe before we're done writing to it.
+        close(pipefds[0]);
+        close(pipefds[1]);
+
+        closeMutex(&mutex);
+        closeCondition(&start_cond);
+    } else {
+        pthread_detach(thread);
+    }
+}
+
+int
+rtsTimerSignal(void)
+{
+    return SIGALRM;
+}



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/47d0fba86cc11886b9c39f477392fad95bfa7dda...9ad7c49591c73ae7a3c1f989dcd803f9c69d7ad9

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/47d0fba86cc11886b9c39f477392fad95bfa7dda...9ad7c49591c73ae7a3c1f989dcd803f9c69d7ad9
You're receiving this email because of your account on gitlab.haskell.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20230202/d2e93f76/attachment-0001.html>


More information about the ghc-commits mailing list