summaryrefslogtreecommitdiffstats
path: root/win/tclWinConsole.c
diff options
context:
space:
mode:
Diffstat (limited to 'win/tclWinConsole.c')
-rw-r--r--win/tclWinConsole.c53
1 files changed, 51 insertions, 2 deletions
diff --git a/win/tclWinConsole.c b/win/tclWinConsole.c
index ab55035..5c0b43b 100644
--- a/win/tclWinConsole.c
+++ b/win/tclWinConsole.c
@@ -51,6 +51,8 @@ TCL_DECLARE_MUTEX(consoleMutex)
typedef struct ConsoleThreadInfo {
HANDLE thread; /* Handle to reader or writer thread. */
+ HANDLE threadInitialized; /* Manual-reset event to signal that thread has been initialized. */
+ int threadExiting; /* Boolean indicating that thread is exiting. */
HANDLE readyEvent; /* Manual-reset event to signal _to_ the main
* thread when the worker thread has finished
* waiting for its normal work to happen. */
@@ -536,9 +538,12 @@ StartChannelThread(
threadInfoPtr->readyEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
threadInfoPtr->startEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ threadInfoPtr->threadInitialized = CreateEvent(NULL, TRUE, FALSE, NULL);
+ threadInfoPtr->threadExiting = FALSE;
threadInfoPtr->stopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
threadInfoPtr->thread = CreateThread(NULL, 256, threadProc, infoPtr, 0,
&id);
+ WaitForSingleObject(threadInfoPtr->threadInitialized, INFINITE); /* wait for thread to initialize */
SetThreadPriority(threadInfoPtr->thread, THREAD_PRIORITY_HIGHEST);
}
@@ -572,11 +577,28 @@ StopChannelThread(
* Note that we need to guard against terminating the thread while
* it is in the middle of Tcl_ThreadAlert because it won't be able
* to release the notifier lock.
+ *
+ * Also note that terminating threads during their initialization or teardown phase
+ * may result in ntdll.dll's LoaderLock to remain locked indefinitely.
+ * This causes ntdll.dll's LdrpInitializeThread() to deadlock trying to acquire LoaderLock.
+ * LdrpInitializeThread() is executed within new threads to perform
+ * initialization and to execute DllMain() of all loaded dlls.
+ * As a result, all new threads are deadlocked in their initialization phase and never execute,
+ * even though CreateThread() reports successful thread creation.
+ * This results in a very weird process-wide behavior, which is extremely hard to debug.
+ *
+ * THREADS SHOULD NEVER BE TERMINATED. Period.
+ *
+ * But for now, check if thread is exiting, and if so, let it die peacefully.
*/
Tcl_MutexLock(&consoleMutex);
- /* BUG: this leaks memory. */
- TerminateThread(threadInfoPtr->thread, 0);
+ if ( threadInfoPtr->threadExiting ) {
+ WaitForSingleObject(threadInfoPtr->thread, INFINITE);
+ } else {
+ /* BUG: this leaks memory. */
+ TerminateThread(threadInfoPtr->thread, 0);
+ }
Tcl_MutexUnlock(&consoleMutex);
}
}
@@ -587,6 +609,7 @@ StopChannelThread(
*/
CloseHandle(threadInfoPtr->thread);
+ CloseHandle(threadInfoPtr->threadInitialized);
CloseHandle(threadInfoPtr->readyEvent);
CloseHandle(threadInfoPtr->startEvent);
CloseHandle(threadInfoPtr->stopEvent);
@@ -1186,6 +1209,11 @@ ConsoleReaderThread(
HANDLE wEvents[2];
/*
+ * Notify StartChannelThread() that this thread is initialized
+ */
+ SetEvent(threadInfo->threadInitialized);
+
+ /*
* The first event takes precedence.
*/
@@ -1253,6 +1281,14 @@ ConsoleReaderThread(
Tcl_MutexUnlock(&consoleMutex);
}
+ /*
+ * Inform StopChannelThread() that this thread should not be terminated, since it is about to exit.
+ * See comment in StopChannelThread() for reasons.
+ */
+ Tcl_MutexLock(&consoleMutex);
+ threadInfo->threadExiting = TRUE;
+ Tcl_MutexUnlock(&consoleMutex);
+
return 0;
}
@@ -1287,6 +1323,11 @@ ConsoleWriterThread(
HANDLE wEvents[2];
/*
+ * Notify StartChannelThread() that this thread is initialized
+ */
+ SetEvent(threadInfo->threadInitialized);
+
+ /*
* The first event takes precedence.
*/
@@ -1351,6 +1392,14 @@ ConsoleWriterThread(
Tcl_MutexUnlock(&consoleMutex);
}
+ /*
+ * Inform StopChannelThread() that this thread should not be terminated, since it is about to exit.
+ * See comment in StopChannelThread() for reasons.
+ */
+ Tcl_MutexLock(&consoleMutex);
+ threadInfo->threadExiting = TRUE;
+ Tcl_MutexUnlock(&consoleMutex);
+
return 0;
}