summaryrefslogtreecommitdiffstats
path: root/win/tclWinSerial.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/tclWinSerial.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/tclWinSerial.c')
-rw-r--r--win/tclWinSerial.c41
1 files changed, 38 insertions, 3 deletions
diff --git a/win/tclWinSerial.c b/win/tclWinSerial.c
index 0730a46..3c6a3cc 100644
--- a/win/tclWinSerial.c
+++ b/win/tclWinSerial.c
@@ -94,6 +94,8 @@ typedef struct SerialInfo {
OVERLAPPED osRead; /* OVERLAPPED structure for read operations. */
OVERLAPPED osWrite; /* OVERLAPPED structure for write operations */
HANDLE writeThread; /* Handle to writer thread. */
+ HANDLE writeThreadInitialized; /* Manual-reset event to signal that thread has been initialized. */
+ int writeThreadExiting; /* Boolean indicating that thread is exiting. */
CRITICAL_SECTION csWrite; /* Writer thread synchronisation. */
HANDLE evWritable; /* Manual-reset event to signal when the
* writer thread has finished waiting for the
@@ -643,18 +645,35 @@ SerialCloseProc(
* 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(&serialMutex);
- /* BUG: this leaks memory */
- TerminateThread(serialPtr->writeThread, 0);
-
+ if ( serialPtr->writeThreadExiting ) {
+ WaitForSingleObject(serialPtr->writeThread, INFINITE);
+ } else {
+ /* BUG: this leaks memory. */
+ TerminateThread(serialPtr->writeThread, 0);
+ }
Tcl_MutexUnlock(&serialMutex);
}
}
CloseHandle(serialPtr->writeThread);
+ CloseHandle(serialPtr->writeThreadInitialized);
CloseHandle(serialPtr->osWrite.hEvent);
CloseHandle(serialPtr->evWritable);
CloseHandle(serialPtr->evStartWriter);
@@ -1320,6 +1339,11 @@ SerialWriterThread(
HANDLE wEvents[2];
/*
+ * Notify TclWinOpenSerialChannel() that this thread is initialized
+ */
+ SetEvent(infoPtr->writeThreadInitialized);
+
+ /*
* The stop event takes precedence by being first in the list.
*/
@@ -1404,6 +1428,14 @@ SerialWriterThread(
Tcl_MutexUnlock(&serialMutex);
}
+ /*
+ * Inform SerialCloseProc() that this thread should not be terminated, since it is about to exit.
+ * See comment in SerialCloseProc() for reasons.
+ */
+ Tcl_MutexLock(&serialMutex);
+ infoPtr->writeThreadExiting = TRUE;
+ Tcl_MutexUnlock(&serialMutex);
+
return 0;
}
@@ -1531,8 +1563,11 @@ TclWinOpenSerialChannel(
infoPtr->evWritable = CreateEvent(NULL, TRUE, TRUE, NULL);
infoPtr->evStartWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
infoPtr->evStopWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
+ infoPtr->writeThreadInitialized = CreateEvent(NULL, TRUE, FALSE, NULL);
+ infoPtr->writeThreadExiting = FALSE;
infoPtr->writeThread = CreateThread(NULL, 256, SerialWriterThread,
infoPtr, 0, &id);
+ WaitForSingleObject(infoPtr->writeThreadInitialized, INFINITE); /* wait for thread to initialize */
}
/*