summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog17
-rw-r--r--win/tclWinConsole.c175
-rw-r--r--win/tclWinPipe.c112
-rw-r--r--win/tclWinSerial.c126
-rw-r--r--win/tclWinSock.c4
-rw-r--r--win/tclWinThrd.c4
-rw-r--r--win/tclWinTime.c4
7 files changed, 314 insertions, 128 deletions
diff --git a/ChangeLog b/ChangeLog
index 965f3d0..57a3b20 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2002-11-26 David Gravereaux <davygrvy@pobox.com>
+
+ * win/tclWinConsole.c:
+ * win/tclWinPipe.c:
+ * win/tclWinSerial.c:
+ * win/tclWinSock.c:
+ * win/tclWinThrd.c:
+ * win/tclWinTime.c: General cleanup of all worker threads used
+ by the channel drivers. Eliminates the normal case where the
+ worker thread is terminated ('cept the winsock one). Instead,
+ use kernel events to signal a clean exit. Only when the worker
+ thread is blocked on an I/O call is the thread terminated.
+ Essentially, this makes all other channel worker threads behave
+ like the PipeReaderThread() function for it's cleaner exit
+ behavior. This appears to fix [Bug 597924] but needs 3rd party
+ confirmation to close the issue.
+
2002-11-26 Mo DeJong <mdejong@users.sourceforge.net>
* win/README: Update msys build env URL. This
diff --git a/win/tclWinConsole.c b/win/tclWinConsole.c
index 230a20e..7bb406b 100644
--- a/win/tclWinConsole.c
+++ b/win/tclWinConsole.c
@@ -9,7 +9,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWinConsole.c,v 1.9 2002/11/07 02:13:37 mdejong Exp $
+ * RCS: @(#) $Id: tclWinConsole.c,v 1.10 2002/11/26 22:35:20 davygrvy Exp $
*/
#include "tclWinInt.h"
@@ -79,9 +79,15 @@ typedef struct ConsoleInfo {
HANDLE startWriter; /* Auto-reset event used by the main thread to
* signal when the writer thread should attempt
* to write to the console. */
+ HANDLE stopWriter; /* Auto-reset event used by the main thread to
+ * signal when the writer thread should exit.
+ */
HANDLE startReader; /* Auto-reset event used by the main thread to
* signal when the reader thread should attempt
* to read from the console. */
+ HANDLE stopReader; /* Auto-reset event used by the main thread to
+ * signal when the reader thread should exit.
+ */
DWORD writeError; /* An error caused by the last background
* write. Set to 0 if no error has been
* detected. This word is shared with the
@@ -458,6 +464,7 @@ ConsoleCloseProc(
int errorCode;
ConsoleInfo *infoPtr, **nextPtrPtr;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ DWORD exitCode;
errorCode = 0;
@@ -468,30 +475,50 @@ ConsoleCloseProc(
*/
if (consolePtr->readThread) {
+
/*
- * Forcibly terminate the background thread. We cannot rely on the
- * thread to cleanly terminate itself because we have no way of
- * closing the handle without blocking in the case where the
- * thread is in the middle of an I/O operation. 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.
+ * The thread may already have closed on it's own. Check it's
+ * exit code.
*/
- Tcl_MutexLock(&consoleMutex);
- TerminateThread(consolePtr->readThread, 0);
+ GetExitCodeThread(consolePtr->readThread, &exitCode);
- /*
- * Wait for the thread to terminate. This ensures that we are
- * completely cleaned up before we leave this function.
- */
+ if (exitCode == STILL_ACTIVE) {
- WaitForSingleObject(consolePtr->readThread, INFINITE);
- Tcl_MutexUnlock(&consoleMutex);
+ /*
+ * Set the stop event so that if the reader thread is blocked
+ * in ConsoleReaderThread on WaitForMultipleEvents, it will exit
+ * cleanly.
+ */
+
+ SetEvent(consolePtr->stopReader);
+
+ /*
+ * Wait at most 20 milliseconds for the reader thread to close.
+ */
+
+ if (WaitForSingleObject(consolePtr->readThread, 20)
+ == WAIT_TIMEOUT) {
+ /*
+ * Forcibly terminate the background thread as a last
+ * resort. 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.
+ */
+
+ Tcl_MutexLock(&consoleMutex);
+
+ /* BUG: this leaks memory. */
+ TerminateThread(consolePtr->readThread, 0);
+ Tcl_MutexUnlock(&consoleMutex);
+ }
+ }
CloseHandle(consolePtr->readThread);
CloseHandle(consolePtr->readable);
CloseHandle(consolePtr->startReader);
+ CloseHandle(consolePtr->stopReader);
consolePtr->readThread = NULL;
}
consolePtr->validMask &= ~TCL_READABLE;
@@ -512,29 +539,45 @@ ConsoleCloseProc(
}
/*
- * Forcibly terminate the background thread. We cannot rely on the
- * thread to cleanly terminate itself because we have no way of
- * closing the handle without blocking in the case where the
- * thread is in the middle of an I/O operation. 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.
+ * The thread may already have closed on it's own. Check it's
+ * exit code.
*/
- Tcl_MutexLock(&consoleMutex);
- TerminateThread(consolePtr->writeThread, 0);
+ GetExitCodeThread(consolePtr->writeThread, &exitCode);
- /*
- * Wait for the thread to terminate. This ensures that we are
- * completely cleaned up before we leave this function.
- */
+ if (exitCode == STILL_ACTIVE) {
+ /*
+ * Set the stop event so that if the reader thread is blocked
+ * in ConsoleWriterThread on WaitForMultipleEvents, it will
+ * exit cleanly.
+ */
- WaitForSingleObject(consolePtr->writeThread, INFINITE);
- Tcl_MutexUnlock(&consoleMutex);
+ SetEvent(consolePtr->stopWriter);
+
+ /*
+ * Wait at most 20 milliseconds for the writer thread to close.
+ */
+
+ if (WaitForSingleObject(consolePtr->writeThread, 20)
+ == WAIT_TIMEOUT) {
+ /*
+ * Forcibly terminate the background thread as a last
+ * resort. 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.
+ */
+
+ Tcl_MutexLock(&consoleMutex);
+ TerminateThread(consolePtr->writeThread, 0);
+ Tcl_MutexUnlock(&consoleMutex);
+ }
+ }
CloseHandle(consolePtr->writeThread);
CloseHandle(consolePtr->writable);
CloseHandle(consolePtr->startWriter);
+ CloseHandle(consolePtr->stopWriter);
consolePtr->writeThread = NULL;
}
consolePtr->validMask &= ~TCL_WRITABLE;
@@ -1066,14 +1109,28 @@ ConsoleReaderThread(LPVOID arg)
{
ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
HANDLE *handle = infoPtr->handle;
- DWORD count;
+ DWORD count, waitResult;
+ HANDLE wEvents[2];
+
+ /* The first event takes precedence. */
+ wEvents[0] = infoPtr->stopReader;
+ wEvents[1] = infoPtr->startReader;
for (;;) {
/*
* Wait for the main thread to signal before attempting to wait.
*/
- WaitForSingleObject(infoPtr->startReader, INFINITE);
+ waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
+
+ if (waitResult != (WAIT_OBJECT_0 + 1)) {
+ /*
+ * The start event was not signaled. It must be the stop event
+ * or an error, so exit this thread.
+ */
+
+ break;
+ }
count = 0;
@@ -1081,7 +1138,7 @@ ConsoleReaderThread(LPVOID arg)
* Look for data on the console, but first ignore any events
* that are not KEY_EVENTs
*/
- if (ReadConsole(handle, infoPtr->buffer, CONSOLE_BUFFER_SIZE,
+ if (ReadConsoleA(handle, infoPtr->buffer, CONSOLE_BUFFER_SIZE,
(LPDWORD) &infoPtr->bytesRead, NULL) != FALSE) {
/*
* Data was stored in the buffer.
@@ -1114,7 +1171,8 @@ ConsoleReaderThread(LPVOID arg)
Tcl_ThreadAlert(infoPtr->threadId);
Tcl_MutexUnlock(&consoleMutex);
}
- return 0; /* NOT REACHED */
+
+ return 0;
}
/*
@@ -1141,15 +1199,29 @@ ConsoleWriterThread(LPVOID arg)
ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
HANDLE *handle = infoPtr->handle;
- DWORD count, toWrite;
+ DWORD count, toWrite, waitResult;
char *buf;
+ HANDLE wEvents[2];
+
+ /* The first event takes precedence. */
+ wEvents[0] = infoPtr->stopWriter;
+ wEvents[1] = infoPtr->startWriter;
for (;;) {
/*
* Wait for the main thread to signal before attempting to write.
*/
- WaitForSingleObject(infoPtr->startWriter, INFINITE);
+ waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
+
+ if (waitResult != (WAIT_OBJECT_0 + 1)) {
+ /*
+ * The start event was not signaled. It must be the stop event
+ * or an error, so exit this thread.
+ */
+
+ break;
+ }
buf = infoPtr->writeBuf;
toWrite = infoPtr->toWrite;
@@ -1159,7 +1231,7 @@ ConsoleWriterThread(LPVOID arg)
*/
while (toWrite > 0) {
- if (WriteFile(handle, buf, toWrite, &count, NULL) == FALSE) {
+ if (WriteConsoleA(handle, buf, toWrite, &count, NULL) == FALSE) {
infoPtr->writeError = GetLastError();
break;
} else {
@@ -1185,7 +1257,8 @@ ConsoleWriterThread(LPVOID arg)
Tcl_ThreadAlert(infoPtr->threadId);
Tcl_MutexUnlock(&consoleMutex);
}
- return 0; /* NOT REACHED */
+
+ return 0;
}
@@ -1217,7 +1290,7 @@ TclWinOpenConsoleChannel(handle, channelName, permissions)
char encoding[4 + TCL_INTEGER_SPACE];
ConsoleInfo *infoPtr;
ThreadSpecificData *tsdPtr;
- DWORD id;
+ DWORD id, modes;
tsdPtr = ConsoleInit();
@@ -1232,7 +1305,7 @@ TclWinOpenConsoleChannel(handle, channelName, permissions)
infoPtr->handle = handle;
wsprintfA(encoding, "cp%d", GetConsoleCP());
-
+
/*
* Use the pointer for the name of the result channel.
* This keeps the channel names unique, since some may share
@@ -1247,18 +1320,32 @@ TclWinOpenConsoleChannel(handle, channelName, permissions)
infoPtr->threadId = Tcl_GetCurrentThread();
if (permissions & TCL_READABLE) {
+ /*
+ * Make sure the console input buffer is ready for only character
+ * input notifications and the buffer is set for line buffering.
+ * IOW, we only want to catch when complete lines are ready for
+ * reading.
+ */
+ GetConsoleMode(infoPtr->handle, &modes);
+ modes &= ~(ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
+ modes |= ENABLE_LINE_INPUT;
+ SetConsoleMode(infoPtr->handle, modes);
+
infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL);
infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL);
- infoPtr->readThread = CreateThread(NULL, 8000, ConsoleReaderThread,
+ infoPtr->stopReader = CreateEvent(NULL, FALSE, FALSE, NULL);
+ infoPtr->readThread = CreateThread(NULL, 256, ConsoleReaderThread,
infoPtr, 0, &id);
- SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST);
+ SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST);
}
if (permissions & TCL_WRITABLE) {
infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL);
infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
- infoPtr->writeThread = CreateThread(NULL, 8000, ConsoleWriterThread,
+ infoPtr->stopWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
+ infoPtr->writeThread = CreateThread(NULL, 256, ConsoleWriterThread,
infoPtr, 0, &id);
+ SetThreadPriority(infoPtr->writeThread, THREAD_PRIORITY_HIGHEST);
}
/*
diff --git a/win/tclWinPipe.c b/win/tclWinPipe.c
index 2134d8d..8a6fc59 100644
--- a/win/tclWinPipe.c
+++ b/win/tclWinPipe.c
@@ -9,7 +9,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWinPipe.c,v 1.26 2002/11/07 02:13:37 mdejong Exp $
+ * RCS: @(#) $Id: tclWinPipe.c,v 1.27 2002/11/26 22:35:20 davygrvy Exp $
*/
#include "tclWinInt.h"
@@ -120,6 +120,8 @@ typedef struct PipeInfo {
HANDLE startWriter; /* Auto-reset event used by the main thread to
* signal when the writer thread should attempt
* to write to the pipe. */
+ HANDLE stopWriter; /* Manual-reset event used to alert the reader
+ * thread to fall-out and exit */
HANDLE startReader; /* Auto-reset event used by the main thread to
* signal when the reader thread should attempt
* to read from the pipe. */
@@ -1685,7 +1687,7 @@ TclpCreateCommandChannel(
infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL);
infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL);
infoPtr->stopReader = CreateEvent(NULL, TRUE, FALSE, NULL);
- infoPtr->readThread = CreateThread(NULL, 512, PipeReaderThread,
+ infoPtr->readThread = CreateThread(NULL, 256, PipeReaderThread,
infoPtr, 0, &id);
SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST);
infoPtr->validMask |= TCL_READABLE;
@@ -1699,7 +1701,8 @@ TclpCreateCommandChannel(
infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL);
infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
- infoPtr->writeThread = CreateThread(NULL, 512, PipeWriterThread,
+ infoPtr->stopWriter = CreateEvent(NULL, TRUE, FALSE, NULL);
+ infoPtr->writeThread = CreateThread(NULL, 256, PipeWriterThread,
infoPtr, 0, &id);
SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST);
infoPtr->validMask |= TCL_WRITABLE;
@@ -1873,13 +1876,11 @@ PipeClose2Proc(
SetEvent(pipePtr->stopReader);
/*
- * Wait at most 10 milliseconds for the reader thread to close.
+ * Wait at most 20 milliseconds for the reader thread to close.
*/
- WaitForSingleObject(pipePtr->readThread, 10);
- GetExitCodeThread(pipePtr->readThread, &exitCode);
-
- if (exitCode == STILL_ACTIVE) {
+ if (WaitForSingleObject(pipePtr->readThread, 20)
+ == WAIT_TIMEOUT) {
/*
* The thread must be blocked waiting for the pipe to
* become readable in ReadFile(). There isn't a clean way
@@ -1898,10 +1899,6 @@ PipeClose2Proc(
/* BUG: this leaks memory */
TerminateThread(pipePtr->readThread, 0);
-
- /* Wait for the thread to terminate. */
- WaitForSingleObject(pipePtr->readThread, INFINITE);
-
Tcl_MutexUnlock(&pipeMutex);
}
}
@@ -1920,40 +1917,65 @@ PipeClose2Proc(
}
if ((!flags || (flags & TCL_CLOSE_WRITE))
&& (pipePtr->writeFile != NULL)) {
- /*
- * Wait for the writer thread to finish the current buffer, then
- * terminate the thread and close the handles. If the channel is
- * nonblocking, there should be no pending write operations.
- */
if (pipePtr->writeThread) {
- WaitForSingleObject(pipePtr->writable, INFINITE);
-
/*
- * Forcibly terminate the background thread. We cannot rely on the
- * thread to cleanly terminate itself because we have no way of
- * closing the pipe handle without blocking in the case where the
- * thread is in the middle of an I/O operation. 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.
+ * Wait for the writer thread to finish the current buffer,
+ * then terminate the thread and close the handles. If the
+ * channel is nonblocking, there should be no pending write
+ * operations.
*/
- Tcl_MutexLock(&pipeMutex);
- TerminateThread(pipePtr->writeThread, 0);
+ WaitForSingleObject(pipePtr->writable, INFINITE);
/*
- * Wait for the thread to terminate. This ensures that we are
- * completely cleaned up before we leave this function.
+ * The thread may already have closed on it's own. Check it's
+ * exit code.
*/
- WaitForSingleObject(pipePtr->writeThread, INFINITE);
- Tcl_MutexUnlock(&pipeMutex);
+ GetExitCodeThread(pipePtr->writeThread, &exitCode);
+
+ if (exitCode == STILL_ACTIVE) {
+ /*
+ * Set the stop event so that if the reader thread is blocked
+ * in PipeReaderThread on WaitForMultipleEvents, it will exit
+ * cleanly.
+ */
+
+ SetEvent(pipePtr->stopWriter);
+
+ /*
+ * Wait at most 20 milliseconds for the reader thread to close.
+ */
+
+ if (WaitForSingleObject(pipePtr->writeThread, 20)
+ == WAIT_TIMEOUT) {
+ /*
+ * The thread must be blocked waiting for the pipe to
+ * consume input in WriteFile(). There isn't a clean way
+ * to exit the thread from this condition. We should
+ * terminate the child process instead to get the writer
+ * thread to fall out of WriteFile with a FALSE. (below) is
+ * not the correct way to do this, but will stay here until
+ * a better solution is found.
+ *
+ * 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.
+ */
+ Tcl_MutexLock(&pipeMutex);
+
+ /* BUG: this leaks memory */
+ TerminateThread(pipePtr->writeThread, 0);
+ Tcl_MutexUnlock(&pipeMutex);
+ }
+ }
CloseHandle(pipePtr->writeThread);
CloseHandle(pipePtr->writable);
CloseHandle(pipePtr->startWriter);
+ CloseHandle(pipePtr->stopWriter);
pipePtr->writeThread = NULL;
}
if (TclpCloseFile(pipePtr->writeFile) != 0) {
@@ -2745,7 +2767,7 @@ PipeReaderThread(LPVOID arg)
DWORD count, err;
int done = 0;
HANDLE wEvents[2];
- DWORD dwWait;
+ DWORD waitResult;
wEvents[0] = infoPtr->stopReader;
wEvents[1] = infoPtr->startReader;
@@ -2756,15 +2778,15 @@ PipeReaderThread(LPVOID arg)
* on the pipe becoming readable.
*/
- dwWait = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
+ waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
- if (dwWait != (WAIT_OBJECT_0 + 1)) {
+ if (waitResult != (WAIT_OBJECT_0 + 1)) {
/*
* The start event was not signaled. It might be the stop event
* or an error, so exit.
*/
- return 0;
+ break;
}
/*
@@ -2832,6 +2854,7 @@ PipeReaderThread(LPVOID arg)
Tcl_ThreadAlert(infoPtr->threadId);
Tcl_MutexUnlock(&pipeMutex);
}
+
return 0;
}
@@ -2862,13 +2885,27 @@ PipeWriterThread(LPVOID arg)
DWORD count, toWrite;
char *buf;
int done = 0;
+ HANDLE wEvents[2];
+ DWORD waitResult;
+
+ wEvents[0] = infoPtr->stopWriter;
+ wEvents[1] = infoPtr->startWriter;
while (!done) {
/*
* Wait for the main thread to signal before attempting to write.
*/
- WaitForSingleObject(infoPtr->startWriter, INFINITE);
+ waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
+
+ if (waitResult != (WAIT_OBJECT_0 + 1)) {
+ /*
+ * The start event was not signaled. It might be the stop event
+ * or an error, so exit.
+ */
+
+ break;
+ }
buf = infoPtr->writeBuf;
toWrite = infoPtr->toWrite;
@@ -2905,6 +2942,7 @@ PipeWriterThread(LPVOID arg)
Tcl_ThreadAlert(infoPtr->threadId);
Tcl_MutexUnlock(&pipeMutex);
}
+
return 0;
}
diff --git a/win/tclWinSerial.c b/win/tclWinSerial.c
index a1cdcfd..e6781ae 100644
--- a/win/tclWinSerial.c
+++ b/win/tclWinSerial.c
@@ -11,7 +11,7 @@
*
* Serial functionality implemented by Rolf.Schroedter@dlr.de
*
- * RCS: @(#) $Id: tclWinSerial.c,v 1.22 2002/11/07 02:13:37 mdejong Exp $
+ * RCS: @(#) $Id: tclWinSerial.c,v 1.23 2002/11/26 22:35:20 davygrvy Exp $
*/
#include "tclWinInt.h"
@@ -103,6 +103,9 @@ typedef struct SerialInfo {
HANDLE evStartWriter; /* Auto-reset event used by the main thread to
* signal when the writer thread should attempt
* to write to the serial. */
+ HANDLE evStopWriter; /* Auto-reset event used by the main thread to
+ * signal when the writer thread should close.
+ */
DWORD writeError; /* An error caused by the last background
* write. Set to 0 if no error has been
* detected. This word is shared with the
@@ -585,6 +588,7 @@ SerialCloseProc(
int errorCode, result = 0;
SerialInfo *infoPtr, **nextPtrPtr;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ DWORD exitCode;
errorCode = 0;
@@ -600,30 +604,52 @@ SerialCloseProc(
* Generally we cannot wait for a pending write operation
* because it may hang due to handshake
* WaitForSingleObject(serialPtr->evWritable, INFINITE);
- */
- /*
- * Forcibly terminate the background thread. We cannot rely on the
- * thread to cleanly terminate itself because we have no way of
- * closing the handle without blocking in the case where the
- * thread is in the middle of an I/O operation. 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.
*/
- Tcl_MutexLock(&serialMutex);
- TerminateThread(serialPtr->writeThread, 0);
- Tcl_MutexUnlock(&serialMutex);
+ /*
+ * The thread may have already closed on it's own. Check it's
+ * exit code.
+ */
- /*
- * Wait for the thread to terminate. This ensures that we are
- * completely cleaned up before we leave this function.
- */
+ GetExitCodeThread(serialPtr->writeThread, &exitCode);
+
+ if (exitCode == STILL_ACTIVE) {
+ /*
+ * Set the stop event so that if the writer thread is
+ * blocked in SerialWriterThread on WaitForMultipleEvents, it
+ * will exit cleanly.
+ */
+
+ SetEvent(serialPtr->evStopWriter);
+
+ /*
+ * Wait at most 20 milliseconds for the writer thread to
+ * close.
+ */
+
+ if (WaitForSingleObject(serialPtr->writeThread, 20)
+ == WAIT_TIMEOUT) {
+ /*
+ * Forcibly terminate the background thread as a last
+ * resort. 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.
+ */
+
+ Tcl_MutexLock(&serialMutex);
+
+ /* BUG: this leaks memory */
+ TerminateThread(serialPtr->writeThread, 0);
+
+ Tcl_MutexUnlock(&serialMutex);
+ }
+ }
- WaitForSingleObject(serialPtr->writeThread, INFINITE);
CloseHandle(serialPtr->writeThread);
CloseHandle(serialPtr->evWritable);
CloseHandle(serialPtr->evStartWriter);
+ CloseHandle(serialPtr->evStopWriter);
serialPtr->writeThread = NULL;
PurgeComm(serialPtr->handle, PURGE_TXABORT | PURGE_TXCLEAR);
@@ -637,13 +663,13 @@ SerialCloseProc(
*/
if (!TclInThreadExit()
- || ((GetStdHandle(STD_INPUT_HANDLE) != serialPtr->handle)
- && (GetStdHandle(STD_OUTPUT_HANDLE) != serialPtr->handle)
- && (GetStdHandle(STD_ERROR_HANDLE) != serialPtr->handle))) {
- if (CloseHandle(serialPtr->handle) == FALSE) {
- TclWinConvertError(GetLastError());
- errorCode = errno;
- }
+ || ((GetStdHandle(STD_INPUT_HANDLE) != serialPtr->handle)
+ && (GetStdHandle(STD_OUTPUT_HANDLE) != serialPtr->handle)
+ && (GetStdHandle(STD_ERROR_HANDLE) != serialPtr->handle))) {
+ if (CloseHandle(serialPtr->handle) == FALSE) {
+ TclWinConvertError(GetLastError());
+ errorCode = errno;
+ }
}
serialPtr->watchMask &= serialPtr->validMask;
@@ -653,8 +679,8 @@ SerialCloseProc(
*/
for (nextPtrPtr = &(tsdPtr->firstSerialPtr), infoPtr = *nextPtrPtr;
- infoPtr != NULL;
- nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
+ infoPtr != NULL;
+ nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
if (infoPtr == (SerialInfo *)serialPtr) {
*nextPtrPtr = infoPtr->nextPtr;
break;
@@ -703,20 +729,20 @@ blockingRead(
LPOVERLAPPED osPtr ) /* OVERLAPPED structure */
{
/*
- * Perform overlapped blocking read.
- * 1. Reset the overlapped event
- * 2. Start overlapped read operation
- * 3. Wait for completion
- */
+ * Perform overlapped blocking read.
+ * 1. Reset the overlapped event
+ * 2. Start overlapped read operation
+ * 3. Wait for completion
+ */
- /*
- * Set Offset to ZERO, otherwise NT4.0 may report an error
- */
- osPtr->Offset = osPtr->OffsetHigh = 0;
+ /*
+ * Set Offset to ZERO, otherwise NT4.0 may report an error.
+ */
+ osPtr->Offset = osPtr->OffsetHigh = 0;
ResetEvent(osPtr->hEvent);
if (! ReadFile(infoPtr->handle, buf, bufSize, lpRead, osPtr) ) {
if (GetLastError() != ERROR_IO_PENDING) {
- /* ReadFile failed, but it isn't delayed. Report error */
+ /* ReadFile failed, but it isn't delayed. Report error. */
return FALSE;
} else {
/* Read is pending, wait for completion, timeout ? */
@@ -1247,16 +1273,32 @@ SerialWriterThread(LPVOID arg)
SerialInfo *infoPtr = (SerialInfo *)arg;
HANDLE *handle = infoPtr->handle;
- DWORD bytesWritten, toWrite;
+ DWORD bytesWritten, toWrite, waitResult;
char *buf;
OVERLAPPED myWrite; /* have an own OVERLAPPED in this thread */
+ HANDLE wEvents[2];
+
+ /*
+ * The stop event takes precedence by being first in the list.
+ */
+ wEvents[0] = infoPtr->evStopWriter;
+ wEvents[1] = infoPtr->evStartWriter;
for (;;) {
/*
* Wait for the main thread to signal before attempting to write.
*/
- WaitForSingleObject(infoPtr->evStartWriter, INFINITE);
+ waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
+
+ if (waitResult != (WAIT_OBJECT_0 + 1)) {
+ /*
+ * The start event was not signaled. It might be the stop event
+ * or an error, so exit.
+ */
+
+ break;
+ }
buf = infoPtr->writeBuf;
toWrite = infoPtr->toWrite;
@@ -1306,7 +1348,8 @@ SerialWriterThread(LPVOID arg)
Tcl_ThreadAlert(infoPtr->threadId);
Tcl_MutexUnlock(&serialMutex);
}
- return 0; /* NOT REACHED */
+
+ return 0;
}
@@ -1426,8 +1469,9 @@ TclWinOpenSerialChannel(handle, channelName, permissions)
infoPtr->osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
infoPtr->evWritable = CreateEvent(NULL, TRUE, TRUE, NULL);
infoPtr->evStartWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
+ infoPtr->evStopWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
InitializeCriticalSection(&infoPtr->csWrite);
- infoPtr->writeThread = CreateThread(NULL, 8000, SerialWriterThread,
+ infoPtr->writeThread = CreateThread(NULL, 256, SerialWriterThread,
infoPtr, 0, &id);
}
diff --git a/win/tclWinSock.c b/win/tclWinSock.c
index a5ffdfb..a520380 100644
--- a/win/tclWinSock.c
+++ b/win/tclWinSock.c
@@ -8,7 +8,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWinSock.c,v 1.26 2002/05/24 18:57:09 andreas_kupries Exp $
+ * RCS: @(#) $Id: tclWinSock.c,v 1.27 2002/11/26 22:35:20 davygrvy Exp $
*/
#include "tclWinInt.h"
@@ -435,7 +435,7 @@ InitSockets()
tsdPtr->readyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
tsdPtr->socketListLock = CreateEvent(NULL, FALSE, TRUE, NULL);
- tsdPtr->socketThread = CreateThread(NULL, 8000, SocketThread,
+ tsdPtr->socketThread = CreateThread(NULL, 256, SocketThread,
tsdPtr, 0, &id);
SetThreadPriority(tsdPtr->socketThread, THREAD_PRIORITY_HIGHEST);
diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c
index cbef305..2f1d60e 100644
--- a/win/tclWinThrd.c
+++ b/win/tclWinThrd.c
@@ -9,7 +9,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWinThrd.c,v 1.21 2002/11/19 01:29:27 davygrvy Exp $
+ * RCS: @(#) $Id: tclWinThrd.c,v 1.22 2002/11/26 22:35:21 davygrvy Exp $
*/
#include "tclWinInt.h"
@@ -825,7 +825,7 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr)
/*
* Be careful on timeouts because the signal might arrive right around
- * time time limit and someone else could have taken us off the queue.
+ * the time limit and someone else could have taken us off the queue.
*/
if (timeout) {
diff --git a/win/tclWinTime.c b/win/tclWinTime.c
index 87d3c4e..13d1a4e 100644
--- a/win/tclWinTime.c
+++ b/win/tclWinTime.c
@@ -9,7 +9,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWinTime.c,v 1.11 2002/10/09 23:57:25 kennykb Exp $
+ * RCS: @(#) $Id: tclWinTime.c,v 1.12 2002/11/26 22:35:21 davygrvy Exp $
*/
#include "tclWinInt.h"
@@ -304,7 +304,7 @@ Tcl_GetTime(timePtr)
timeInfo.readyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
timeInfo.exitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
timeInfo.calibrationThread = CreateThread( NULL,
- 8192,
+ 256,
CalibrationThread,
(LPVOID) NULL,
0,