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.
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);
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);
#include "STM.h" /* initSTM */
#include "Signals.h"
#include "RtsSignals.h"
+#include "ThrIOManager.h"
#include "Timer.h" /* startTimer, stopTimer */
#include "Weak.h"
#include "Ticky.h"
x86_init_fpu();
#endif
-#if defined(THREADED_RTS) && !defined(mingw32_HOST_OS)
+#if defined(THREADED_RTS)
ioManagerStart();
#endif
/* start timing the shutdown */
stat_startExit();
-#if defined(THREADED_RTS) && !defined(mingw32_HOST_OS)
+#if defined(THREADED_RTS)
ioManagerDie();
#endif
#include "Trace.h"
#include "RaiseAsync.h"
#include "Threads.h"
+#include "ThrIOManager.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
* 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)
{
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.
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
}
--- /dev/null
+/* -----------------------------------------------------------------------------\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
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 */
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;
StgInt stg_pending_events = 0; /* number of undelivered events */
DWORD stg_pending_buf[N_PENDING_EVENTS]; /* their associated event numbers. */
+#endif
+
/*
* Function: initUserSignals()
*
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 */
FALSE, /* initially non-signalled */
NULL); /* no name */
}
+#endif
return;
}
void
finiUserSignals(void)
{
+#if !defined (THREADED_RTS)
if (hConsoleEvent != INVALID_HANDLE_VALUE) {
CloseHandle(hConsoleEvent);
}
+#endif
}
/*
stg_exit(EXIT_INTERRUPTED);
} else {
interruptStgRts();
- /* Cheesy pulsing of an event to wake up a waiting RTS thread, if any */
- abandonRequestWait();
- resetAbandonRequestWait();
}
return TRUE;
}
+#if !defined (THREADED_RTS)
/*
* Function: startSignalHandlers()
*
RELEASE_LOCK(&sched_mutex);
unblockUserSignals();
}
+#endif /* !THREADED_RTS */
/*
* Function: markSignalHandlers()
*/
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:
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);
}
*
*/
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()
*
*/
return (WaitForSingleObject(hConsoleEvent, INFINITE) == WAIT_OBJECT_0);
}
+#endif
* 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.
*/
/*
*/
extern int rts_waitConsoleHandlerCompletion(void);
+#endif /* THREADED_RTS */
+
#endif /* __CONSOLEHANDLER_H__ */
--- /dev/null
+/* -----------------------------------------------------------------------------\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