summaryrefslogtreecommitdiffstats
path: root/win/tclWinConsole.c
diff options
context:
space:
mode:
authorsebres <sebres@users.sourceforge.net>2017-04-05 10:40:29 (GMT)
committersebres <sebres@users.sourceforge.net>2017-04-05 10:40:29 (GMT)
commit9b1d320daf1b34b63dca5889e205d63cc874912d (patch)
tree9181e72e81b06cb0cbd35df7aa75a2135d356576 /win/tclWinConsole.c
parent224849c744f0f6ceb853162afd318b858835850c (diff)
downloadtcl-9b1d320daf1b34b63dca5889e205d63cc874912d.zip
tcl-9b1d320daf1b34b63dca5889e205d63cc874912d.tar.gz
tcl-9b1d320daf1b34b63dca5889e205d63cc874912d.tar.bz2
Contributed by "stanko" as patch within 8bd13f07bde6fb0631f27927e36461fdefe8ca95
Resolves blocking of pipes-thread (reader/writer) under huge last: Terminating threads during their initialization resp. teardown phase may result LoaderLock in the ntdll.dll's (to remain locked indefinitely). This causes ntdll.dll's LdrpInitializeThread() to deadlock trying to acquire LoaderLock. Possible fix for 9d75181ee70af318830e99ede6ebb5df72a9b079
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;
}