diff options
Diffstat (limited to 'win/tclWinConsole.c')
| -rw-r--r-- | win/tclWinConsole.c | 1007 | 
1 files changed, 567 insertions, 440 deletions
| diff --git a/win/tclWinConsole.c b/win/tclWinConsole.c index 6d3709c..6630083 100644 --- a/win/tclWinConsole.c +++ b/win/tclWinConsole.c @@ -1,23 +1,17 @@ -/*  +/*   * tclWinConsole.c --   * - *	This file implements the Windows-specific console functions, - *	and the "console" channel driver. + *	This file implements the Windows-specific console functions, and the + *	"console" channel driver.   *   * Copyright (c) 1999 by Scriptics Corp.   * - * 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.11 2002/11/26 22:41:58 davygrvy Exp $ + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES.   */  #include "tclWinInt.h" -#include <fcntl.h> -#include <io.h> -#include <sys/stat.h> -  /*   * The following variable is used to tell whether this module has been   * initialized. @@ -45,10 +39,28 @@ TCL_DECLARE_MUTEX(consoleMutex)   */  #define CONSOLE_EOF	  (1<<2)  /* Console has reached EOF. */ -#define CONSOLE_BUFFERED  (1<<3)  /* data was read into a buffer by the reader -				     thread */ +#define CONSOLE_BUFFERED  (1<<3)  /* Data was read into a buffer by the reader +				   * thread. */  #define CONSOLE_BUFFER_SIZE (8*1024) + +/* + * Structure containing handles associated with one of the special console + * threads. + */ + +typedef struct ConsoleThreadInfo { +    HANDLE thread;		/* Handle to reader or writer thread. */ +    HANDLE readyEvent;		/* Manual-reset event to signal _to_ the main +				 * thread when the worker thread has finished +				 * waiting for its normal work to happen. */ +    HANDLE startEvent;		/* Auto-reset event used by the main thread to +				 * signal when the thread should attempt to do +				 * its normal work. */ +    HANDLE stopEvent;		/* Auto-reset event used by the main thread to +				 * signal when the thread should exit. */ +} ConsoleThreadInfo; +  /*   * This structure describes per-instance data for a console based channel.   */ @@ -68,55 +80,44 @@ typedef struct ConsoleInfo {      Tcl_ThreadId threadId;	/* Thread to which events should be reported.  				 * This value is used by the reader/writer  				 * threads. */ -    HANDLE writeThread;		/* Handle to writer thread. */ -    HANDLE readThread;		/* Handle to reader thread. */ -    HANDLE writable;		/* Manual-reset event to signal when the -				 * writer thread has finished waiting for -				 * the current buffer to be written. */ -    HANDLE readable;		/* Manual-reset event to signal when the -				 * reader thread has finished waiting for -				 * input. */ -    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. -				 */ +    ConsoleThreadInfo writer;	/* A specialized thread for handling +				 * asynchronous writes to the console; the +				 * waiting starts when a start event is sent, +				 * and a reset event is sent back to the main +				 * thread when the write is done. A stop event +				 * is used to terminate the thread. */ +    ConsoleThreadInfo reader;	/* A specialized thread for handling +				 * asynchronous reads from the console; the +				 * waiting starts when a start event is sent, +				 * and a reset event is sent back to the main +				 * thread when input is available. A stop +				 * event is used to terminate the thread. */      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 +				 * write. Set to 0 if no error has been +				 * detected. This word is shared with the  				 * writer thread so access must be -				 * synchronized with the writable object. -				 */ -    char *writeBuf;		/* Current background output buffer. -				 * Access is synchronized with the writable -				 * object. */ -    int writeBufLen;		/* Size of write buffer.  Access is -				 * synchronized with the writable -				 * object. */ -    int toWrite;		/* Current amount to be written.  Access is +				 * synchronized with the writable object. */ +    char *writeBuf;		/* Current background output buffer. Access is +				 * synchronized with the writable object. */ +    int writeBufLen;		/* Size of write buffer. Access is +				 * synchronized with the writable object. */ +    int toWrite;		/* Current amount to be written. Access is  				 * synchronized with the writable object. */      int readFlags;		/* Flags that are shared with the reader -				 * thread.  Access is synchronized with the -				 * readable object.  */ -    int bytesRead;              /* number of bytes in the buffer */ -    int offset;                 /* number of bytes read out of the buffer */ +				 * thread. Access is synchronized with the +				 * readable object. */ +    int bytesRead;		/* Number of bytes in the buffer. */ +    int offset;			/* Number of bytes read out of the buffer. */      char buffer[CONSOLE_BUFFER_SIZE]; -                                /* Data consumed by reader thread. */ +				/* Data consumed by reader thread. */  } ConsoleInfo;  typedef struct ThreadSpecificData {      /* -     * The following pointer refers to the head of the list of consoles -     * that are being watched for file events. +     * The following pointer refers to the head of the list of consoles that +     * are being watched for file events.       */ -     +      ConsoleInfo *firstConsolePtr;  } ThreadSpecificData; @@ -128,9 +129,9 @@ static Tcl_ThreadDataKey dataKey;   */  typedef struct ConsoleEvent { -    Tcl_Event header;		/* Information that is standard for -				 * all events. */ -    ConsoleInfo *infoPtr;	/* Pointer to console info structure.  Note +    Tcl_Event header;		/* Information that is standard for all +				 * events. */ +    ConsoleInfo *infoPtr;	/* Pointer to console info structure. Note  				 * that we still have to verify that the  				 * console exists before dereferencing this  				 * pointer. */ @@ -140,7 +141,8 @@ typedef struct ConsoleEvent {   * Declarations for functions used only in this file.   */ -static int		ConsoleBlockModeProc(ClientData instanceData, int mode); +static int		ConsoleBlockModeProc(ClientData instanceData, +			    int mode);  static void		ConsoleCheckProc(ClientData clientData, int flags);  static int		ConsoleCloseProc(ClientData instanceData,  			    Tcl_Interp *interp); @@ -148,26 +150,37 @@ static int		ConsoleEventProc(Tcl_Event *evPtr, int flags);  static void		ConsoleExitHandler(ClientData clientData);  static int		ConsoleGetHandleProc(ClientData instanceData,  			    int direction, ClientData *handlePtr); -static ThreadSpecificData *ConsoleInit(void); +static void		ConsoleInit(void);  static int		ConsoleInputProc(ClientData instanceData, char *buf,  			    int toRead, int *errorCode);  static int		ConsoleOutputProc(ClientData instanceData, -			    CONST char *buf, int toWrite, int *errorCode); +			    const char *buf, int toWrite, int *errorCode);  static DWORD WINAPI	ConsoleReaderThread(LPVOID arg);  static void		ConsoleSetupProc(ClientData clientData, int flags);  static void		ConsoleWatchProc(ClientData instanceData, int mask);  static DWORD WINAPI	ConsoleWriterThread(LPVOID arg);  static void		ProcExitHandler(ClientData clientData);  static int		WaitForRead(ConsoleInfo *infoPtr, int blocking); +static void		ConsoleThreadActionProc(ClientData instanceData, +			    int action); +static BOOL		ReadConsoleBytes(HANDLE hConsole, LPVOID lpBuffer, +			    DWORD nbytes, LPDWORD nbytesread); +static BOOL		WriteConsoleBytes(HANDLE hConsole, +			    const void *lpBuffer, DWORD nbytes, +			    LPDWORD nbyteswritten); +static void		StartChannelThread(ConsoleInfo *infoPtr, +			    ConsoleThreadInfo *threadInfoPtr, +			    LPTHREAD_START_ROUTINE threadProc); +static void		StopChannelThread(ConsoleThreadInfo *threadInfoPtr);  /*   * This structure describes the channel type structure for command console   * based IO.   */ -static Tcl_ChannelType consoleChannelType = { +static const Tcl_ChannelType consoleChannelType = {      "console",			/* Type name. */ -    TCL_CHANNEL_VERSION_2,	/* v2 channel */ +    TCL_CHANNEL_VERSION_5,	/* v5 channel */      ConsoleCloseProc,		/* Close proc. */      ConsoleInputProc,		/* Input proc. */      ConsoleOutputProc,		/* Output proc. */ @@ -177,14 +190,66 @@ static Tcl_ChannelType consoleChannelType = {      ConsoleWatchProc,		/* Set up notifier to watch the channel. */      ConsoleGetHandleProc,	/* Get an OS handle from channel. */      NULL,			/* close2proc. */ -    ConsoleBlockModeProc,	/* Set blocking or non-blocking mode.*/ -    NULL,			/* flush proc. */ -    NULL,			/* handler proc. */ +    ConsoleBlockModeProc,	/* Set blocking or non-blocking mode. */ +    NULL,			/* Flush proc. */ +    NULL,			/* Handler proc. */ +    NULL,			/* Wide seek proc. */ +    ConsoleThreadActionProc,	/* Thread action proc. */ +    NULL			/* Truncation proc. */  };  /*   *----------------------------------------------------------------------   * + * ReadConsoleBytes, WriteConsoleBytes -- + * + *	Wrapper for ReadConsole{A,W}, that takes and returns number of bytes + *	instead of number of TCHARS. + * + *---------------------------------------------------------------------- + */ + +static BOOL +ReadConsoleBytes( +    HANDLE hConsole, +    LPVOID lpBuffer, +    DWORD nbytes, +    LPDWORD nbytesread) +{ +    DWORD ntchars; +    BOOL result; +    int tcharsize = sizeof(TCHAR); + +    result = ReadConsole(hConsole, lpBuffer, nbytes / tcharsize, &ntchars, +	    NULL); +    if (nbytesread != NULL) { +	*nbytesread = ntchars * tcharsize; +    } +    return result; +} + +static BOOL +WriteConsoleBytes( +    HANDLE hConsole, +    const void *lpBuffer, +    DWORD nbytes, +    LPDWORD nbyteswritten) +{ +    DWORD ntchars; +    BOOL result; +    int tcharsize = sizeof(TCHAR); + +    result = WriteConsole(hConsole, lpBuffer, nbytes / tcharsize, &ntchars, +	    NULL); +    if (nbyteswritten != NULL) { +	*nbyteswritten = ntchars * tcharsize; +    } +    return result; +} + +/* + *---------------------------------------------------------------------- + *   * ConsoleInit --   *   *	This function initializes the static variables for this file. @@ -198,14 +263,12 @@ static Tcl_ChannelType consoleChannelType = {   *----------------------------------------------------------------------   */ -static ThreadSpecificData * -ConsoleInit() +static void +ConsoleInit(void)  { -    ThreadSpecificData *tsdPtr; -      /* -     * Check the initialized flag first, then check again in the mutex. -     * This is a speed enhancement. +     * Check the initialized flag first, then check again in the mutex. This +     * is a speed enhancement.       */      if (!initialized) { @@ -217,14 +280,13 @@ ConsoleInit()  	Tcl_MutexUnlock(&consoleMutex);      } -    tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); -    if (tsdPtr == NULL) { -	tsdPtr = TCL_TSD_INIT(&dataKey); +    if (TclThreadDataKeyGet(&dataKey) == NULL) { +	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); +  	tsdPtr->firstConsolePtr = NULL;  	Tcl_CreateEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);  	Tcl_CreateThreadExitHandler(ConsoleExitHandler, NULL);      } -    return tsdPtr;  }  /* @@ -232,8 +294,8 @@ ConsoleInit()   *   * ConsoleExitHandler --   * - *	This function is called to cleanup the console module before - *	Tcl is unloaded. + *	This function is called to cleanup the console module before Tcl is + *	unloaded.   *   * Results:   *	None. @@ -246,7 +308,7 @@ ConsoleInit()  static void  ConsoleExitHandler( -    ClientData clientData)	/* Old window proc */ +    ClientData clientData)	/* Old window proc. */  {      Tcl_DeleteEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);  } @@ -256,8 +318,8 @@ ConsoleExitHandler(   *   * ProcExitHandler --   * - *	This function is called to cleanup the process list before - *	Tcl is unloaded. + *	This function is called to cleanup the process list before Tcl is + *	unloaded.   *   * Results:   *	None. @@ -270,7 +332,7 @@ ConsoleExitHandler(  static void  ProcExitHandler( -    ClientData clientData)	/* Old window proc */ +    ClientData clientData)	/* Old window proc. */  {      Tcl_MutexLock(&consoleMutex);      initialized = 0; @@ -282,8 +344,8 @@ ProcExitHandler(   *   * ConsoleSetupProc --   * - *	This procedure is invoked before Tcl_DoOneEvent blocks waiting - *	for an event. + *	This procedure is invoked before Tcl_DoOneEvent blocks waiting for an + *	event.   *   * Results:   *	None. @@ -307,15 +369,16 @@ ConsoleSetupProc(      if (!(flags & TCL_FILE_EVENTS)) {  	return;      } -     +      /* -     * Look to see if any events are already pending.  If they are, poll. +     * Look to see if any events are already pending. If they are, poll.       */ -    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;  +    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;  	    infoPtr = infoPtr->nextPtr) {  	if (infoPtr->watchMask & TCL_WRITABLE) { -	    if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) { +	    if (WaitForSingleObject(infoPtr->writer.readyEvent, +		    0) != WAIT_TIMEOUT) {  		block = 0;  	    }  	} @@ -335,8 +398,8 @@ ConsoleSetupProc(   *   * ConsoleCheckProc --   * - *	This procedure is called by Tcl_DoOneEvent to check the console - *	event source for events.  + *	This procedure is called by Tcl_DoOneEvent to check the console event + *	source for events.   *   * Results:   *	None. @@ -353,36 +416,36 @@ ConsoleCheckProc(      int flags)			/* Event flags as passed to Tcl_DoOneEvent. */  {      ConsoleInfo *infoPtr; -    ConsoleEvent *evPtr;      int needEvent;      ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);      if (!(flags & TCL_FILE_EVENTS)) {  	return;      } -     +      /*       * Queue events for any ready consoles that don't already have events       * queued.       */ -    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;  +    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;  	    infoPtr = infoPtr->nextPtr) {  	if (infoPtr->flags & CONSOLE_PENDING) {  	    continue;  	} -	 +  	/*  	 * Queue an event if the console is signaled for reading or writing.  	 */  	needEvent = 0;  	if (infoPtr->watchMask & TCL_WRITABLE) { -	    if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) { +	    if (WaitForSingleObject(infoPtr->writer.readyEvent, +		    0) != WAIT_TIMEOUT) {  		needEvent = 1;  	    }  	} -	 +  	if (infoPtr->watchMask & TCL_READABLE) {  	    if (WaitForRead(infoPtr, 0) >= 0) {  		needEvent = 1; @@ -390,8 +453,9 @@ ConsoleCheckProc(  	}  	if (needEvent) { +	    ConsoleEvent *evPtr = ckalloc(sizeof(ConsoleEvent)); +  	    infoPtr->flags |= CONSOLE_PENDING; -	    evPtr = (ConsoleEvent *) ckalloc(sizeof(ConsoleEvent));  	    evPtr->header.proc = ConsoleEventProc;  	    evPtr->infoPtr = infoPtr;  	    Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); @@ -399,7 +463,6 @@ ConsoleCheckProc(      }  } -  /*   *----------------------------------------------------------------------   * @@ -420,21 +483,22 @@ static int  ConsoleBlockModeProc(      ClientData instanceData,	/* Instance data for channel. */      int mode)			/* TCL_MODE_BLOCKING or -                                 * TCL_MODE_NONBLOCKING. */ +				 * TCL_MODE_NONBLOCKING. */  { -    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData; -     +    ConsoleInfo *infoPtr = instanceData; +      /* -     * Consoles on Windows can not be switched between blocking and nonblocking, -     * hence we have to emulate the behavior. This is done in the input -     * function by checking against a bit in the state. We set or unset the -     * bit here to cause the input function to emulate the correct behavior. +     * Consoles on Windows can not be switched between blocking and +     * nonblocking, hence we have to emulate the behavior. This is done in the +     * input function by checking against a bit in the state. We set or unset +     * the bit here to cause the input function to emulate the correct +     * behavior.       */      if (mode == TCL_MODE_NONBLOCKING) {  	infoPtr->flags |= CONSOLE_ASYNC;      } else { -	infoPtr->flags &= ~(CONSOLE_ASYNC); +	infoPtr->flags &= ~CONSOLE_ASYNC;      }      return 0;  } @@ -442,6 +506,84 @@ ConsoleBlockModeProc(  /*   *----------------------------------------------------------------------   * + * StartChannelThread, StopChannelThread -- + * + *	Helpers that codify how to ask one of the console service threads to + *	start and stop. + * + *---------------------------------------------------------------------- + */ + +static void +StartChannelThread( +    ConsoleInfo *infoPtr, +    ConsoleThreadInfo *threadInfoPtr, +    LPTHREAD_START_ROUTINE threadProc) +{ +    DWORD id; + +    threadInfoPtr->readyEvent = CreateEvent(NULL, TRUE, TRUE, NULL); +    threadInfoPtr->startEvent = CreateEvent(NULL, FALSE, FALSE, NULL); +    threadInfoPtr->stopEvent = CreateEvent(NULL, FALSE, FALSE, NULL); +    threadInfoPtr->thread = CreateThread(NULL, 256, threadProc, infoPtr, 0, +	    &id); +    SetThreadPriority(threadInfoPtr->thread, THREAD_PRIORITY_HIGHEST); +} + +static void +StopChannelThread( +    ConsoleThreadInfo *threadInfoPtr) +{ +    DWORD exitCode = 0; + +    /* +     * The thread may already have closed on it's own. Check it's exit +     * code. +     */ + +    GetExitCodeThread(threadInfoPtr->thread, &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(threadInfoPtr->stopEvent); + +	/* +	 * Wait at most 20 milliseconds for the reader thread to close. +	 */ + +	if (WaitForSingleObject(threadInfoPtr->thread, 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(threadInfoPtr->thread, 0); +	    Tcl_MutexUnlock(&consoleMutex); +	} +    } + +    /* +     * Close all the handles associated with the thread, and set the thread +     * handle field to NULL to mark that the thread has been cleaned up. +     */ + +    CloseHandle(threadInfoPtr->thread); +    CloseHandle(threadInfoPtr->readyEvent); +    CloseHandle(threadInfoPtr->startEvent); +    CloseHandle(threadInfoPtr->stopEvent); +    threadInfoPtr->thread = NULL; +} + +/* + *---------------------------------------------------------------------- + *   * ConsoleCloseProc --   *   *	Closes a console based IO channel. @@ -460,138 +602,49 @@ ConsoleCloseProc(      ClientData instanceData,	/* Pointer to ConsoleInfo structure. */      Tcl_Interp *interp)		/* For error reporting. */  { -    ConsoleInfo *consolePtr = (ConsoleInfo *) instanceData; -    int errorCode; +    ConsoleInfo *consolePtr = instanceData; +    int errorCode = 0;      ConsoleInfo *infoPtr, **nextPtrPtr;      ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); -    DWORD exitCode; -    errorCode = 0; -          /* -     * Clean up the background thread if necessary.  Note that this -     * must be done before we can close the file, since the  -     * thread may be blocking trying to read from the console. +     * Clean up the background thread if necessary. Note that this must be +     * done before we can close the file, since the thread may be blocking +     * trying to read from the console.       */ -     -    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; +    if (consolePtr->reader.thread) { +	StopChannelThread(&consolePtr->reader);      }      consolePtr->validMask &= ~TCL_READABLE;      /* -     * 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. +     * 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 (consolePtr->writeThread) { -	if (consolePtr->toWrite) { -	    /* -	     * We only need to wait if there is something to write. -	     * This may prevent infinite wait on exit. [python bug 216289] -	     */ -	    WaitForSingleObject(consolePtr->writable, INFINITE); -	} - -	/* -	 * 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); +    if (consolePtr->writer.thread) { +	if (consolePtr->toWrite) {  	    /* -	     * Wait at most 20 milliseconds for the writer thread to close. +	     * We only need to wait if there is something to write. This may +	     * prevent infinite wait on exit. [Python Bug 216289]  	     */ -	    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); - -		/* BUG: this leaks memory. */ -		TerminateThread(consolePtr->writeThread, 0); -		Tcl_MutexUnlock(&consoleMutex); -	    } +	    WaitForSingleObject(consolePtr->writer.readyEvent, INFINITE);  	} -	CloseHandle(consolePtr->writeThread); -	CloseHandle(consolePtr->writable); -	CloseHandle(consolePtr->startWriter); -	CloseHandle(consolePtr->stopWriter); -	consolePtr->writeThread = NULL; +	StopChannelThread(&consolePtr->writer);      }      consolePtr->validMask &= ~TCL_WRITABLE; -      /* -     * Don't close the Win32 handle if the handle is a standard channel -     * during the thread exit process.  Otherwise, one thread may kill -     * the stdio of another. +     * Don't close the Win32 handle if the handle is a standard channel during +     * the thread exit process. Otherwise, one thread may kill the stdio of +     * another.       */ -    if (!TclInThreadExit()  +    if (!TclInThreadExit()  	    || ((GetStdHandle(STD_INPUT_HANDLE) != consolePtr->handle)  		&& (GetStdHandle(STD_OUTPUT_HANDLE) != consolePtr->handle)  		&& (GetStdHandle(STD_ERROR_HANDLE) != consolePtr->handle))) { @@ -600,7 +653,7 @@ ConsoleCloseProc(  	    errorCode = errno;  	}      } -     +      consolePtr->watchMask &= consolePtr->validMask;      /* @@ -610,7 +663,7 @@ ConsoleCloseProc(      for (nextPtrPtr = &(tsdPtr->firstConsolePtr), infoPtr = *nextPtrPtr;  	    infoPtr != NULL;  	    nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) { -	if (infoPtr == (ConsoleInfo *)consolePtr) { +	if (infoPtr == (ConsoleInfo *) consolePtr) {  	    *nextPtrPtr = infoPtr->nextPtr;  	    break;  	} @@ -619,7 +672,7 @@ ConsoleCloseProc(  	ckfree(consolePtr->writeBuf);  	consolePtr->writeBuf = 0;      } -    ckfree((char*) consolePtr); +    ckfree(consolePtr);      return errorCode;  } @@ -629,8 +682,8 @@ ConsoleCloseProc(   *   * ConsoleInputProc --   * - *	Reads input from the IO channel into the buffer given. Returns - *	count of how many bytes were actually read, and an error indication. + *	Reads input from the IO channel into the buffer given. Returns count + *	of how many bytes were actually read, and an error indication.   *   * Results:   *	A count of how many bytes were read is returned and an error @@ -644,13 +697,13 @@ ConsoleCloseProc(  static int  ConsoleInputProc( -    ClientData instanceData,		/* Console state. */ -    char *buf,				/* Where to store data read. */ -    int bufSize,			/* How much space is available -                                         * in the buffer? */ -    int *errorCode)			/* Where to store error code. */ +    ClientData instanceData,	/* Console state. */ +    char *buf,			/* Where to store data read. */ +    int bufSize,		/* How much space is available in the +				 * buffer? */ +    int *errorCode)		/* Where to store error code. */  { -    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData; +    ConsoleInfo *infoPtr = instanceData;      DWORD count, bytesRead = 0;      int result; @@ -659,13 +712,13 @@ ConsoleInputProc(      /*       * Synchronize with the reader thread.       */ -     +      result = WaitForRead(infoPtr, (infoPtr->flags & CONSOLE_ASYNC) ? 0 : 1); -     +      /*       * If an error occurred, return immediately.       */ -     +      if (result == -1) {  	*errorCode = errno;  	return -1; @@ -685,24 +738,31 @@ ConsoleInputProc(  	    bytesRead = infoPtr->bytesRead - infoPtr->offset;  	    /* -	     * Reset the buffer +	     * Reset the buffer.  	     */ -	     +  	    infoPtr->readFlags &= ~CONSOLE_BUFFERED;  	    infoPtr->offset = 0;  	}  	return bytesRead;      } -     +      /* -     * Attempt to read bufSize bytes.  The read will return immediately -     * if there is any data available.  Otherwise it will block until -     * at least one byte is available or an EOF occurs. +     * Attempt to read bufSize bytes. The read will return immediately if +     * there is any data available. Otherwise it will block until at least one +     * byte is available or an EOF occurs.       */ -    if (ReadConsole(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &count, -		    (LPOVERLAPPED) NULL) == TRUE) { +    if (ReadConsoleBytes(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, +	    &count) == TRUE) { +	/* +	 * TODO: This potentially writes beyond the limits specified +	 * by the caller.  In practice this is harmless, since all writes +	 * are into ChannelBuffers, and those have padding, but still +	 * ought to remove this, unless some Windows wizard can give +	 * a reason not to.   +	 */  	buf[count] = '\0';  	return count;      } @@ -715,12 +775,12 @@ ConsoleInputProc(   *   * ConsoleOutputProc --   * - *	Writes the given output on the IO channel. Returns count of how - *	many characters were actually written, and an error indication. + *	Writes the given output on the IO channel. Returns count of how many + *	characters were actually written, and an error indication.   *   * Results: - *	A count of how many characters were written is returned and an - *	error indication is returned in an output argument. + *	A count of how many characters were written is returned and an error + *	indication is returned in an output argument.   *   * Side effects:   *	Writes output on the actual channel. @@ -730,26 +790,27 @@ ConsoleInputProc(  static int  ConsoleOutputProc( -    ClientData instanceData,		/* Console state. */ -    CONST char *buf,			/* The data buffer. */ -    int toWrite,			/* How many bytes to write? */ -    int *errorCode)			/* Where to store error code. */ +    ClientData instanceData,	/* Console state. */ +    const char *buf,		/* The data buffer. */ +    int toWrite,		/* How many bytes to write? */ +    int *errorCode)		/* Where to store error code. */  { -    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData; +    ConsoleInfo *infoPtr = instanceData; +    ConsoleThreadInfo *threadInfo = &infoPtr->reader;      DWORD bytesWritten, timeout; -     +      *errorCode = 0;      timeout = (infoPtr->flags & CONSOLE_ASYNC) ? 0 : INFINITE; -    if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) { +    if (WaitForSingleObject(threadInfo->readyEvent,timeout) == WAIT_TIMEOUT) {  	/* -	 * The writer thread is blocked waiting for a write to complete -	 * and the channel is in non-blocking mode. +	 * The writer thread is blocked waiting for a write to complete and +	 * the channel is in non-blocking mode.  	 */ -	errno = EAGAIN; +	errno = EWOULDBLOCK;  	goto error;      } -     +      /*       * Check for a background error on the last write.       */ @@ -775,31 +836,30 @@ ConsoleOutputProc(  		ckfree(infoPtr->writeBuf);  	    }  	    infoPtr->writeBufLen = toWrite; -	    infoPtr->writeBuf = ckalloc((unsigned int) toWrite); +	    infoPtr->writeBuf = ckalloc(toWrite);  	}  	memcpy(infoPtr->writeBuf, buf, (size_t) toWrite);  	infoPtr->toWrite = toWrite; -	ResetEvent(infoPtr->writable); -	SetEvent(infoPtr->startWriter); +	ResetEvent(threadInfo->readyEvent); +	SetEvent(threadInfo->startEvent);  	bytesWritten = toWrite;      } else {  	/* -	 * In the blocking case, just try to write the buffer directly. -	 * This avoids an unnecessary copy. +	 * In the blocking case, just try to write the buffer directly. This +	 * avoids an unnecessary copy.  	 */ -	if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite, -		&bytesWritten, (LPOVERLAPPED) NULL) == FALSE) { +	if (WriteConsoleBytes(infoPtr->handle, buf, (DWORD) toWrite, +		&bytesWritten) == FALSE) {  	    TclWinConvertError(GetLastError());  	    goto error;  	}      }      return bytesWritten; -    error: +  error:      *errorCode = errno;      return -1; -  }  /* @@ -807,15 +867,15 @@ ConsoleOutputProc(   *   * ConsoleEventProc --   * - *	This function is invoked by Tcl_ServiceEvent when a file event - *	reaches the front of the event queue.  This procedure invokes - *	Tcl_NotifyChannel on the console. + *	This function is invoked by Tcl_ServiceEvent when a file event reaches + *	the front of the event queue. This procedure invokes Tcl_NotifyChannel + *	on the console.   *   * Results: - *	Returns 1 if the event was handled, meaning it should be removed - *	from the queue.  Returns 0 if the event was not handled, meaning - *	it should stay on the queue.  The only time the event isn't - *	handled is if the TCL_FILE_EVENTS flag bit isn't set. + *	Returns 1 if the event was handled, meaning it should be removed from + *	the queue. Returns 0 if the event was not handled, meaning it should + *	stay on the queue. The only time the event isn't handled is if the + *	TCL_FILE_EVENTS flag bit isn't set.   *   * Side effects:   *	Whatever the notifier callback does. @@ -826,10 +886,10 @@ ConsoleOutputProc(  static int  ConsoleEventProc(      Tcl_Event *evPtr,		/* Event to service. */ -    int flags)			/* Flags that indicate what events to -				 * handle, such as TCL_FILE_EVENTS. */ +    int flags)			/* Flags that indicate what events to handle, +				 * such as TCL_FILE_EVENTS. */  { -    ConsoleEvent *consoleEvPtr = (ConsoleEvent *)evPtr; +    ConsoleEvent *consoleEvPtr = (ConsoleEvent *) evPtr;      ConsoleInfo *infoPtr;      int mask;      ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); @@ -840,15 +900,15 @@ ConsoleEventProc(      /*       * Search through the list of watched consoles for the one whose handle -     * matches the event.  We do this rather than simply dereferencing -     * the handle in the event so that consoles can be deleted while the -     * event is in the queue. +     * matches the event. We do this rather than simply dereferencing the +     * handle in the event so that consoles can be deleted while the event is +     * in the queue.       */      for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;  	    infoPtr = infoPtr->nextPtr) {  	if (consoleEvPtr->infoPtr == infoPtr) { -	    infoPtr->flags &= ~(CONSOLE_PENDING); +	    infoPtr->flags &= ~CONSOLE_PENDING;  	    break;  	}      } @@ -862,14 +922,15 @@ ConsoleEventProc(      }      /* -     * Check to see if the console is readable.  Note -     * that we can't tell if a console is writable, so we always report it -     * as being writable unless we have detected EOF. +     * Check to see if the console is readable. Note that we can't tell if a +     * console is writable, so we always report it as being writable unless we +     * have detected EOF.       */      mask = 0;      if (infoPtr->watchMask & TCL_WRITABLE) { -	if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) { +	if (WaitForSingleObject(infoPtr->writer.readyEvent, +		0) != WAIT_TIMEOUT) {  	    mask = TCL_WRITABLE;  	}      } @@ -881,7 +942,7 @@ ConsoleEventProc(  	    } else {  		mask |= TCL_READABLE;  	    } -	}  +	}      }      /* @@ -897,8 +958,7 @@ ConsoleEventProc(   *   * ConsoleWatchProc --   * - *	Called by the notifier to set up to watch for events on this - *	channel. + *	Called by the notifier to set up to watch for events on this channel.   *   * Results:   *	None. @@ -911,43 +971,41 @@ ConsoleEventProc(  static void  ConsoleWatchProc( -    ClientData instanceData,		/* Console state. */ -    int mask)				/* What events to watch for, OR-ed -                                         * combination of TCL_READABLE, -                                         * TCL_WRITABLE and TCL_EXCEPTION. */ +    ClientData instanceData,	/* Console state. */ +    int mask)			/* What events to watch for, OR-ed combination +				 * of TCL_READABLE, TCL_WRITABLE and +				 * TCL_EXCEPTION. */  {      ConsoleInfo **nextPtrPtr, *ptr; -    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData; +    ConsoleInfo *infoPtr = instanceData;      int oldMask = infoPtr->watchMask;      ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);      /* -     * Since most of the work is handled by the background threads, -     * we just need to update the watchMask and then force the notifier -     * to poll once.  +     * Since most of the work is handled by the background threads, we just +     * need to update the watchMask and then force the notifier to poll once.       */      infoPtr->watchMask = mask & infoPtr->validMask;      if (infoPtr->watchMask) {  	Tcl_Time blockTime = { 0, 0 }; +  	if (!oldMask) {  	    infoPtr->nextPtr = tsdPtr->firstConsolePtr;  	    tsdPtr->firstConsolePtr = infoPtr;  	}  	Tcl_SetMaxBlockTime(&blockTime); -    } else { -	if (oldMask) { -	    /* -	     * Remove the console from the list of watched consoles. -	     */ +    } else if (oldMask) { +	/* +	 * Remove the console from the list of watched consoles. +	 */ -	    for (nextPtrPtr = &(tsdPtr->firstConsolePtr), ptr = *nextPtrPtr; -		 ptr != NULL; -		 nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) { -		if (infoPtr == ptr) { -		    *nextPtrPtr = ptr->nextPtr; -		    break; -		} +	for (nextPtrPtr = &(tsdPtr->firstConsolePtr), ptr = *nextPtrPtr; +		ptr != NULL; +		nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) { +	    if (infoPtr == ptr) { +		*nextPtrPtr = ptr->nextPtr; +		break;  	    }  	}      } @@ -958,12 +1016,12 @@ ConsoleWatchProc(   *   * ConsoleGetHandleProc --   * - *	Called from Tcl_GetChannelHandle to retrieve OS handles from - *	inside a command consoleline based channel. + *	Called from Tcl_GetChannelHandle to retrieve OS handles from inside a + *	command consoleline based channel.   *   * Results: - *	Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if - *	there is no handle for the specified direction.  + *	Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if there is no + *	handle for the specified direction.   *   * Side effects:   *	None. @@ -974,12 +1032,12 @@ ConsoleWatchProc(  static int  ConsoleGetHandleProc(      ClientData instanceData,	/* The console state. */ -    int direction,		/* TCL_READABLE or TCL_WRITABLE */ -    ClientData *handlePtr)	/* Where to store the handle.  */ +    int direction,		/* TCL_READABLE or TCL_WRITABLE. */ +    ClientData *handlePtr)	/* Where to store the handle. */  { -    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData; +    ConsoleInfo *infoPtr = instanceData; -    *handlePtr = (ClientData) infoPtr->handle; +    *handlePtr = infoPtr->handle;      return TCL_OK;  } @@ -988,69 +1046,70 @@ ConsoleGetHandleProc(   *   * WaitForRead --   * - *	Wait until some data is available, the console is at - *	EOF or the reader thread is blocked waiting for data (if the - *	channel is in non-blocking mode). + *	Wait until some data is available, the console is at EOF or the reader + *	thread is blocked waiting for data (if the channel is in non-blocking + *	mode).   *   * Results: - *	Returns 1 if console is readable.  Returns 0 if there is no data - *	on the console, but there is buffered data.  Returns -1 if an - *	error occurred.  If an error occurred, the threads may not - *	be synchronized. + *	Returns 1 if console is readable. Returns 0 if there is no data on the + *	console, but there is buffered data. Returns -1 if an error occurred. + *	If an error occurred, the threads may not be synchronized.   *   * Side effects: - *	Updates the shared state flags.  If no error occurred, - *      the reader thread is blocked waiting for a signal from the  - *      main thread. + *	Updates the shared state flags. If no error occurred, the reader + *	thread is blocked waiting for a signal from the main thread.   *   *----------------------------------------------------------------------   */  static int  WaitForRead( -    ConsoleInfo *infoPtr,		/* Console state. */ -    int blocking)		/* Indicates whether call should be -				 * blocking or not. */ +    ConsoleInfo *infoPtr,	/* Console state. */ +    int blocking)		/* Indicates whether call should be blocking +				 * or not. */  {      DWORD timeout, count;      HANDLE *handle = infoPtr->handle; +    ConsoleThreadInfo *threadInfo = &infoPtr->reader;      INPUT_RECORD input; -     +      while (1) {  	/*  	 * Synchronize with the reader thread.  	 */ -        +  	timeout = blocking ? INFINITE : 0; -	if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) { +	if (WaitForSingleObject(threadInfo->readyEvent, +		timeout) == WAIT_TIMEOUT) {  	    /*  	     * The reader thread is blocked waiting for data and the channel  	     * is in non-blocking mode.  	     */ -	    errno = EAGAIN; + +	    errno = EWOULDBLOCK;  	    return -1;  	} -	 +  	/* -	 * At this point, the two threads are synchronized, so it is safe -	 * to access shared state. +	 * At this point, the two threads are synchronized, so it is safe to +	 * access shared state.  	 */ -	 +  	/*  	 * If the console has hit EOF, it is always readable.  	 */ -	 +  	if (infoPtr->readFlags & CONSOLE_EOF) {  	    return 1;  	} -	 +  	if (PeekConsoleInput(handle, &input, 1, &count) == FALSE) { -            /* +	    /*  	     * Check to see if the peek failed because of EOF.  	     */ -	     +  	    TclWinConvertError(GetLastError()); -	     +  	    if (errno == EOF) {  		infoPtr->readFlags |= CONSOLE_EOF;  		return 1; @@ -1059,7 +1118,7 @@ WaitForRead(  	    /*  	     * Ignore errors if there is data in the buffer.  	     */ -	     +  	    if (infoPtr->readFlags & CONSOLE_BUFFERED) {  		return 0;  	    } else { @@ -1068,22 +1127,20 @@ WaitForRead(  	}  	/* -	 * If there is data in the buffer, the console must be -	 * readable (since it is a line-oriented device). +	 * If there is data in the buffer, the console must be readable (since +	 * it is a line-oriented device).  	 */  	if (infoPtr->readFlags & CONSOLE_BUFFERED) {  	    return 1;  	} -	  	/* -	 * There wasn't any data available, so reset the thread and -	 * try again. +	 * There wasn't any data available, so reset the thread and try again.  	 */ -     -	ResetEvent(infoPtr->readable); -	SetEvent(infoPtr->startReader); + +	ResetEvent(threadInfo->readyEvent); +	SetEvent(threadInfo->startEvent);      }  } @@ -1092,31 +1149,36 @@ WaitForRead(   *   * ConsoleReaderThread --   * - *	This function runs in a separate thread and waits for input - *	to become available on a console. + *	This function runs in a separate thread and waits for input to become + *	available on a console.   *   * Results:   *	None.   *   * Side effects: - *	Signals the main thread when input become available.  May - *	cause the main thread to wake up by posting a message.  May - *	one line from the console for each wait operation. + *	Signals the main thread when input become available. May cause the + *	main thread to wake up by posting a message. May one line from the + *	console for each wait operation.   *   *----------------------------------------------------------------------   */  static DWORD WINAPI -ConsoleReaderThread(LPVOID arg) +ConsoleReaderThread( +    LPVOID arg)  { -    ConsoleInfo *infoPtr = (ConsoleInfo *)arg; +    ConsoleInfo *infoPtr = arg;      HANDLE *handle = infoPtr->handle; -    DWORD count, waitResult; +    ConsoleThreadInfo *threadInfo = &infoPtr->reader; +    DWORD waitResult;      HANDLE wEvents[2]; -    /* The first event takes precedence. */ -    wEvents[0] = infoPtr->stopReader; -    wEvents[1] = infoPtr->startReader; +    /* +     * The first event takes precedence. +     */ + +    wEvents[0] = threadInfo->stopEvent; +    wEvents[1] = threadInfo->startEvent;      for (;;) {  	/* @@ -1127,50 +1189,55 @@ ConsoleReaderThread(LPVOID arg)  	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. +	     * The start event was not signaled. It must be the stop event or +	     * an error, so exit this thread.  	     */  	    break;  	} -	count = 0; - -	/*  -	 * Look for data on the console, but first ignore any events -	 * that are not KEY_EVENTs  +	/* +	 * 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) { + +	if (ReadConsoleBytes(handle, infoPtr->buffer, CONSOLE_BUFFER_SIZE, +		(LPDWORD) &infoPtr->bytesRead) != FALSE) {  	    /*  	     * Data was stored in the buffer.  	     */ -	     +  	    infoPtr->readFlags |= CONSOLE_BUFFERED;  	} else { -	    DWORD err; -	    err = GetLastError(); -	     -	    if (err == EOF) { +	    DWORD err = GetLastError(); + +	    if (err == (DWORD) EOF) {  		infoPtr->readFlags = CONSOLE_EOF;  	    }  	}  	/* -	 * Signal the main thread by signalling the readable event and -	 * then waking up the notifier thread. +	 * Signal the main thread by signalling the readable event and then +	 * waking up the notifier thread.  	 */ -	SetEvent(infoPtr->readable); +	SetEvent(threadInfo->readyEvent);  	/* -	 * Alert the foreground thread.  Note that we need to treat this like -	 * a critical section so the foreground thread does not terminate -	 * this thread while we are holding a mutex in the notifier code. +	 * Alert the foreground thread. Note that we need to treat this like a +	 * critical section so the foreground thread does not terminate this +	 * thread while we are holding a mutex in the notifier code.  	 */  	Tcl_MutexLock(&consoleMutex); -	Tcl_ThreadAlert(infoPtr->threadId); +	if (infoPtr->threadId != NULL) { +	    /* +	     * TIP #218. When in flight ignore the event, no one will receive +	     * it anyway. +	     */ + +	    Tcl_ThreadAlert(infoPtr->threadId); +	}  	Tcl_MutexUnlock(&consoleMutex);      } @@ -1182,32 +1249,37 @@ ConsoleReaderThread(LPVOID arg)   *   * ConsoleWriterThread --   * - *	This function runs in a separate thread and writes data - *	onto a console. + *	This function runs in a separate thread and writes data onto a + *	console.   *   * Results:   *	Always returns 0.   *   * Side effects: - *	Signals the main thread when an output operation is completed. - *	May cause the main thread to wake up by posting a message.   + + *	Signals the main thread when an output operation is completed. May + *	cause the main thread to wake up by posting a message.   *   *----------------------------------------------------------------------   */  static DWORD WINAPI -ConsoleWriterThread(LPVOID arg) +ConsoleWriterThread( +    LPVOID arg)  { - -    ConsoleInfo *infoPtr = (ConsoleInfo *)arg; +    ConsoleInfo *infoPtr = arg;      HANDLE *handle = infoPtr->handle; +    ConsoleThreadInfo *threadInfo = &infoPtr->writer;      DWORD count, toWrite, waitResult;      char *buf;      HANDLE wEvents[2]; -    /* The first event takes precedence. */ -    wEvents[0] = infoPtr->stopWriter; -    wEvents[1] = infoPtr->startWriter; +    /* +     * The first event takes precedence. +     */ + +    wEvents[0] = threadInfo->stopEvent; +    wEvents[1] = threadInfo->startEvent;      for (;;) {  	/* @@ -1218,8 +1290,8 @@ ConsoleWriterThread(LPVOID arg)  	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. +	     * The start event was not signaled. It must be the stop event or +	     * an error, so exit this thread.  	     */  	    break; @@ -1233,37 +1305,42 @@ ConsoleWriterThread(LPVOID arg)  	 */  	while (toWrite > 0) { -	    if (WriteConsoleA(handle, buf, toWrite, &count, NULL) == FALSE) { +	    if (WriteConsoleBytes(handle, buf, (DWORD) toWrite, +		    &count) == FALSE) {  		infoPtr->writeError = GetLastError();  		break; -	    } else { -		toWrite -= count; -		buf += count;  	    } +	    toWrite -= count; +	    buf += count;  	}  	/* -	 * Signal the main thread by signalling the writable event and -	 * then waking up the notifier thread. +	 * Signal the main thread by signalling the writable event and then +	 * waking up the notifier thread.  	 */ -	 -	SetEvent(infoPtr->writable); + +	SetEvent(threadInfo->readyEvent);  	/* -	 * Alert the foreground thread.  Note that we need to treat this like -	 * a critical section so the foreground thread does not terminate -	 * this thread while we are holding a mutex in the notifier code. +	 * Alert the foreground thread. Note that we need to treat this like a +	 * critical section so the foreground thread does not terminate this +	 * thread while we are holding a mutex in the notifier code.  	 */  	Tcl_MutexLock(&consoleMutex); -	Tcl_ThreadAlert(infoPtr->threadId); +	if (infoPtr->threadId != NULL) { +	    /* +	     * TIP #218. When in flight ignore the event, no one will receive +	     * it anyway. +	     */ + +	    Tcl_ThreadAlert(infoPtr->threadId); +	}  	Tcl_MutexUnlock(&consoleMutex);      }      return 0;  } - -  /*   *---------------------------------------------------------------------- @@ -1271,93 +1348,143 @@ ConsoleWriterThread(LPVOID arg)   * TclWinOpenConsoleChannel --   *   *	Constructs a Console channel for the specified standard OS handle. - *      This is a helper function to break up the construction of  - *      channels into File, Console, or Serial. + *	This is a helper function to break up the construction of channels + *	into File, Console, or Serial.   *   * Results:   *	Returns the new channel, or NULL.   *   * Side effects: - *	May open the channel + *	May open the channel.   *   *----------------------------------------------------------------------   */  Tcl_Channel -TclWinOpenConsoleChannel(handle, channelName, permissions) -    HANDLE handle; -    char *channelName; -    int permissions; +TclWinOpenConsoleChannel( +    HANDLE handle, +    char *channelName, +    int permissions)  {      char encoding[4 + TCL_INTEGER_SPACE];      ConsoleInfo *infoPtr; -    ThreadSpecificData *tsdPtr; -    DWORD id, modes; +    DWORD modes; -    tsdPtr = ConsoleInit(); +    ConsoleInit();      /*       * See if a channel with this handle already exists.       */ -     -    infoPtr = (ConsoleInfo *) ckalloc((unsigned) sizeof(ConsoleInfo)); + +    infoPtr = ckalloc(sizeof(ConsoleInfo));      memset(infoPtr, 0, sizeof(ConsoleInfo));      infoPtr->validMask = permissions;      infoPtr->handle = handle; +    infoPtr->channel = (Tcl_Channel) NULL;      wsprintfA(encoding, "cp%d", GetConsoleCP()); +    infoPtr->threadId = Tcl_GetCurrentThread(); +      /* -     * Use the pointer for the name of the result channel. -     * This keeps the channel names unique, since some may share -     * handles (stdin/stdout/stderr for instance). +     * Use the pointer for the name of the result channel. This keeps the +     * channel names unique, since some may share handles (stdin/stdout/stderr +     * for instance).       */ -    wsprintfA(channelName, "file%lx", (int) infoPtr); -     -    infoPtr->channel = Tcl_CreateChannel(&consoleChannelType, channelName, -            (ClientData) infoPtr, permissions); +    sprintf(channelName, "file%" TCL_I_MODIFIER "x", (size_t) infoPtr); -    infoPtr->threadId = Tcl_GetCurrentThread(); +    infoPtr->channel = Tcl_CreateChannel(&consoleChannelType, channelName, +	    infoPtr, permissions);      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. +	 * 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); +	StartChannelThread(infoPtr, &infoPtr->reader, ConsoleReaderThread);      }      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); +	StartChannelThread(infoPtr, &infoPtr->writer, ConsoleWriterThread);      }      /* -     * Files have default translation of AUTO and ^Z eof char, which -     * means that a ^Z will be accepted as EOF when reading. +     * Files have default translation of AUTO and ^Z eof char, which means +     * that a ^Z will be accepted as EOF when reading.       */ -     +      Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");      Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}"); +#ifdef UNICODE +    Tcl_SetChannelOption(NULL, infoPtr->channel, "-encoding", "unicode"); +#else      Tcl_SetChannelOption(NULL, infoPtr->channel, "-encoding", encoding); - +#endif      return infoPtr->channel;  } + +/* + *---------------------------------------------------------------------- + * + * ConsoleThreadActionProc -- + * + *	Insert or remove any thread local refs to this channel. + * + * Results: + *	None. + * + * Side effects: + *	Changes thread local list of valid channels. + * + *---------------------------------------------------------------------- + */ + +static void +ConsoleThreadActionProc( +    ClientData instanceData, +    int action) +{ +    ConsoleInfo *infoPtr = instanceData; + +    /* +     * We do not access firstConsolePtr in the thread structures. This is not +     * for all serials managed by the thread, but only those we are watching. +     * Removal of the filevent handlers before transfer thus takes care of +     * this structure. +     */ + +    Tcl_MutexLock(&consoleMutex); +    if (action == TCL_CHANNEL_THREAD_INSERT) { +	/* +	 * We can't copy the thread information from the channel when the +	 * channel is created. At this time the channel back pointer has not +	 * been set yet. However in that case the threadId has already been +	 * set by TclpCreateCommandChannel itself, so the structure is still +	 * good. +	 */ + +	ConsoleInit(); +	if (infoPtr->channel != NULL) { +	    infoPtr->threadId = Tcl_GetChannelThread(infoPtr->channel); +	} +    } else { +	infoPtr->threadId = NULL; +    } +    Tcl_MutexUnlock(&consoleMutex); +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ | 
