Tcl Source Code

Artifact [e381249b93]
Login

Artifact e381249b935119ea64710a989d0538c80b18c38d:

Attachment "patch.txt" to ticket [597924ffff] added by davygrvy 2002-11-21 15:13:04.
*** 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,