[project @ 2005-04-07 14:33:30 by simonmar]
authorsimonmar <unknown>
Thu, 7 Apr 2005 14:33:32 +0000 (14:33 +0000)
committersimonmar <unknown>
Thu, 7 Apr 2005 14:33:32 +0000 (14:33 +0000)
Support handling signals in the threaded RTS by passing the signal
number down the pipe to the IO manager.  This avoids needing
synchronisation in the signal handler.

Signals should now work with -threaded.  Since this is a bugfix, I'll
merge the changes into the 6.4 branch.

ghc/includes/RtsExternal.h
ghc/rts/Capability.c
ghc/rts/Schedule.c
ghc/rts/Select.c
ghc/rts/Signals.c
ghc/rts/Signals.h

index a597918..a988b18 100644 (file)
@@ -68,6 +68,9 @@ extern int stg_InstallConsoleEvent(int action, StgStablePtr *handler);
 extern int stg_sig_install (int, int, StgStablePtr *, void *);
 #endif
 
+extern void startSignalHandler(int sig);
+extern void setIOManagerPipe (int fd);
+
 /* -----------------------------------------------------------------------------
    Storage manager stuff exported
    -------------------------------------------------------------------------- */
index bdb651d..56e23d1 100644 (file)
@@ -22,7 +22,6 @@
 #include "OSThreads.h"
 #include "Capability.h"
 #include "Schedule.h"  /* to get at EMPTY_RUN_QUEUE() */
-#include "Signals.h" /* to get at handleSignalsInThisThread() */
 
 #if !defined(SMP)
 Capability MainCapability;     /* for non-SMP, we have one global capability */
@@ -184,7 +183,6 @@ grabCapability( Capability** cap )
   rts_n_free_capabilities = 0;
 # endif
   *cap = &MainCapability;
-  handleSignalsInThisThread();
 #endif
 #if defined(RTS_SUPPORTS_THREADS)
   IF_DEBUG(scheduler, sched_belch("worker: got capability"));
@@ -304,7 +302,6 @@ waitForReturnCapability( Mutex* pMutex, Capability** pCap )
        *pCap = &MainCapability;
        ASSERT(rts_n_free_capabilities == 0);
 #endif
-       handleSignalsInThisThread();
     } else {
        grabCapability(pCap);
     }
index 6cac6c1..b0a4072 100644 (file)
@@ -816,7 +816,7 @@ schedulePreLoop(void)
 static void
 scheduleStartSignalHandlers(void)
 {
-#if defined(RTS_USER_SIGNALS)
+#if defined(RTS_USER_SIGNALS) && !defined(RTS_SUPPORTS_THREADS)
     if (signals_pending()) {
       RELEASE_LOCK(&sched_mutex); /* ToDo: kill */
       startSignalHandlers();
@@ -901,11 +901,13 @@ scheduleDetectDeadlock(void)
 
            awaitUserSignals();
 
+#if !defined(RTS_SUPPORTS_THREADS)
            if (signals_pending()) {
                RELEASE_LOCK(&sched_mutex);
                startSignalHandlers();
                ACQUIRE_LOCK(&sched_mutex);
            }
+#endif
 
            // either we have threads to run, or we were interrupted:
            ASSERT(!EMPTY_RUN_QUEUE() || interrupted);
index 67ba589..3cec3a9 100644 (file)
@@ -6,7 +6,6 @@
  *
  * ---------------------------------------------------------------------------*/
 
-
 /* we're outside the realms of POSIX here... */
 /* #include "PosixSource.h" */
 
 /* last timestamp */
 lnat timestamp = 0;
 
+#if !defined(RTS_SUPPORTS_THREADS)
+/* 
+ * The threaded RTS uses an IO-manager thread in Haskell instead (see GHC.Conc) 
+ */
+
 /* There's a clever trick here to avoid problems when the time wraps
  * around.  Since our maximum delay is smaller than 31 bits of ticks
  * (it's actually 31 bits of microseconds), we can safely check
@@ -262,3 +266,5 @@ awaitEvent(rtsBool wait)
       
     } while (wait && !interrupted && run_queue_hd == END_TSO_QUEUE);
 }
+
+#endif /* RTS_SUPPORTS_THREADS */
index 67fbf42..bd8c3aa 100644 (file)
 /* This curious flag is provided for the benefit of the Haskell binding
  * to POSIX.1 to control whether or not to include SA_NOCLDSTOP when
  * installing a SIGCHLD handler. 
- * 
  */
 StgInt nocldstop = 0;
 
+/* -----------------------------------------------------------------------------
+ * The table of signal handlers
+ * -------------------------------------------------------------------------- */
+
 #if defined(RTS_USER_SIGNALS)
 
 /* SUP: The type of handlers is a little bit, well, doubtful... */
@@ -49,30 +52,6 @@ static StgInt nHandlers = 0;    /* Size of handlers array */
 
 static nat n_haskell_handlers = 0;
 
-#define N_PENDING_HANDLERS 16
-
-StgPtr pending_handler_buf[N_PENDING_HANDLERS];
-StgPtr *next_pending_handler = pending_handler_buf;
-
-/* -----------------------------------------------------------------------------
- * Signal handling
- * -------------------------------------------------------------------------- */
-
-#ifdef RTS_SUPPORTS_THREADS
-pthread_t signalHandlingThread;
-#endif
-
-// Handle all signals in the current thread.
-// Called from Capability.c whenever the main capability is granted to a thread
-// and in installDefaultHandlers
-void
-handleSignalsInThisThread(void)
-{
-#ifdef RTS_SUPPORTS_THREADS
-    signalHandlingThread = pthread_self();
-#endif
-}
-
 /* -----------------------------------------------------------------------------
  * Allocate/resize the table of signal handlers.
  * -------------------------------------------------------------------------- */
@@ -98,6 +77,53 @@ more_handlers(I_ sig)
 }
 
 /* -----------------------------------------------------------------------------
+ * Pending Handlers
+ *
+ * The mechanism for starting handlers differs between the threaded
+ * (RTS_SUPPORTS_THREADS) and non-threaded versions of the RTS.
+ *
+ * When the RTS is single-threaded, we just write the pending signal
+ * handlers into a buffer, and start a thread for each one in the
+ * scheduler loop.
+ *
+ * When RTS_SUPPORTS_THREADS, the problem is that signals might be
+ * delivered to multiple threads, so we would need to synchronise
+ * access to pending_handler_buf somehow.  Using thread
+ * synchronisation from a signal handler isn't possible in general
+ * (some OSs support it, eg. MacOS X, but not all).  So instead:
+ *
+ *   - the signal handler writes the signal number into the pipe
+ *     managed by the IO manager thread (see GHC.Conc).
+ *   - the IO manager picks up the signal number and calls
+ *     startSignalHandler() to start the thread.
+ *
+ * This also has the nice property that we don't need to arrange to
+ * wake up a worker task to start the signal handler: the IO manager
+ * wakes up when we write into the pipe.
+ *
+ * -------------------------------------------------------------------------- */
+
+// Here's the pipe into which we will send our signals
+static int io_manager_pipe = -1;
+
+void
+setIOManagerPipe (int fd)
+{
+    // only called when RTS_SUPPORTS_THREADS, but unconditionally
+    // compiled here because GHC.Conc depends on it.
+    io_manager_pipe = fd;
+}
+
+#if !defined(RTS_SUPPORTS_THREADS)
+
+#define N_PENDING_HANDLERS 16
+
+StgPtr pending_handler_buf[N_PENDING_HANDLERS];
+StgPtr *next_pending_handler = pending_handler_buf;
+
+#endif /* RTS_SUPPORTS_THREADS */
+
+/* -----------------------------------------------------------------------------
  * SIGCONT handler
  *
  * It seems that shells tend to put stdin back into blocking mode
@@ -125,18 +151,20 @@ generic_handler(int sig)
 {
     sigset_t signals;
 
-#if defined(THREADED_RTS)
-       // Make the thread that currently holds the main capability
-       // handle the signal.
-       // This makes sure that awaitEvent() is interrupted
-       // and it (hopefully) prevents race conditions
-       // (signal handlers are not atomic with respect to other threads)
+#if defined(RTS_SUPPORTS_THREADS)
 
-    if(pthread_self() != signalHandlingThread) {
-        pthread_kill(signalHandlingThread, sig);
-        return;
+    if (io_manager_pipe != -1)
+    {
+       // Write the signal number into the pipe as a single byte.  We
+       // hope that signals fit into a byte...
+       StgWord8 csig = (StgWord8)sig;
+       write(io_manager_pipe, &csig, 1);
     }
-#endif
+    // If the IO manager hasn't told us what the FD of the write end
+    // of its pipe is, there's not much we can do here, so just ignore
+    // the signal..
+
+#else /* not RTS_SUPPORTS_THREADS */
 
     /* Can't call allocate from here.  Probably can't call malloc
        either.  However, we have to schedule a new thread somehow.
@@ -174,6 +202,8 @@ generic_handler(int sig)
        stg_exit(EXIT_FAILURE);
     }
     
+#endif /* RTS_SUPPORTS_THREADS */
+
     // re-establish the signal handler, and carry on
     sigemptyset(&signals);
     sigaddset(&signals, sig);
@@ -218,6 +248,7 @@ anyUserHandlers(void)
     return n_haskell_handlers != 0;
 }
 
+#if !defined(RTS_SUPPORTS_THREADS)
 void
 awaitUserSignals(void)
 {
@@ -225,6 +256,7 @@ awaitUserSignals(void)
        pause();
     }
 }
+#endif
 
 /* -----------------------------------------------------------------------------
  * Install a Haskell signal handler.
@@ -307,11 +339,27 @@ stg_sig_install(int sig, int spi, StgStablePtr *handler, void *mask)
 }
 
 /* -----------------------------------------------------------------------------
- * Creating new threads for the pending signal handlers.
+ * Creating new threads for signal handlers.
  * -------------------------------------------------------------------------- */
+
+void
+startSignalHandler(int sig)  // called by the IO manager, see GHC.Conc
+{
+#if defined(RTS_SUPPORTS_THREADS)
+    // ToDo: fix race window between the time at which the signal is
+    // delivered and the deRefStablePtr() call here.  There's no way
+    // to safely uninstall a signal handler.
+    scheduleThread(
+       createIOThread(RtsFlags.GcFlags.initialStkSize, 
+                      (StgClosure *)deRefStablePtr((StgStablePtr)handlers[sig]))
+       );
+#endif
+}
+
 void
 startSignalHandlers(void)
 {
+#if !defined(RTS_SUPPORTS_THREADS)
   blockUserSignals();
   
   while (next_pending_handler != pending_handler_buf) {
@@ -324,6 +372,7 @@ startSignalHandlers(void)
   }
 
   unblockUserSignals();
+#endif
 }
 
 /* ----------------------------------------------------------------------------
@@ -335,6 +384,7 @@ startSignalHandlers(void)
  * avoid race conditions.
  * -------------------------------------------------------------------------- */
 
+#if !defined(RTS_SUPPORTS_THREADS)
 void
 markSignalHandlers (evac_fn evac)
 {
@@ -346,6 +396,12 @@ markSignalHandlers (evac_fn evac)
        evac((StgClosure **)p);
     }
 }
+#else
+void
+markSignalHandlers (evac_fn evac STG_UNUSED)
+{
+}
+#endif
 
 #else /* !RTS_USER_SIGNALS */
 StgInt 
@@ -383,17 +439,6 @@ shutdown_handler(int sig STG_UNUSED)
        pthread_kill(startup_guy, sig);
        return;
     }
-    // ToDo: The code for the threaded RTS below does something very
-    // similar. Maybe the SMP special case is not needed
-    // -- Wolfgang Thaller
-#elif defined(THREADED_RTS)
-       // Make the thread that currently holds the main capability
-       // handle the signal.
-       // This makes sure that awaitEvent() is interrupted
-    if(pthread_self() != signalHandlingThread) {
-        pthread_kill(signalHandlingThread, sig);
-        return;
-    }
 #endif
 
     // If we're already trying to interrupt the RTS, terminate with
@@ -429,9 +474,6 @@ initDefaultHandlers()
 #ifdef SMP
     startup_guy = pthread_self();
 #endif
-#ifdef RTS_SUPPORTS_THREADS
-    handleSignalsInThisThread();
-#endif
 
     // install the SIGINT handler
     action.sa_handler = shutdown_handler;
index ffcac55..32c7fbd 100644 (file)
@@ -9,17 +9,24 @@
 #if !defined(PAR) && !defined(mingw32_HOST_OS)
 #define RTS_USER_SIGNALS 1
 
-extern StgPtr pending_handler_buf[];
-extern StgPtr *next_pending_handler;
-
-#define signals_pending() (next_pending_handler != pending_handler_buf)
-
 extern void    initUserSignals(void);
 extern void    blockUserSignals(void);
 extern void    unblockUserSignals(void);
 
 extern rtsBool anyUserHandlers(void);
-extern void    awaitUserSignals(void);
+
+#if !defined(RTS_SUPPORTS_THREADS)
+
+extern StgPtr pending_handler_buf[];
+extern StgPtr *next_pending_handler;
+#define signals_pending() (next_pending_handler != pending_handler_buf)
+extern void awaitUserSignals(void);
+
+#else
+
+extern void startSignalHandler(int sig);
+
+#endif
 
 /* sig_install declared in PrimOps.h */
 
@@ -27,9 +34,6 @@ extern void startSignalHandlers(void);
 extern void markSignalHandlers (evac_fn evac);
 extern void initDefaultHandlers(void);
 
-extern void handleSignalsInThisThread(void);
-extern void handleSignalsInPrevThread(void);
-
 #elif defined(mingw32_HOST_OS)
 #define RTS_USER_SIGNALS 1
 #include "win32/ConsoleHandler.h"