*** win/tclWinConsole.c 7 Nov 2002 02:13:37 -0000 1.9 --- win/tclWinConsole.c 21 Nov 2002 07:47:42 -0000 *************** *** 79,87 **** --- 79,93 ---- 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,463 **** --- 464,470 ---- int errorCode; ConsoleInfo *infoPtr, **nextPtrPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + DWORD exitCode; errorCode = 0; *************** *** 468,497 **** */ 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. */ ! Tcl_MutexLock(&consoleMutex); ! TerminateThread(consolePtr->readThread, 0); ! /* ! * Wait for the thread to terminate. This ensures that we are ! * completely cleaned up before we leave this function. ! */ ! WaitForSingleObject(consolePtr->readThread, INFINITE); ! Tcl_MutexUnlock(&consoleMutex); CloseHandle(consolePtr->readThread); CloseHandle(consolePtr->readable); CloseHandle(consolePtr->startReader); consolePtr->readThread = NULL; } consolePtr->validMask &= ~TCL_READABLE; --- 475,524 ---- */ if (consolePtr->readThread) { + /* ! * The thread may already have closed on it's own. Check it's ! * exit code. */ ! GetExitCodeThread(consolePtr->readThread, &exitCode); ! if (exitCode == STILL_ACTIVE) { ! /* ! * 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,540 **** } /* ! * 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(&consoleMutex); ! TerminateThread(consolePtr->writeThread, 0); ! /* ! * Wait for the thread to terminate. This ensures that we are ! * completely cleaned up before we leave this function. ! */ ! WaitForSingleObject(consolePtr->writeThread, INFINITE); ! Tcl_MutexUnlock(&consoleMutex); CloseHandle(consolePtr->writeThread); CloseHandle(consolePtr->writable); CloseHandle(consolePtr->startWriter); consolePtr->writeThread = NULL; } consolePtr->validMask &= ~TCL_WRITABLE; --- 539,583 ---- } /* ! * The thread may already have closed on it's own. Check it's ! * exit code. */ ! GetExitCodeThread(consolePtr->writeThread, &exitCode); ! if (exitCode == STILL_ACTIVE) { ! /* ! * Set the stop event so that if the reader thread is blocked ! * in ConsoleWriterThread on WaitForMultipleEvents, it will ! * exit cleanly. ! */ ! 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,1079 **** { ConsoleInfo *infoPtr = (ConsoleInfo *)arg; HANDLE *handle = infoPtr->handle; ! DWORD count; for (;;) { /* * Wait for the main thread to signal before attempting to wait. */ ! WaitForSingleObject(infoPtr->startReader, INFINITE); count = 0; --- 1109,1136 ---- { ConsoleInfo *infoPtr = (ConsoleInfo *)arg; HANDLE *handle = infoPtr->handle; ! 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. */ ! 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,1087 **** * Look for data on the console, but first ignore any events * that are not KEY_EVENTs */ ! if (ReadConsole(handle, infoPtr->buffer, CONSOLE_BUFFER_SIZE, (LPDWORD) &infoPtr->bytesRead, NULL) != FALSE) { /* * Data was stored in the buffer. --- 1138,1144 ---- * Look for data on the console, but first ignore any events * that are not KEY_EVENTs */ ! if (ReadConsoleA(handle, infoPtr->buffer, CONSOLE_BUFFER_SIZE, (LPDWORD) &infoPtr->bytesRead, NULL) != FALSE) { /* * Data was stored in the buffer. *************** *** 1114,1120 **** Tcl_ThreadAlert(infoPtr->threadId); Tcl_MutexUnlock(&consoleMutex); } ! return 0; /* NOT REACHED */ } /* --- 1171,1178 ---- Tcl_ThreadAlert(infoPtr->threadId); Tcl_MutexUnlock(&consoleMutex); } ! ! return 0; } /* *************** *** 1141,1155 **** ConsoleInfo *infoPtr = (ConsoleInfo *)arg; HANDLE *handle = infoPtr->handle; ! DWORD count, toWrite; char *buf; for (;;) { /* * Wait for the main thread to signal before attempting to write. */ ! WaitForSingleObject(infoPtr->startWriter, INFINITE); buf = infoPtr->writeBuf; toWrite = infoPtr->toWrite; --- 1199,1227 ---- ConsoleInfo *infoPtr = (ConsoleInfo *)arg; HANDLE *handle = infoPtr->handle; ! 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. */ ! 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,1165 **** */ while (toWrite > 0) { ! if (WriteFile(handle, buf, toWrite, &count, NULL) == FALSE) { infoPtr->writeError = GetLastError(); break; } else { --- 1231,1237 ---- */ while (toWrite > 0) { ! if (WriteConsoleA(handle, buf, toWrite, &count, NULL) == FALSE) { infoPtr->writeError = GetLastError(); break; } else { *************** *** 1185,1191 **** Tcl_ThreadAlert(infoPtr->threadId); Tcl_MutexUnlock(&consoleMutex); } ! return 0; /* NOT REACHED */ } --- 1257,1264 ---- Tcl_ThreadAlert(infoPtr->threadId); Tcl_MutexUnlock(&consoleMutex); } ! ! return 0; } *************** *** 1217,1223 **** char encoding[4 + TCL_INTEGER_SPACE]; ConsoleInfo *infoPtr; ThreadSpecificData *tsdPtr; ! DWORD id; tsdPtr = ConsoleInit(); --- 1290,1296 ---- char encoding[4 + TCL_INTEGER_SPACE]; ConsoleInfo *infoPtr; ThreadSpecificData *tsdPtr; ! DWORD id, modes; tsdPtr = ConsoleInit(); *************** *** 1232,1238 **** 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 --- 1305,1311 ---- 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,1264 **** infoPtr->threadId = Tcl_GetCurrentThread(); if (permissions & TCL_READABLE) { infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL); infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL); ! infoPtr->readThread = CreateThread(NULL, 8000, ConsoleReaderThread, infoPtr, 0, &id); ! 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, 0, &id); } /* --- 1320,1351 ---- 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->stopReader = CreateEvent(NULL, FALSE, FALSE, NULL); ! infoPtr->readThread = CreateThread(NULL, 256, ConsoleReaderThread, infoPtr, 0, &id); ! 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->stopWriter = CreateEvent(NULL, FALSE, FALSE, NULL); ! infoPtr->writeThread = CreateThread(NULL, 256, ConsoleWriterThread, infoPtr, 0, &id); + SetThreadPriority(infoPtr->writeThread, THREAD_PRIORITY_HIGHEST); } /* *** win/tclWinPipe.c 7 Nov 2002 02:13:37 -0000 1.26 --- win/tclWinPipe.c 21 Nov 2002 07:47:56 -0000 *************** *** 120,125 **** --- 120,127 ---- 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,1691 **** 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, 0, &id); SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST); infoPtr->validMask |= TCL_READABLE; --- 1687,1693 ---- 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, 256, PipeReaderThread, infoPtr, 0, &id); SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST); infoPtr->validMask |= TCL_READABLE; *************** *** 1699,1705 **** infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL); infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL); ! infoPtr->writeThread = CreateThread(NULL, 512, PipeWriterThread, infoPtr, 0, &id); SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST); infoPtr->validMask |= TCL_WRITABLE; --- 1701,1708 ---- infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL); infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL); ! 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,1885 **** SetEvent(pipePtr->stopReader); /* ! * Wait at most 10 milliseconds for the reader thread to close. */ ! WaitForSingleObject(pipePtr->readThread, 10); ! GetExitCodeThread(pipePtr->readThread, &exitCode); ! ! if (exitCode == STILL_ACTIVE) { /* * The thread must be blocked waiting for the pipe to * become readable in ReadFile(). There isn't a clean way --- 1876,1886 ---- SetEvent(pipePtr->stopReader); /* ! * Wait at most 20 milliseconds for the reader thread to close. */ ! 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,1907 **** /* BUG: this leaks memory */ TerminateThread(pipePtr->readThread, 0); - - /* Wait for the thread to terminate. */ - WaitForSingleObject(pipePtr->readThread, INFINITE); - Tcl_MutexUnlock(&pipeMutex); } } --- 1899,1904 ---- *************** *** 1920,1959 **** } 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. */ ! Tcl_MutexLock(&pipeMutex); ! TerminateThread(pipePtr->writeThread, 0); /* ! * Wait for the thread to terminate. This ensures that we are ! * completely cleaned up before we leave this function. */ ! WaitForSingleObject(pipePtr->writeThread, INFINITE); ! Tcl_MutexUnlock(&pipeMutex); CloseHandle(pipePtr->writeThread); CloseHandle(pipePtr->writable); CloseHandle(pipePtr->startWriter); pipePtr->writeThread = NULL; } if (TclpCloseFile(pipePtr->writeFile) != 0) { --- 1917,1981 ---- } if ((!flags || (flags & TCL_CLOSE_WRITE)) && (pipePtr->writeFile != NULL)) { if (pipePtr->writeThread) { /* ! * 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. */ ! WaitForSingleObject(pipePtr->writable, INFINITE); /* ! * The thread may already have closed on it's own. Check it's ! * exit code. */ ! 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,2751 **** DWORD count, err; int done = 0; HANDLE wEvents[2]; ! DWORD dwWait; wEvents[0] = infoPtr->stopReader; wEvents[1] = infoPtr->startReader; --- 2767,2773 ---- DWORD count, err; int done = 0; HANDLE wEvents[2]; ! DWORD waitResult; wEvents[0] = infoPtr->stopReader; wEvents[1] = infoPtr->startReader; *************** *** 2756,2770 **** * on the pipe becoming readable. */ ! dwWait = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE); ! if (dwWait != (WAIT_OBJECT_0 + 1)) { /* * The start event was not signaled. It might be the stop event * or an error, so exit. */ ! return 0; } /* --- 2778,2792 ---- * on the pipe becoming readable. */ ! 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; } /* *************** *** 2832,2837 **** --- 2854,2860 ---- Tcl_ThreadAlert(infoPtr->threadId); Tcl_MutexUnlock(&pipeMutex); } + return 0; } *************** *** 2862,2874 **** DWORD count, toWrite; char *buf; int done = 0; while (!done) { /* * Wait for the main thread to signal before attempting to write. */ ! WaitForSingleObject(infoPtr->startWriter, INFINITE); buf = infoPtr->writeBuf; toWrite = infoPtr->toWrite; --- 2885,2911 ---- 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. */ ! 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,2910 **** --- 2942,2948 ---- Tcl_ThreadAlert(infoPtr->threadId); Tcl_MutexUnlock(&pipeMutex); } + return 0; } *** win/tclWinSerial.c 7 Nov 2002 02:13:37 -0000 1.22 --- win/tclWinSerial.c 21 Nov 2002 07:48:04 -0000 *************** *** 103,108 **** --- 103,111 ---- 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,590 **** --- 588,594 ---- int errorCode, result = 0; SerialInfo *infoPtr, **nextPtrPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + DWORD exitCode; errorCode = 0; *************** *** 600,629 **** * 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); ! ! /* ! * Wait for the thread to terminate. This ensures that we are ! * completely cleaned up before we leave this function. ! */ - WaitForSingleObject(serialPtr->writeThread, INFINITE); CloseHandle(serialPtr->writeThread); CloseHandle(serialPtr->evWritable); CloseHandle(serialPtr->evStartWriter); serialPtr->writeThread = NULL; PurgeComm(serialPtr->handle, PURGE_TXABORT | PURGE_TXCLEAR); --- 604,655 ---- * Generally we cannot wait for a pending write operation * because it may hang due to handshake * WaitForSingleObject(serialPtr->evWritable, INFINITE); */ ! /* ! * The thread may have already closed on it's own. Check it's ! * exit code. ! */ ! ! 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); ! } ! } CloseHandle(serialPtr->writeThread); CloseHandle(serialPtr->evWritable); CloseHandle(serialPtr->evStartWriter); + CloseHandle(serialPtr->evStopWriter); serialPtr->writeThread = NULL; PurgeComm(serialPtr->handle, PURGE_TXABORT | PURGE_TXCLEAR); *************** *** 637,649 **** */ 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; ! } } serialPtr->watchMask &= serialPtr->validMask; --- 663,675 ---- */ 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; ! } } serialPtr->watchMask &= serialPtr->validMask; *************** *** 653,660 **** */ for (nextPtrPtr = &(tsdPtr->firstSerialPtr), infoPtr = *nextPtrPtr; ! infoPtr != NULL; ! nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) { if (infoPtr == (SerialInfo *)serialPtr) { *nextPtrPtr = infoPtr->nextPtr; break; --- 679,686 ---- */ for (nextPtrPtr = &(tsdPtr->firstSerialPtr), infoPtr = *nextPtrPtr; ! infoPtr != NULL; ! nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) { if (infoPtr == (SerialInfo *)serialPtr) { *nextPtrPtr = infoPtr->nextPtr; break; *************** *** 703,722 **** LPOVERLAPPED osPtr ) /* OVERLAPPED structure */ { /* ! * 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; 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 */ return FALSE; } else { /* Read is pending, wait for completion, timeout ? */ --- 729,748 ---- LPOVERLAPPED osPtr ) /* OVERLAPPED structure */ { /* ! * 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; 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. */ return FALSE; } else { /* Read is pending, wait for completion, timeout ? */ *************** *** 1247,1262 **** SerialInfo *infoPtr = (SerialInfo *)arg; HANDLE *handle = infoPtr->handle; ! DWORD bytesWritten, toWrite; char *buf; OVERLAPPED myWrite; /* have an own OVERLAPPED in this thread */ for (;;) { /* * Wait for the main thread to signal before attempting to write. */ ! WaitForSingleObject(infoPtr->evStartWriter, INFINITE); buf = infoPtr->writeBuf; toWrite = infoPtr->toWrite; --- 1273,1304 ---- SerialInfo *infoPtr = (SerialInfo *)arg; HANDLE *handle = infoPtr->handle; ! 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. */ ! 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,1312 **** Tcl_ThreadAlert(infoPtr->threadId); Tcl_MutexUnlock(&serialMutex); } ! return 0; /* NOT REACHED */ } --- 1348,1355 ---- Tcl_ThreadAlert(infoPtr->threadId); Tcl_MutexUnlock(&serialMutex); } ! ! return 0; } *************** *** 1426,1433 **** infoPtr->osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); infoPtr->evWritable = CreateEvent(NULL, TRUE, TRUE, NULL); infoPtr->evStartWriter = CreateEvent(NULL, FALSE, FALSE, NULL); InitializeCriticalSection(&infoPtr->csWrite); ! infoPtr->writeThread = CreateThread(NULL, 8000, SerialWriterThread, infoPtr, 0, &id); } --- 1469,1477 ---- 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, 256, SerialWriterThread, infoPtr, 0, &id); } *** win/tclWinSock.c 24 May 2002 18:57:09 -0000 1.26 --- win/tclWinSock.c 21 Nov 2002 07:48:11 -0000 *************** *** 435,441 **** tsdPtr->readyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); tsdPtr->socketListLock = CreateEvent(NULL, FALSE, TRUE, NULL); ! tsdPtr->socketThread = CreateThread(NULL, 8000, SocketThread, tsdPtr, 0, &id); SetThreadPriority(tsdPtr->socketThread, THREAD_PRIORITY_HIGHEST); --- 438,444 ---- tsdPtr->readyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); tsdPtr->socketListLock = CreateEvent(NULL, FALSE, TRUE, NULL); ! tsdPtr->socketThread = CreateThread(NULL, 256, SocketThread, tsdPtr, 0, &id); SetThreadPriority(tsdPtr->socketThread, THREAD_PRIORITY_HIGHEST); *** win/tclWinThrd.c 19 Nov 2002 01:29:27 -0000 1.21 --- win/tclWinThrd.c 21 Nov 2002 07:48:20 -0000 *************** *** 825,831 **** /* * Be careful on timeouts because the signal might arrive right around ! * time time limit and someone else could have taken us off the queue. */ if (timeout) { --- 825,831 ---- /* * Be careful on timeouts because the signal might arrive right around ! * the time limit and someone else could have taken us off the queue. */ if (timeout) { *** win/tclWinTime.c 9 Oct 2002 23:57:25 -0000 1.11 --- win/tclWinTime.c 21 Nov 2002 07:48:24 -0000 *************** *** 304,310 **** timeInfo.readyEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); timeInfo.exitEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); timeInfo.calibrationThread = CreateThread( NULL, ! 8192, CalibrationThread, (LPVOID) NULL, 0, --- 304,310 ---- timeInfo.readyEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); timeInfo.exitEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); timeInfo.calibrationThread = CreateThread( NULL, ! 256, CalibrationThread, (LPVOID) NULL, 0,