From c0a30cfc05c2bb7dc8e9948c9869c54d847db951 Mon Sep 17 00:00:00 2001 From: davygrvy Date: Tue, 26 Nov 2002 22:35:19 +0000 Subject: * 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. --- ChangeLog | 17 +++++ win/tclWinConsole.c | 175 +++++++++++++++++++++++++++++++++++++++------------- win/tclWinPipe.c | 112 ++++++++++++++++++++++----------- win/tclWinSerial.c | 126 +++++++++++++++++++++++++------------ win/tclWinSock.c | 4 +- win/tclWinThrd.c | 4 +- win/tclWinTime.c | 4 +- 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 + + * 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 * 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, -- cgit v0.12