Add support for the IO manager thread on Windows
authorSimon Marlow <simonmar@microsoft.com>
Fri, 1 Dec 2006 14:48:23 +0000 (14:48 +0000)
committerSimon Marlow <simonmar@microsoft.com>
Fri, 1 Dec 2006 14:48:23 +0000 (14:48 +0000)
Fixes #637.
The implications of this change are:
  - threadDelay on Windows no longer creates a new OS thread each time,
    instead it communicates with the IO manager thread in the same way as
    on Unix.
  - deadlock detection now works the same way on Windows as on Unix; that
    is the timer interrupt wakes up the IO manager thread, which causes
    the scheduler to check for deadlock.
  - Console events now get sent to the IO manager thread, in the same way as
    signals do on Unix.  This means that console events should behave more
    reliably with -threaded on Windows.

All this applies only with -threaded.  Without -threaded, the old
ConsoleEvent code is still used.

After some testing, this could be pushed to the 6.6 branch.

includes/RtsExternal.h
rts/Prelude.h
rts/RtsStartup.c
rts/Schedule.c
rts/ThrIOManager.h [new file with mode: 0644]
rts/posix/Signals.h
rts/win32/ConsoleHandler.c
rts/win32/ConsoleHandler.h
rts/win32/ThrIOManager.c [new file with mode: 0644]

index 8e1a26a..bf581b7 100644 (file)
@@ -73,10 +73,19 @@ extern void rts_ConsoleHandlerDone  ( int ev );
 extern int stg_sig_install (int, int, StgStablePtr *, void *);
 #endif
 
-#if !defined(mingw32_HOST_OS)
+#if defined(mingw32_HOST_OS)
+extern StgInt console_handler;
+#else
 extern StgInt *signal_handlers;
 #endif
+
+#if defined(mingw32_HOST_OS)
+void *getIOManagerEvent (void);
+StgWord32 readIOManagerEvent (void);
+void sendIOManagerEvent (StgWord32 event);
+#else
 extern void setIOManagerPipe (int fd);
+#endif
 
 extern void* allocateExec(unsigned int len);
 
index 7d7b5b2..2a6ca91 100644 (file)
@@ -42,9 +42,7 @@ PRELUDE_CLOSURE(base_GHCziIOBase_BlockedIndefinitely_closure);
 PRELUDE_CLOSURE(base_GHCziIOBase_NonTermination_closure);
 PRELUDE_CLOSURE(base_GHCziIOBase_NestedAtomically_closure);
 
-#if !defined(mingw32_HOST_OS)
 PRELUDE_CLOSURE(base_GHCziConc_ensureIOManagerIsRunning_closure);
-#endif
 
 PRELUDE_INFO(base_GHCziBase_Czh_static_info);
 PRELUDE_INFO(base_GHCziBase_Izh_static_info);
index dba529b..824ef98 100644 (file)
@@ -19,6 +19,7 @@
 #include "STM.h"        /* initSTM */
 #include "Signals.h"
 #include "RtsSignals.h"
+#include "ThrIOManager.h"
 #include "Timer.h"      /* startTimer, stopTimer */
 #include "Weak.h"
 #include "Ticky.h"
@@ -268,7 +269,7 @@ hs_init(int *argc, char **argv[])
     x86_init_fpu();
 #endif
 
-#if defined(THREADED_RTS) && !defined(mingw32_HOST_OS)
+#if defined(THREADED_RTS)
     ioManagerStart();
 #endif
 
@@ -371,7 +372,7 @@ hs_exit(void)
     /* start timing the shutdown */
     stat_startExit();
     
-#if defined(THREADED_RTS) && !defined(mingw32_HOST_OS)
+#if defined(THREADED_RTS)
     ioManagerDie();
 #endif
 
index 0a46ec5..cf62c5a 100644 (file)
@@ -51,6 +51,7 @@
 #include "Trace.h"
 #include "RaiseAsync.h"
 #include "Threads.h"
+#include "ThrIOManager.h"
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -849,7 +850,7 @@ schedulePushWork(Capability *cap USED_IF_THREADS,
  * Start any pending signal handlers
  * ------------------------------------------------------------------------- */
 
-#if defined(RTS_USER_SIGNALS) && (!defined(THREADED_RTS) || defined(mingw32_HOST_OS))
+#if defined(RTS_USER_SIGNALS) && !defined(THREADED_RTS)
 static void
 scheduleStartSignalHandlers(Capability *cap)
 {
@@ -971,7 +972,7 @@ scheduleDetectDeadlock (Capability *cap, Task *task)
        
        if ( !emptyRunQueue(cap) ) return;
 
-#if defined(RTS_USER_SIGNALS) && (!defined(THREADED_RTS) || defined(mingw32_HOST_OS))
+#if defined(RTS_USER_SIGNALS) && !defined(THREADED_RTS)
        /* If we have user-installed signal handlers, then wait
         * for signals to arrive rather then bombing out with a
         * deadlock.
@@ -2820,17 +2821,10 @@ void
 wakeUpRts(void)
 {
 #if defined(THREADED_RTS)
-#if !defined(mingw32_HOST_OS)
     // This forces the IO Manager thread to wakeup, which will
     // in turn ensure that some OS thread wakes up and runs the
     // scheduler loop, which will cause a GC and deadlock check.
     ioManagerWakeup();
-#else
-    // On Windows this might be safe enough, because we aren't
-    // in a signal handler.  Later we should use the IO Manager,
-    // though.
-    prodOneCapability();
-#endif
 #endif
 }
 
diff --git a/rts/ThrIOManager.h b/rts/ThrIOManager.h
new file mode 100644 (file)
index 0000000..eeccc6c
--- /dev/null
@@ -0,0 +1,15 @@
+/* -----------------------------------------------------------------------------\r
+ *\r
+ * (c) The GHC Team 1998-2006\r
+ *\r
+ * Communicating with the IO manager thread (see GHC.Conc).\r
+ * Posix implementation in posix/Signals.c\r
+ * Win32 implementation in win32/ThrIOManager.c\r
+ *\r
+ * -------------------------------------------------------------------------*/\r
+\r
+#if defined(THREADED_RTS)\r
+void ioManagerWakeup (void);\r
+void ioManagerDie (void);\r
+void ioManagerStart (void);\r
+#endif\r
index b005abb..aa440b3 100644 (file)
@@ -18,12 +18,6 @@ extern StgPtr *next_pending_handler;
 void startSignalHandlers(Capability *cap);
 #endif
 
-#if defined(THREADED_RTS)
-void ioManagerWakeup (void);
-void ioManagerDie (void);
-void ioManagerStart (void);
-#endif
-
 extern StgInt *signal_handlers;
 
 #endif /* POSIX_SIGNALS_H */
index afaa424..5b5cfc3 100644 (file)
@@ -18,7 +18,9 @@ static BOOL WINAPI shutdown_handler(DWORD dwCtrlType);
 static BOOL WINAPI generic_handler(DWORD dwCtrlType);
 
 static rtsBool deliver_event = rtsTrue;
-static StgInt console_handler = STG_SIG_DFL;
+StgInt console_handler = STG_SIG_DFL;
+
+#if !defined(THREADED_RTS)
 
 static HANDLE hConsoleEvent = INVALID_HANDLE_VALUE;
 
@@ -26,6 +28,8 @@ static HANDLE hConsoleEvent = INVALID_HANDLE_VALUE;
 StgInt stg_pending_events = 0;           /* number of undelivered events */
 DWORD stg_pending_buf[N_PENDING_EVENTS]; /* their associated event numbers. */
 
+#endif
+
 /*
  * Function: initUserSignals()
  *
@@ -34,8 +38,9 @@ DWORD stg_pending_buf[N_PENDING_EVENTS]; /* their associated event numbers. */
 void
 initUserSignals(void)
 {
-    stg_pending_events = 0;
     console_handler = STG_SIG_DFL;
+#if !defined (THREADED_RTS)
+    stg_pending_events = 0;
     if (hConsoleEvent == INVALID_HANDLE_VALUE) {
        hConsoleEvent = 
            CreateEvent ( NULL,  /* default security attributes */
@@ -43,6 +48,7 @@ initUserSignals(void)
                          FALSE, /* initially non-signalled */
                          NULL); /* no name */
     }
+#endif
     return;
 }
 
@@ -50,9 +56,11 @@ initUserSignals(void)
 void
 finiUserSignals(void)
 {
+#if !defined (THREADED_RTS)
     if (hConsoleEvent != INVALID_HANDLE_VALUE) {
         CloseHandle(hConsoleEvent);
     }
+#endif
 }
 
 /*
@@ -83,9 +91,6 @@ static BOOL WINAPI shutdown_handler(DWORD dwCtrlType)
            stg_exit(EXIT_INTERRUPTED);
        } else {
            interruptStgRts();
-           /* Cheesy pulsing of an event to wake up a waiting RTS thread, if any */
-           abandonRequestWait();
-           resetAbandonRequestWait();
        }
        return TRUE;
 
@@ -148,6 +153,7 @@ void awaitUserSignals(void)
 }
 
 
+#if !defined (THREADED_RTS)
 /*
  * Function: startSignalHandlers()
  *
@@ -180,6 +186,7 @@ void startSignalHandlers(Capability *cap)
     RELEASE_LOCK(&sched_mutex);
     unblockUserSignals();
 }
+#endif /* !THREADED_RTS */
 
 /*
  * Function: markSignalHandlers()
@@ -202,8 +209,6 @@ void markSignalHandlers (evac_fn evac STG_UNUSED)
  */
 static BOOL WINAPI generic_handler(DWORD dwCtrlType)
 {
-    ACQUIRE_LOCK(&sched_mutex);
-
     /* Ultra-simple -- up the counter + signal a switch. */
     switch(dwCtrlType) {
     case CTRL_CLOSE_EVENT:
@@ -217,17 +222,16 @@ static BOOL WINAPI generic_handler(DWORD dwCtrlType)
     default:
        if (!deliver_event) return TRUE;
 
+#if defined(THREADED_RTS)
+        sendIOManagerEvent((StgWord8) ((dwCtrlType<<1) | 1));
+#else
        if ( stg_pending_events < N_PENDING_EVENTS ) {
            stg_pending_buf[stg_pending_events] = dwCtrlType;
            stg_pending_events++;
        }
-       /* Cheesy pulsing of an event to wake up a waiting RTS thread, if any */
-       abandonRequestWait();
-       resetAbandonRequestWait();
+#endif
        return TRUE;
     }
-
-    RELEASE_LOCK(&sched_mutex);
 }
 
 
@@ -293,17 +297,22 @@ rts_InstallConsoleEvent(int action, StgStablePtr *handler)
  *
  */
 void
-rts_ConsoleHandlerDone(int ev)
+rts_ConsoleHandlerDone (int ev USED_IF_NOT_THREADS)
 {
+#if !defined(THREADED_RTS)
     if ( (DWORD)ev == CTRL_BREAK_EVENT ||
         (DWORD)ev == CTRL_C_EVENT ) {
        /* only these two cause stdin system calls to abort.. */
        SetEvent(hConsoleEvent); /* event is manual-reset */
        Sleep(0); /* yield */
        ResetEvent(hConsoleEvent); /* turn it back off again */
+        // SDM: yeuch, this can't possibly work reliably.
+        // I'm not having it in THREADED_RTS.
     }
+#endif
 }
 
+#if !defined(THREADED_RTS)
 /*
  * Function: rts_waitConsoleHandlerCompletion()
  *
@@ -318,3 +327,4 @@ rts_waitConsoleHandlerCompletion()
      */
     return (WaitForSingleObject(hConsoleEvent, INFINITE) == WAIT_OBJECT_0);
 }
+#endif
index b09adf7..33fa065 100644 (file)
@@ -9,9 +9,16 @@
  * Console control handlers lets an application handle Ctrl+C, Ctrl+Break etc.
  * in Haskell under Win32. Akin to the Unix signal SIGINT.
  *
- * The API offered by ConsoleHandler.h is identical to that of the signal handling
- * code (which isn't supported under win32.) Unsurprisingly, the underlying impl 
- * is derived from the signal handling code also.
+ * The API offered by ConsoleHandler.h is identical to that of the
+ * signal handling code (which isn't supported under win32.)
+ * Unsurprisingly, the underlying impl is derived from the signal
+ * handling code also.
+ */
+
+#if !defined(THREADED_RTS)
+/* 
+ * under THREADED_RTS, console events are passed to the IO manager
+ * thread, which starts up the handler.  See ThrIOManager.c.
  */
 
 /*
@@ -60,4 +67,6 @@ extern void handleSignalsInThisThread(void);
  */
 extern int rts_waitConsoleHandlerCompletion(void);
 
+#endif /* THREADED_RTS */
+
 #endif /* __CONSOLEHANDLER_H__ */
diff --git a/rts/win32/ThrIOManager.c b/rts/win32/ThrIOManager.c
new file mode 100644 (file)
index 0000000..b0da0de
--- /dev/null
@@ -0,0 +1,144 @@
+/* -----------------------------------------------------------------------------\r
+ *\r
+ * (c) The GHC Team, 1998-2006\r
+ *\r
+ * The IO manager thread in THREADED_RTS.  \r
+ * See also libraries/base/GHC/Conc.lhs.\r
+ *\r
+ * ---------------------------------------------------------------------------*/\r
+\r
+#include "Rts.h"\r
+#include "ThrIOManager.h"\r
+#include "Prelude.h"\r
+#include <windows.h>\r
+\r
+// Here's the Event that we use to wake up the IO manager thread\r
+static HANDLE io_manager_event = INVALID_HANDLE_VALUE;\r
+\r
+// must agree with values in GHC.Conc:\r
+#define IO_MANAGER_WAKEUP 0xffffffff\r
+#define IO_MANAGER_DIE    0xfffffffe\r
+// spurios wakeups are returned as zero.\r
+// console events are ((event<<1) | 1)\r
+\r
+HANDLE\r
+getIOManagerEvent (void)\r
+{\r
+    // This function has to exist even in the non-THREADED_RTS,\r
+    // because code in GHC.Conc refers to it.  It won't ever be called\r
+    // unless we're in the threaded RTS, however.\r
+#ifdef THREADED_RTS\r
+    HANDLE hRes;\r
+\r
+    if (io_manager_event == INVALID_HANDLE_VALUE) {\r
+        hRes = CreateEvent ( NULL, // no security attrs\r
+                             TRUE, // manual reset\r
+                             FALSE, // initial state,\r
+                             "IO Manager Event" );\r
+        if (hRes == NULL) {\r
+            sysErrorBelch("getIOManagerEvent");\r
+            stg_exit(EXIT_FAILURE);\r
+        }\r
+        io_manager_event = hRes;\r
+        return hRes;\r
+    } else {\r
+        return io_manager_event;\r
+    }\r
+#else\r
+    return NULL;\r
+#endif\r
+}\r
+\r
+\r
+#if defined(THREADED_RTS)\r
+\r
+#define EVENT_BUFSIZ 256\r
+Mutex event_buf_mutex;\r
+StgWord32 event_buf[EVENT_BUFSIZ];\r
+nat next_event;\r
+\r
+#endif\r
+\r
+StgWord32\r
+readIOManagerEvent (void)\r
+{\r
+    // This function must exist even in non-THREADED_RTS, \r
+    // see getIOManagerEvent() above.\r
+#if defined(THREADED_RTS)\r
+    StgWord32 res;\r
+\r
+    ACQUIRE_LOCK(&event_buf_mutex);\r
+    if (io_manager_event != INVALID_HANDLE_VALUE) {\r
+        if (next_event == 0) {\r
+            res = 0; // no event to return\r
+        } else {\r
+            res = event_buf[--next_event];\r
+            if (next_event == 0) {\r
+                if (!ResetEvent(io_manager_event)) {\r
+                    sysErrorBelch("readIOManagerEvent");\r
+                    stg_exit(EXIT_FAILURE);\r
+                }\r
+            }\r
+        }\r
+    } else {\r
+        res = 0;\r
+    }\r
+    RELEASE_LOCK(&event_buf_mutex);\r
+    // debugBelch("readIOManagerEvent: %d\n", res);\r
+    return res;\r
+#else\r
+    return 0;\r
+#endif\r
+}\r
+\r
+void\r
+sendIOManagerEvent (StgWord32 event)\r
+{\r
+#if defined(THREADED_RTS)\r
+    // debugBelch("sendIOManagerEvent: %d\n", event);\r
+    ACQUIRE_LOCK(&event_buf_mutex);\r
+    if (io_manager_event != INVALID_HANDLE_VALUE) {\r
+        if (next_event == EVENT_BUFSIZ) {\r
+            errorBelch("event buffer overflowed; event dropped");\r
+        } else {\r
+            if (!SetEvent(io_manager_event)) {\r
+                sysErrorBelch("sendIOManagerEvent");\r
+                stg_exit(EXIT_FAILURE);\r
+            }        \r
+            event_buf[next_event++] = event;\r
+        }\r
+    }\r
+    RELEASE_LOCK(&event_buf_mutex);\r
+#endif\r
+}    \r
+\r
+#if defined(THREADED_RTS)\r
+void\r
+ioManagerWakeup (void)\r
+{\r
+    sendIOManagerEvent(IO_MANAGER_WAKEUP);\r
+}\r
+\r
+void\r
+ioManagerDie (void)\r
+{\r
+    sendIOManagerEvent(IO_MANAGER_DIE);\r
+    // ToDo: wait for the IO manager to pick up the event, and\r
+    // then release the Event and Mutex objects we've allocated.\r
+}\r
+\r
+void\r
+ioManagerStart (void)\r
+{\r
+    initMutex(&event_buf_mutex);\r
+    next_event = 0;\r
+\r
+    // Make sure the IO manager thread is running\r
+    Capability *cap;\r
+    if (io_manager_event == INVALID_HANDLE_VALUE) {\r
+       cap = rts_lock();\r
+       rts_evalIO(cap,&base_GHCziConc_ensureIOManagerIsRunning_closure,NULL);\r
+       rts_unlock(cap);\r
+    }\r
+}\r
+#endif\r