os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinConsole.c
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinConsole.c	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,1421 @@
     1.4 +/* 
     1.5 + * tclWinConsole.c --
     1.6 + *
     1.7 + *	This file implements the Windows-specific console functions,
     1.8 + *	and the "console" channel driver.
     1.9 + *
    1.10 + * Copyright (c) 1999 by Scriptics Corp.
    1.11 + *
    1.12 + * See the file "license.terms" for information on usage and redistribution
    1.13 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    1.14 + *
    1.15 + * RCS: @(#) $Id: tclWinConsole.c,v 1.11.2.3 2006/03/28 21:02:37 hobbs Exp $
    1.16 + */
    1.17 +
    1.18 +#include "tclWinInt.h"
    1.19 +
    1.20 +#include <fcntl.h>
    1.21 +#include <io.h>
    1.22 +#include <sys/stat.h>
    1.23 +
    1.24 +/*
    1.25 + * The following variable is used to tell whether this module has been
    1.26 + * initialized.
    1.27 + */
    1.28 +
    1.29 +static int initialized = 0;
    1.30 +
    1.31 +/*
    1.32 + * The consoleMutex locks around access to the initialized variable, and it is
    1.33 + * used to protect background threads from being terminated while they are
    1.34 + * using APIs that hold locks.
    1.35 + */
    1.36 +
    1.37 +TCL_DECLARE_MUTEX(consoleMutex)
    1.38 +
    1.39 +/*
    1.40 + * Bit masks used in the flags field of the ConsoleInfo structure below.
    1.41 + */
    1.42 +
    1.43 +#define CONSOLE_PENDING	(1<<0)	/* Message is pending in the queue. */
    1.44 +#define CONSOLE_ASYNC	(1<<1)	/* Channel is non-blocking. */
    1.45 +
    1.46 +/*
    1.47 + * Bit masks used in the sharedFlags field of the ConsoleInfo structure below.
    1.48 + */
    1.49 +
    1.50 +#define CONSOLE_EOF	  (1<<2)  /* Console has reached EOF. */
    1.51 +#define CONSOLE_BUFFERED  (1<<3)  /* data was read into a buffer by the reader
    1.52 +				     thread */
    1.53 +
    1.54 +#define CONSOLE_BUFFER_SIZE (8*1024)
    1.55 +/*
    1.56 + * This structure describes per-instance data for a console based channel.
    1.57 + */
    1.58 +
    1.59 +typedef struct ConsoleInfo {
    1.60 +    HANDLE handle;
    1.61 +    int type;
    1.62 +    struct ConsoleInfo *nextPtr;/* Pointer to next registered console. */
    1.63 +    Tcl_Channel channel;	/* Pointer to channel structure. */
    1.64 +    int validMask;		/* OR'ed combination of TCL_READABLE,
    1.65 +				 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
    1.66 +				 * which operations are valid on the file. */
    1.67 +    int watchMask;		/* OR'ed combination of TCL_READABLE,
    1.68 +				 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
    1.69 +				 * which events should be reported. */
    1.70 +    int flags;			/* State flags, see above for a list. */
    1.71 +    Tcl_ThreadId threadId;	/* Thread to which events should be reported.
    1.72 +				 * This value is used by the reader/writer
    1.73 +				 * threads. */
    1.74 +    HANDLE writeThread;		/* Handle to writer thread. */
    1.75 +    HANDLE readThread;		/* Handle to reader thread. */
    1.76 +    HANDLE writable;		/* Manual-reset event to signal when the
    1.77 +				 * writer thread has finished waiting for
    1.78 +				 * the current buffer to be written. */
    1.79 +    HANDLE readable;		/* Manual-reset event to signal when the
    1.80 +				 * reader thread has finished waiting for
    1.81 +				 * input. */
    1.82 +    HANDLE startWriter;		/* Auto-reset event used by the main thread to
    1.83 +				 * signal when the writer thread should attempt
    1.84 +				 * to write to the console. */
    1.85 +    HANDLE stopWriter;		/* Auto-reset event used by the main thread to
    1.86 +				 * signal when the writer thread should exit.
    1.87 +				 */
    1.88 +    HANDLE startReader;		/* Auto-reset event used by the main thread to
    1.89 +				 * signal when the reader thread should attempt
    1.90 +				 * to read from the console. */
    1.91 +    HANDLE stopReader;		/* Auto-reset event used by the main thread to
    1.92 +				 * signal when the reader thread should exit.
    1.93 +				 */
    1.94 +    DWORD writeError;		/* An error caused by the last background
    1.95 +				 * write.  Set to 0 if no error has been
    1.96 +				 * detected.  This word is shared with the
    1.97 +				 * writer thread so access must be
    1.98 +				 * synchronized with the writable object.
    1.99 +				 */
   1.100 +    char *writeBuf;		/* Current background output buffer.
   1.101 +				 * Access is synchronized with the writable
   1.102 +				 * object. */
   1.103 +    int writeBufLen;		/* Size of write buffer.  Access is
   1.104 +				 * synchronized with the writable
   1.105 +				 * object. */
   1.106 +    int toWrite;		/* Current amount to be written.  Access is
   1.107 +				 * synchronized with the writable object. */
   1.108 +    int readFlags;		/* Flags that are shared with the reader
   1.109 +				 * thread.  Access is synchronized with the
   1.110 +				 * readable object.  */
   1.111 +    int bytesRead;              /* number of bytes in the buffer */
   1.112 +    int offset;                 /* number of bytes read out of the buffer */
   1.113 +    char buffer[CONSOLE_BUFFER_SIZE];
   1.114 +                                /* Data consumed by reader thread. */
   1.115 +} ConsoleInfo;
   1.116 +
   1.117 +typedef struct ThreadSpecificData {
   1.118 +    /*
   1.119 +     * The following pointer refers to the head of the list of consoles
   1.120 +     * that are being watched for file events.
   1.121 +     */
   1.122 +    
   1.123 +    ConsoleInfo *firstConsolePtr;
   1.124 +} ThreadSpecificData;
   1.125 +
   1.126 +static Tcl_ThreadDataKey dataKey;
   1.127 +
   1.128 +/*
   1.129 + * The following structure is what is added to the Tcl event queue when
   1.130 + * console events are generated.
   1.131 + */
   1.132 +
   1.133 +typedef struct ConsoleEvent {
   1.134 +    Tcl_Event header;		/* Information that is standard for
   1.135 +				 * all events. */
   1.136 +    ConsoleInfo *infoPtr;	/* Pointer to console info structure.  Note
   1.137 +				 * that we still have to verify that the
   1.138 +				 * console exists before dereferencing this
   1.139 +				 * pointer. */
   1.140 +} ConsoleEvent;
   1.141 +
   1.142 +/*
   1.143 + * Declarations for functions used only in this file.
   1.144 + */
   1.145 +
   1.146 +static int		ConsoleBlockModeProc(ClientData instanceData, int mode);
   1.147 +static void		ConsoleCheckProc(ClientData clientData, int flags);
   1.148 +static int		ConsoleCloseProc(ClientData instanceData,
   1.149 +			    Tcl_Interp *interp);
   1.150 +static int		ConsoleEventProc(Tcl_Event *evPtr, int flags);
   1.151 +static void		ConsoleExitHandler(ClientData clientData);
   1.152 +static int		ConsoleGetHandleProc(ClientData instanceData,
   1.153 +			    int direction, ClientData *handlePtr);
   1.154 +static void             ConsoleInit(void);
   1.155 +static int		ConsoleInputProc(ClientData instanceData, char *buf,
   1.156 +			    int toRead, int *errorCode);
   1.157 +static int		ConsoleOutputProc(ClientData instanceData,
   1.158 +			    CONST char *buf, int toWrite, int *errorCode);
   1.159 +static DWORD WINAPI	ConsoleReaderThread(LPVOID arg);
   1.160 +static void		ConsoleSetupProc(ClientData clientData, int flags);
   1.161 +static void		ConsoleWatchProc(ClientData instanceData, int mask);
   1.162 +static DWORD WINAPI	ConsoleWriterThread(LPVOID arg);
   1.163 +static void		ProcExitHandler(ClientData clientData);
   1.164 +static int		WaitForRead(ConsoleInfo *infoPtr, int blocking);
   1.165 +
   1.166 +static void             ConsoleThreadActionProc _ANSI_ARGS_ ((
   1.167 +			   ClientData instanceData, int action));
   1.168 +
   1.169 +/*
   1.170 + * This structure describes the channel type structure for command console
   1.171 + * based IO.
   1.172 + */
   1.173 +
   1.174 +static Tcl_ChannelType consoleChannelType = {
   1.175 +    "console",			/* Type name. */
   1.176 +    TCL_CHANNEL_VERSION_4,	/* v4 channel */
   1.177 +    ConsoleCloseProc,		/* Close proc. */
   1.178 +    ConsoleInputProc,		/* Input proc. */
   1.179 +    ConsoleOutputProc,		/* Output proc. */
   1.180 +    NULL,			/* Seek proc. */
   1.181 +    NULL,			/* Set option proc. */
   1.182 +    NULL,			/* Get option proc. */
   1.183 +    ConsoleWatchProc,		/* Set up notifier to watch the channel. */
   1.184 +    ConsoleGetHandleProc,	/* Get an OS handle from channel. */
   1.185 +    NULL,			/* close2proc. */
   1.186 +    ConsoleBlockModeProc,	/* Set blocking or non-blocking mode.*/
   1.187 +    NULL,			/* flush proc. */
   1.188 +    NULL,			/* handler proc. */
   1.189 +    NULL,                       /* wide seek proc */
   1.190 +    ConsoleThreadActionProc,    /* thread action proc */
   1.191 +};
   1.192 +
   1.193 +/*
   1.194 + *----------------------------------------------------------------------
   1.195 + *
   1.196 + * ConsoleInit --
   1.197 + *
   1.198 + *	This function initializes the static variables for this file.
   1.199 + *
   1.200 + * Results:
   1.201 + *	None.
   1.202 + *
   1.203 + * Side effects:
   1.204 + *	Creates a new event source.
   1.205 + *
   1.206 + *----------------------------------------------------------------------
   1.207 + */
   1.208 +
   1.209 +static void
   1.210 +ConsoleInit()
   1.211 +{
   1.212 +    ThreadSpecificData *tsdPtr;
   1.213 +
   1.214 +    /*
   1.215 +     * Check the initialized flag first, then check again in the mutex.
   1.216 +     * This is a speed enhancement.
   1.217 +     */
   1.218 +
   1.219 +    if (!initialized) {
   1.220 +	Tcl_MutexLock(&consoleMutex);
   1.221 +	if (!initialized) {
   1.222 +	    initialized = 1;
   1.223 +	    Tcl_CreateExitHandler(ProcExitHandler, NULL);
   1.224 +	}
   1.225 +	Tcl_MutexUnlock(&consoleMutex);
   1.226 +    }
   1.227 +
   1.228 +    tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
   1.229 +    if (tsdPtr == NULL) {
   1.230 +	tsdPtr = TCL_TSD_INIT(&dataKey);
   1.231 +	tsdPtr->firstConsolePtr = NULL;
   1.232 +	Tcl_CreateEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
   1.233 +	Tcl_CreateThreadExitHandler(ConsoleExitHandler, NULL);
   1.234 +    }
   1.235 +}
   1.236 +
   1.237 +/*
   1.238 + *----------------------------------------------------------------------
   1.239 + *
   1.240 + * ConsoleExitHandler --
   1.241 + *
   1.242 + *	This function is called to cleanup the console module before
   1.243 + *	Tcl is unloaded.
   1.244 + *
   1.245 + * Results:
   1.246 + *	None.
   1.247 + *
   1.248 + * Side effects:
   1.249 + *	Removes the console event source.
   1.250 + *
   1.251 + *----------------------------------------------------------------------
   1.252 + */
   1.253 +
   1.254 +static void
   1.255 +ConsoleExitHandler(
   1.256 +    ClientData clientData)	/* Old window proc */
   1.257 +{
   1.258 +    Tcl_DeleteEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
   1.259 +}
   1.260 +
   1.261 +/*
   1.262 + *----------------------------------------------------------------------
   1.263 + *
   1.264 + * ProcExitHandler --
   1.265 + *
   1.266 + *	This function is called to cleanup the process list before
   1.267 + *	Tcl is unloaded.
   1.268 + *
   1.269 + * Results:
   1.270 + *	None.
   1.271 + *
   1.272 + * Side effects:
   1.273 + *	Resets the process list.
   1.274 + *
   1.275 + *----------------------------------------------------------------------
   1.276 + */
   1.277 +
   1.278 +static void
   1.279 +ProcExitHandler(
   1.280 +    ClientData clientData)	/* Old window proc */
   1.281 +{
   1.282 +    Tcl_MutexLock(&consoleMutex);
   1.283 +    initialized = 0;
   1.284 +    Tcl_MutexUnlock(&consoleMutex);
   1.285 +}
   1.286 +
   1.287 +/*
   1.288 + *----------------------------------------------------------------------
   1.289 + *
   1.290 + * ConsoleSetupProc --
   1.291 + *
   1.292 + *	This procedure is invoked before Tcl_DoOneEvent blocks waiting
   1.293 + *	for an event.
   1.294 + *
   1.295 + * Results:
   1.296 + *	None.
   1.297 + *
   1.298 + * Side effects:
   1.299 + *	Adjusts the block time if needed.
   1.300 + *
   1.301 + *----------------------------------------------------------------------
   1.302 + */
   1.303 +
   1.304 +void
   1.305 +ConsoleSetupProc(
   1.306 +    ClientData data,		/* Not used. */
   1.307 +    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
   1.308 +{
   1.309 +    ConsoleInfo *infoPtr;
   1.310 +    Tcl_Time blockTime = { 0, 0 };
   1.311 +    int block = 1;
   1.312 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   1.313 +
   1.314 +    if (!(flags & TCL_FILE_EVENTS)) {
   1.315 +	return;
   1.316 +    }
   1.317 +    
   1.318 +    /*
   1.319 +     * Look to see if any events are already pending.  If they are, poll.
   1.320 +     */
   1.321 +
   1.322 +    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL; 
   1.323 +	    infoPtr = infoPtr->nextPtr) {
   1.324 +	if (infoPtr->watchMask & TCL_WRITABLE) {
   1.325 +	    if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
   1.326 +		block = 0;
   1.327 +	    }
   1.328 +	}
   1.329 +	if (infoPtr->watchMask & TCL_READABLE) {
   1.330 +	    if (WaitForRead(infoPtr, 0) >= 0) {
   1.331 +		block = 0;
   1.332 +	    }
   1.333 +	}
   1.334 +    }
   1.335 +    if (!block) {
   1.336 +	Tcl_SetMaxBlockTime(&blockTime);
   1.337 +    }
   1.338 +}
   1.339 +
   1.340 +/*
   1.341 + *----------------------------------------------------------------------
   1.342 + *
   1.343 + * ConsoleCheckProc --
   1.344 + *
   1.345 + *	This procedure is called by Tcl_DoOneEvent to check the console
   1.346 + *	event source for events. 
   1.347 + *
   1.348 + * Results:
   1.349 + *	None.
   1.350 + *
   1.351 + * Side effects:
   1.352 + *	May queue an event.
   1.353 + *
   1.354 + *----------------------------------------------------------------------
   1.355 + */
   1.356 +
   1.357 +static void
   1.358 +ConsoleCheckProc(
   1.359 +    ClientData data,		/* Not used. */
   1.360 +    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
   1.361 +{
   1.362 +    ConsoleInfo *infoPtr;
   1.363 +    ConsoleEvent *evPtr;
   1.364 +    int needEvent;
   1.365 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   1.366 +
   1.367 +    if (!(flags & TCL_FILE_EVENTS)) {
   1.368 +	return;
   1.369 +    }
   1.370 +    
   1.371 +    /*
   1.372 +     * Queue events for any ready consoles that don't already have events
   1.373 +     * queued.
   1.374 +     */
   1.375 +
   1.376 +    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL; 
   1.377 +	    infoPtr = infoPtr->nextPtr) {
   1.378 +	if (infoPtr->flags & CONSOLE_PENDING) {
   1.379 +	    continue;
   1.380 +	}
   1.381 +	
   1.382 +	/*
   1.383 +	 * Queue an event if the console is signaled for reading or writing.
   1.384 +	 */
   1.385 +
   1.386 +	needEvent = 0;
   1.387 +	if (infoPtr->watchMask & TCL_WRITABLE) {
   1.388 +	    if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
   1.389 +		needEvent = 1;
   1.390 +	    }
   1.391 +	}
   1.392 +	
   1.393 +	if (infoPtr->watchMask & TCL_READABLE) {
   1.394 +	    if (WaitForRead(infoPtr, 0) >= 0) {
   1.395 +		needEvent = 1;
   1.396 +	    }
   1.397 +	}
   1.398 +
   1.399 +	if (needEvent) {
   1.400 +	    infoPtr->flags |= CONSOLE_PENDING;
   1.401 +	    evPtr = (ConsoleEvent *) ckalloc(sizeof(ConsoleEvent));
   1.402 +	    evPtr->header.proc = ConsoleEventProc;
   1.403 +	    evPtr->infoPtr = infoPtr;
   1.404 +	    Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
   1.405 +	}
   1.406 +    }
   1.407 +}
   1.408 +
   1.409 +
   1.410 +/*
   1.411 + *----------------------------------------------------------------------
   1.412 + *
   1.413 + * ConsoleBlockModeProc --
   1.414 + *
   1.415 + *	Set blocking or non-blocking mode on channel.
   1.416 + *
   1.417 + * Results:
   1.418 + *	0 if successful, errno when failed.
   1.419 + *
   1.420 + * Side effects:
   1.421 + *	Sets the device into blocking or non-blocking mode.
   1.422 + *
   1.423 + *----------------------------------------------------------------------
   1.424 + */
   1.425 +
   1.426 +static int
   1.427 +ConsoleBlockModeProc(
   1.428 +    ClientData instanceData,	/* Instance data for channel. */
   1.429 +    int mode)			/* TCL_MODE_BLOCKING or
   1.430 +                                 * TCL_MODE_NONBLOCKING. */
   1.431 +{
   1.432 +    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
   1.433 +    
   1.434 +    /*
   1.435 +     * Consoles on Windows can not be switched between blocking and nonblocking,
   1.436 +     * hence we have to emulate the behavior. This is done in the input
   1.437 +     * function by checking against a bit in the state. We set or unset the
   1.438 +     * bit here to cause the input function to emulate the correct behavior.
   1.439 +     */
   1.440 +
   1.441 +    if (mode == TCL_MODE_NONBLOCKING) {
   1.442 +	infoPtr->flags |= CONSOLE_ASYNC;
   1.443 +    } else {
   1.444 +	infoPtr->flags &= ~(CONSOLE_ASYNC);
   1.445 +    }
   1.446 +    return 0;
   1.447 +}
   1.448 +
   1.449 +/*
   1.450 + *----------------------------------------------------------------------
   1.451 + *
   1.452 + * ConsoleCloseProc --
   1.453 + *
   1.454 + *	Closes a console based IO channel.
   1.455 + *
   1.456 + * Results:
   1.457 + *	0 on success, errno otherwise.
   1.458 + *
   1.459 + * Side effects:
   1.460 + *	Closes the physical channel.
   1.461 + *
   1.462 + *----------------------------------------------------------------------
   1.463 + */
   1.464 +
   1.465 +static int
   1.466 +ConsoleCloseProc(
   1.467 +    ClientData instanceData,	/* Pointer to ConsoleInfo structure. */
   1.468 +    Tcl_Interp *interp)		/* For error reporting. */
   1.469 +{
   1.470 +    ConsoleInfo *consolePtr = (ConsoleInfo *) instanceData;
   1.471 +    int errorCode;
   1.472 +    ConsoleInfo *infoPtr, **nextPtrPtr;
   1.473 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   1.474 +    DWORD exitCode;
   1.475 +
   1.476 +    errorCode = 0;
   1.477 +    
   1.478 +    /*
   1.479 +     * Clean up the background thread if necessary.  Note that this
   1.480 +     * must be done before we can close the file, since the 
   1.481 +     * thread may be blocking trying to read from the console.
   1.482 +     */
   1.483 +    
   1.484 +    if (consolePtr->readThread) {
   1.485 +
   1.486 +	/*
   1.487 +	 * The thread may already have closed on it's own.  Check it's
   1.488 +	 * exit code.
   1.489 +	 */
   1.490 +
   1.491 +	GetExitCodeThread(consolePtr->readThread, &exitCode);
   1.492 +
   1.493 +	if (exitCode == STILL_ACTIVE) {
   1.494 +
   1.495 +	    /*
   1.496 +	     * Set the stop event so that if the reader thread is blocked
   1.497 +	     * in ConsoleReaderThread on WaitForMultipleEvents, it will exit
   1.498 +	     * cleanly.
   1.499 +	     */
   1.500 +
   1.501 +	    SetEvent(consolePtr->stopReader);
   1.502 +
   1.503 +	    /*
   1.504 +	     * Wait at most 20 milliseconds for the reader thread to close.
   1.505 +	     */
   1.506 +
   1.507 +	    if (WaitForSingleObject(consolePtr->readThread, 20)
   1.508 +		    == WAIT_TIMEOUT) {
   1.509 +		/*
   1.510 +		 * Forcibly terminate the background thread as a last
   1.511 +		 * resort.  Note that we need to guard against
   1.512 +		 * terminating the thread while it is in the middle of
   1.513 +		 * Tcl_ThreadAlert because it won't be able to release
   1.514 +		 * the notifier lock.
   1.515 +		 */
   1.516 +
   1.517 +		Tcl_MutexLock(&consoleMutex);
   1.518 +
   1.519 +		/* BUG: this leaks memory. */
   1.520 +		TerminateThread(consolePtr->readThread, 0);
   1.521 +		Tcl_MutexUnlock(&consoleMutex);
   1.522 +	    }
   1.523 +	}
   1.524 +
   1.525 +	CloseHandle(consolePtr->readThread);
   1.526 +	CloseHandle(consolePtr->readable);
   1.527 +	CloseHandle(consolePtr->startReader);
   1.528 +	CloseHandle(consolePtr->stopReader);
   1.529 +	consolePtr->readThread = NULL;
   1.530 +    }
   1.531 +    consolePtr->validMask &= ~TCL_READABLE;
   1.532 +
   1.533 +    /*
   1.534 +     * Wait for the writer thread to finish the current buffer, then
   1.535 +     * terminate the thread and close the handles.  If the channel is
   1.536 +     * nonblocking, there should be no pending write operations.
   1.537 +     */
   1.538 +    
   1.539 +    if (consolePtr->writeThread) {
   1.540 +	if (consolePtr->toWrite) {
   1.541 +	    /*
   1.542 +	     * We only need to wait if there is something to write.
   1.543 +	     * This may prevent infinite wait on exit. [python bug 216289]
   1.544 +	     */
   1.545 +	    WaitForSingleObject(consolePtr->writable, INFINITE);
   1.546 +	}
   1.547 +
   1.548 +	/*
   1.549 +	 * The thread may already have closed on it's own.  Check it's
   1.550 +	 * exit code.
   1.551 +	 */
   1.552 +
   1.553 +	GetExitCodeThread(consolePtr->writeThread, &exitCode);
   1.554 +
   1.555 +	if (exitCode == STILL_ACTIVE) {
   1.556 +	    /*
   1.557 +	     * Set the stop event so that if the reader thread is blocked
   1.558 +	     * in ConsoleWriterThread on WaitForMultipleEvents, it will
   1.559 +	     * exit cleanly.
   1.560 +	     */
   1.561 +
   1.562 +	    SetEvent(consolePtr->stopWriter);
   1.563 +
   1.564 +	    /*
   1.565 +	     * Wait at most 20 milliseconds for the writer thread to close.
   1.566 +	     */
   1.567 +
   1.568 +	    if (WaitForSingleObject(consolePtr->writeThread, 20)
   1.569 +		    == WAIT_TIMEOUT) {
   1.570 +		/*
   1.571 +		 * Forcibly terminate the background thread as a last
   1.572 +		 * resort.  Note that we need to guard against
   1.573 +		 * terminating the thread while it is in the middle of
   1.574 +		 * Tcl_ThreadAlert because it won't be able to release
   1.575 +		 * the notifier lock.
   1.576 +		 */
   1.577 +
   1.578 +		Tcl_MutexLock(&consoleMutex);
   1.579 +
   1.580 +		/* BUG: this leaks memory. */
   1.581 +		TerminateThread(consolePtr->writeThread, 0);
   1.582 +		Tcl_MutexUnlock(&consoleMutex);
   1.583 +	    }
   1.584 +	}
   1.585 +
   1.586 +	CloseHandle(consolePtr->writeThread);
   1.587 +	CloseHandle(consolePtr->writable);
   1.588 +	CloseHandle(consolePtr->startWriter);
   1.589 +	CloseHandle(consolePtr->stopWriter);
   1.590 +	consolePtr->writeThread = NULL;
   1.591 +    }
   1.592 +    consolePtr->validMask &= ~TCL_WRITABLE;
   1.593 +
   1.594 +
   1.595 +    /*
   1.596 +     * Don't close the Win32 handle if the handle is a standard channel
   1.597 +     * during the thread exit process.  Otherwise, one thread may kill
   1.598 +     * the stdio of another.
   1.599 +     */
   1.600 +
   1.601 +    if (!TclInThreadExit() 
   1.602 +	    || ((GetStdHandle(STD_INPUT_HANDLE) != consolePtr->handle)
   1.603 +		&& (GetStdHandle(STD_OUTPUT_HANDLE) != consolePtr->handle)
   1.604 +		&& (GetStdHandle(STD_ERROR_HANDLE) != consolePtr->handle))) {
   1.605 +	if (CloseHandle(consolePtr->handle) == FALSE) {
   1.606 +	    TclWinConvertError(GetLastError());
   1.607 +	    errorCode = errno;
   1.608 +	}
   1.609 +    }
   1.610 +    
   1.611 +    consolePtr->watchMask &= consolePtr->validMask;
   1.612 +
   1.613 +    /*
   1.614 +     * Remove the file from the list of watched files.
   1.615 +     */
   1.616 +
   1.617 +    for (nextPtrPtr = &(tsdPtr->firstConsolePtr), infoPtr = *nextPtrPtr;
   1.618 +	    infoPtr != NULL;
   1.619 +	    nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
   1.620 +	if (infoPtr == (ConsoleInfo *)consolePtr) {
   1.621 +	    *nextPtrPtr = infoPtr->nextPtr;
   1.622 +	    break;
   1.623 +	}
   1.624 +    }
   1.625 +    if (consolePtr->writeBuf != NULL) {
   1.626 +	ckfree(consolePtr->writeBuf);
   1.627 +	consolePtr->writeBuf = 0;
   1.628 +    }
   1.629 +    ckfree((char*) consolePtr);
   1.630 +
   1.631 +    return errorCode;
   1.632 +}
   1.633 +
   1.634 +/*
   1.635 + *----------------------------------------------------------------------
   1.636 + *
   1.637 + * ConsoleInputProc --
   1.638 + *
   1.639 + *	Reads input from the IO channel into the buffer given. Returns
   1.640 + *	count of how many bytes were actually read, and an error indication.
   1.641 + *
   1.642 + * Results:
   1.643 + *	A count of how many bytes were read is returned and an error
   1.644 + *	indication is returned in an output argument.
   1.645 + *
   1.646 + * Side effects:
   1.647 + *	Reads input from the actual channel.
   1.648 + *
   1.649 + *----------------------------------------------------------------------
   1.650 + */
   1.651 +
   1.652 +static int
   1.653 +ConsoleInputProc(
   1.654 +    ClientData instanceData,		/* Console state. */
   1.655 +    char *buf,				/* Where to store data read. */
   1.656 +    int bufSize,			/* How much space is available
   1.657 +                                         * in the buffer? */
   1.658 +    int *errorCode)			/* Where to store error code. */
   1.659 +{
   1.660 +    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
   1.661 +    DWORD count, bytesRead = 0;
   1.662 +    int result;
   1.663 +
   1.664 +    *errorCode = 0;
   1.665 +
   1.666 +    /*
   1.667 +     * Synchronize with the reader thread.
   1.668 +     */
   1.669 +    
   1.670 +    result = WaitForRead(infoPtr, (infoPtr->flags & CONSOLE_ASYNC) ? 0 : 1);
   1.671 +    
   1.672 +    /*
   1.673 +     * If an error occurred, return immediately.
   1.674 +     */
   1.675 +    
   1.676 +    if (result == -1) {
   1.677 +	*errorCode = errno;
   1.678 +	return -1;
   1.679 +    }
   1.680 +
   1.681 +    if (infoPtr->readFlags & CONSOLE_BUFFERED) {
   1.682 +	/*
   1.683 +	 * Data is stored in the buffer.
   1.684 +	 */
   1.685 +
   1.686 +	if (bufSize < (infoPtr->bytesRead - infoPtr->offset)) {
   1.687 +	    memcpy(buf, &infoPtr->buffer[infoPtr->offset], (size_t) bufSize);
   1.688 +	    bytesRead = bufSize;
   1.689 +	    infoPtr->offset += bufSize;
   1.690 +	} else {
   1.691 +	    memcpy(buf, &infoPtr->buffer[infoPtr->offset], (size_t) bufSize);
   1.692 +	    bytesRead = infoPtr->bytesRead - infoPtr->offset;
   1.693 +
   1.694 +	    /*
   1.695 +	     * Reset the buffer
   1.696 +	     */
   1.697 +	    
   1.698 +	    infoPtr->readFlags &= ~CONSOLE_BUFFERED;
   1.699 +	    infoPtr->offset = 0;
   1.700 +	}
   1.701 +
   1.702 +	return bytesRead;
   1.703 +    }
   1.704 +    
   1.705 +    /*
   1.706 +     * Attempt to read bufSize bytes.  The read will return immediately
   1.707 +     * if there is any data available.  Otherwise it will block until
   1.708 +     * at least one byte is available or an EOF occurs.
   1.709 +     */
   1.710 +
   1.711 +    if (ReadConsole(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &count,
   1.712 +		    (LPOVERLAPPED) NULL) == TRUE) {
   1.713 +	buf[count] = '\0';
   1.714 +	return count;
   1.715 +    }
   1.716 +
   1.717 +    return -1;
   1.718 +}
   1.719 +
   1.720 +/*
   1.721 + *----------------------------------------------------------------------
   1.722 + *
   1.723 + * ConsoleOutputProc --
   1.724 + *
   1.725 + *	Writes the given output on the IO channel. Returns count of how
   1.726 + *	many characters were actually written, and an error indication.
   1.727 + *
   1.728 + * Results:
   1.729 + *	A count of how many characters were written is returned and an
   1.730 + *	error indication is returned in an output argument.
   1.731 + *
   1.732 + * Side effects:
   1.733 + *	Writes output on the actual channel.
   1.734 + *
   1.735 + *----------------------------------------------------------------------
   1.736 + */
   1.737 +
   1.738 +static int
   1.739 +ConsoleOutputProc(
   1.740 +    ClientData instanceData,		/* Console state. */
   1.741 +    CONST char *buf,			/* The data buffer. */
   1.742 +    int toWrite,			/* How many bytes to write? */
   1.743 +    int *errorCode)			/* Where to store error code. */
   1.744 +{
   1.745 +    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
   1.746 +    DWORD bytesWritten, timeout;
   1.747 +    
   1.748 +    *errorCode = 0;
   1.749 +    timeout = (infoPtr->flags & CONSOLE_ASYNC) ? 0 : INFINITE;
   1.750 +    if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) {
   1.751 +	/*
   1.752 +	 * The writer thread is blocked waiting for a write to complete
   1.753 +	 * and the channel is in non-blocking mode.
   1.754 +	 */
   1.755 +
   1.756 +	errno = EAGAIN;
   1.757 +	goto error;
   1.758 +    }
   1.759 +    
   1.760 +    /*
   1.761 +     * Check for a background error on the last write.
   1.762 +     */
   1.763 +
   1.764 +    if (infoPtr->writeError) {
   1.765 +	TclWinConvertError(infoPtr->writeError);
   1.766 +	infoPtr->writeError = 0;
   1.767 +	goto error;
   1.768 +    }
   1.769 +
   1.770 +    if (infoPtr->flags & CONSOLE_ASYNC) {
   1.771 +	/*
   1.772 +	 * The console is non-blocking, so copy the data into the output
   1.773 +	 * buffer and restart the writer thread.
   1.774 +	 */
   1.775 +
   1.776 +	if (toWrite > infoPtr->writeBufLen) {
   1.777 +	    /*
   1.778 +	     * Reallocate the buffer to be large enough to hold the data.
   1.779 +	     */
   1.780 +
   1.781 +	    if (infoPtr->writeBuf) {
   1.782 +		ckfree(infoPtr->writeBuf);
   1.783 +	    }
   1.784 +	    infoPtr->writeBufLen = toWrite;
   1.785 +	    infoPtr->writeBuf = ckalloc((unsigned int) toWrite);
   1.786 +	}
   1.787 +	memcpy(infoPtr->writeBuf, buf, (size_t) toWrite);
   1.788 +	infoPtr->toWrite = toWrite;
   1.789 +	ResetEvent(infoPtr->writable);
   1.790 +	SetEvent(infoPtr->startWriter);
   1.791 +	bytesWritten = toWrite;
   1.792 +    } else {
   1.793 +	/*
   1.794 +	 * In the blocking case, just try to write the buffer directly.
   1.795 +	 * This avoids an unnecessary copy.
   1.796 +	 */
   1.797 +
   1.798 +	if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite,
   1.799 +		&bytesWritten, (LPOVERLAPPED) NULL) == FALSE) {
   1.800 +	    TclWinConvertError(GetLastError());
   1.801 +	    goto error;
   1.802 +	}
   1.803 +    }
   1.804 +    return bytesWritten;
   1.805 +
   1.806 +    error:
   1.807 +    *errorCode = errno;
   1.808 +    return -1;
   1.809 +
   1.810 +}
   1.811 +
   1.812 +/*
   1.813 + *----------------------------------------------------------------------
   1.814 + *
   1.815 + * ConsoleEventProc --
   1.816 + *
   1.817 + *	This function is invoked by Tcl_ServiceEvent when a file event
   1.818 + *	reaches the front of the event queue.  This procedure invokes
   1.819 + *	Tcl_NotifyChannel on the console.
   1.820 + *
   1.821 + * Results:
   1.822 + *	Returns 1 if the event was handled, meaning it should be removed
   1.823 + *	from the queue.  Returns 0 if the event was not handled, meaning
   1.824 + *	it should stay on the queue.  The only time the event isn't
   1.825 + *	handled is if the TCL_FILE_EVENTS flag bit isn't set.
   1.826 + *
   1.827 + * Side effects:
   1.828 + *	Whatever the notifier callback does.
   1.829 + *
   1.830 + *----------------------------------------------------------------------
   1.831 + */
   1.832 +
   1.833 +static int
   1.834 +ConsoleEventProc(
   1.835 +    Tcl_Event *evPtr,		/* Event to service. */
   1.836 +    int flags)			/* Flags that indicate what events to
   1.837 +				 * handle, such as TCL_FILE_EVENTS. */
   1.838 +{
   1.839 +    ConsoleEvent *consoleEvPtr = (ConsoleEvent *)evPtr;
   1.840 +    ConsoleInfo *infoPtr;
   1.841 +    int mask;
   1.842 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   1.843 +
   1.844 +    if (!(flags & TCL_FILE_EVENTS)) {
   1.845 +	return 0;
   1.846 +    }
   1.847 +
   1.848 +    /*
   1.849 +     * Search through the list of watched consoles for the one whose handle
   1.850 +     * matches the event.  We do this rather than simply dereferencing
   1.851 +     * the handle in the event so that consoles can be deleted while the
   1.852 +     * event is in the queue.
   1.853 +     */
   1.854 +
   1.855 +    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;
   1.856 +	    infoPtr = infoPtr->nextPtr) {
   1.857 +	if (consoleEvPtr->infoPtr == infoPtr) {
   1.858 +	    infoPtr->flags &= ~(CONSOLE_PENDING);
   1.859 +	    break;
   1.860 +	}
   1.861 +    }
   1.862 +
   1.863 +    /*
   1.864 +     * Remove stale events.
   1.865 +     */
   1.866 +
   1.867 +    if (!infoPtr) {
   1.868 +	return 1;
   1.869 +    }
   1.870 +
   1.871 +    /*
   1.872 +     * Check to see if the console is readable.  Note
   1.873 +     * that we can't tell if a console is writable, so we always report it
   1.874 +     * as being writable unless we have detected EOF.
   1.875 +     */
   1.876 +
   1.877 +    mask = 0;
   1.878 +    if (infoPtr->watchMask & TCL_WRITABLE) {
   1.879 +	if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
   1.880 +	    mask = TCL_WRITABLE;
   1.881 +	}
   1.882 +    }
   1.883 +
   1.884 +    if (infoPtr->watchMask & TCL_READABLE) {
   1.885 +	if (WaitForRead(infoPtr, 0) >= 0) {
   1.886 +	    if (infoPtr->readFlags & CONSOLE_EOF) {
   1.887 +		mask = TCL_READABLE;
   1.888 +	    } else {
   1.889 +		mask |= TCL_READABLE;
   1.890 +	    }
   1.891 +	} 
   1.892 +    }
   1.893 +
   1.894 +    /*
   1.895 +     * Inform the channel of the events.
   1.896 +     */
   1.897 +
   1.898 +    Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
   1.899 +    return 1;
   1.900 +}
   1.901 +
   1.902 +/*
   1.903 + *----------------------------------------------------------------------
   1.904 + *
   1.905 + * ConsoleWatchProc --
   1.906 + *
   1.907 + *	Called by the notifier to set up to watch for events on this
   1.908 + *	channel.
   1.909 + *
   1.910 + * Results:
   1.911 + *	None.
   1.912 + *
   1.913 + * Side effects:
   1.914 + *	None.
   1.915 + *
   1.916 + *----------------------------------------------------------------------
   1.917 + */
   1.918 +
   1.919 +static void
   1.920 +ConsoleWatchProc(
   1.921 +    ClientData instanceData,		/* Console state. */
   1.922 +    int mask)				/* What events to watch for, OR-ed
   1.923 +                                         * combination of TCL_READABLE,
   1.924 +                                         * TCL_WRITABLE and TCL_EXCEPTION. */
   1.925 +{
   1.926 +    ConsoleInfo **nextPtrPtr, *ptr;
   1.927 +    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
   1.928 +    int oldMask = infoPtr->watchMask;
   1.929 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   1.930 +
   1.931 +    /*
   1.932 +     * Since most of the work is handled by the background threads,
   1.933 +     * we just need to update the watchMask and then force the notifier
   1.934 +     * to poll once. 
   1.935 +     */
   1.936 +
   1.937 +    infoPtr->watchMask = mask & infoPtr->validMask;
   1.938 +    if (infoPtr->watchMask) {
   1.939 +	Tcl_Time blockTime = { 0, 0 };
   1.940 +	if (!oldMask) {
   1.941 +	    infoPtr->nextPtr = tsdPtr->firstConsolePtr;
   1.942 +	    tsdPtr->firstConsolePtr = infoPtr;
   1.943 +	}
   1.944 +	Tcl_SetMaxBlockTime(&blockTime);
   1.945 +    } else {
   1.946 +	if (oldMask) {
   1.947 +	    /*
   1.948 +	     * Remove the console from the list of watched consoles.
   1.949 +	     */
   1.950 +
   1.951 +	    for (nextPtrPtr = &(tsdPtr->firstConsolePtr), ptr = *nextPtrPtr;
   1.952 +		 ptr != NULL;
   1.953 +		 nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
   1.954 +		if (infoPtr == ptr) {
   1.955 +		    *nextPtrPtr = ptr->nextPtr;
   1.956 +		    break;
   1.957 +		}
   1.958 +	    }
   1.959 +	}
   1.960 +    }
   1.961 +}
   1.962 +
   1.963 +/*
   1.964 + *----------------------------------------------------------------------
   1.965 + *
   1.966 + * ConsoleGetHandleProc --
   1.967 + *
   1.968 + *	Called from Tcl_GetChannelHandle to retrieve OS handles from
   1.969 + *	inside a command consoleline based channel.
   1.970 + *
   1.971 + * Results:
   1.972 + *	Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
   1.973 + *	there is no handle for the specified direction. 
   1.974 + *
   1.975 + * Side effects:
   1.976 + *	None.
   1.977 + *
   1.978 + *----------------------------------------------------------------------
   1.979 + */
   1.980 +
   1.981 +static int
   1.982 +ConsoleGetHandleProc(
   1.983 +    ClientData instanceData,	/* The console state. */
   1.984 +    int direction,		/* TCL_READABLE or TCL_WRITABLE */
   1.985 +    ClientData *handlePtr)	/* Where to store the handle.  */
   1.986 +{
   1.987 +    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
   1.988 +
   1.989 +    *handlePtr = (ClientData) infoPtr->handle;
   1.990 +    return TCL_OK;
   1.991 +}
   1.992 +
   1.993 +/*
   1.994 + *----------------------------------------------------------------------
   1.995 + *
   1.996 + * WaitForRead --
   1.997 + *
   1.998 + *	Wait until some data is available, the console is at
   1.999 + *	EOF or the reader thread is blocked waiting for data (if the
  1.1000 + *	channel is in non-blocking mode).
  1.1001 + *
  1.1002 + * Results:
  1.1003 + *	Returns 1 if console is readable.  Returns 0 if there is no data
  1.1004 + *	on the console, but there is buffered data.  Returns -1 if an
  1.1005 + *	error occurred.  If an error occurred, the threads may not
  1.1006 + *	be synchronized.
  1.1007 + *
  1.1008 + * Side effects:
  1.1009 + *	Updates the shared state flags.  If no error occurred,
  1.1010 + *      the reader thread is blocked waiting for a signal from the 
  1.1011 + *      main thread.
  1.1012 + *
  1.1013 + *----------------------------------------------------------------------
  1.1014 + */
  1.1015 +
  1.1016 +static int
  1.1017 +WaitForRead(
  1.1018 +    ConsoleInfo *infoPtr,		/* Console state. */
  1.1019 +    int blocking)		/* Indicates whether call should be
  1.1020 +				 * blocking or not. */
  1.1021 +{
  1.1022 +    DWORD timeout, count;
  1.1023 +    HANDLE *handle = infoPtr->handle;
  1.1024 +    INPUT_RECORD input;
  1.1025 +    
  1.1026 +    while (1) {
  1.1027 +	/*
  1.1028 +	 * Synchronize with the reader thread.
  1.1029 +	 */
  1.1030 +       
  1.1031 +	timeout = blocking ? INFINITE : 0;
  1.1032 +	if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) {
  1.1033 +	    /*
  1.1034 +	     * The reader thread is blocked waiting for data and the channel
  1.1035 +	     * is in non-blocking mode.
  1.1036 +	     */
  1.1037 +	    errno = EAGAIN;
  1.1038 +	    return -1;
  1.1039 +	}
  1.1040 +	
  1.1041 +	/*
  1.1042 +	 * At this point, the two threads are synchronized, so it is safe
  1.1043 +	 * to access shared state.
  1.1044 +	 */
  1.1045 +	
  1.1046 +	/*
  1.1047 +	 * If the console has hit EOF, it is always readable.
  1.1048 +	 */
  1.1049 +	
  1.1050 +	if (infoPtr->readFlags & CONSOLE_EOF) {
  1.1051 +	    return 1;
  1.1052 +	}
  1.1053 +	
  1.1054 +	if (PeekConsoleInput(handle, &input, 1, &count) == FALSE) {
  1.1055 +            /*
  1.1056 +	     * Check to see if the peek failed because of EOF.
  1.1057 +	     */
  1.1058 +	    
  1.1059 +	    TclWinConvertError(GetLastError());
  1.1060 +	    
  1.1061 +	    if (errno == EOF) {
  1.1062 +		infoPtr->readFlags |= CONSOLE_EOF;
  1.1063 +		return 1;
  1.1064 +	    }
  1.1065 +
  1.1066 +	    /*
  1.1067 +	     * Ignore errors if there is data in the buffer.
  1.1068 +	     */
  1.1069 +	    
  1.1070 +	    if (infoPtr->readFlags & CONSOLE_BUFFERED) {
  1.1071 +		return 0;
  1.1072 +	    } else {
  1.1073 +		return -1;
  1.1074 +	    }
  1.1075 +	}
  1.1076 +
  1.1077 +	/*
  1.1078 +	 * If there is data in the buffer, the console must be
  1.1079 +	 * readable (since it is a line-oriented device).
  1.1080 +	 */
  1.1081 +
  1.1082 +	if (infoPtr->readFlags & CONSOLE_BUFFERED) {
  1.1083 +	    return 1;
  1.1084 +	}
  1.1085 +
  1.1086 +	
  1.1087 +	/*
  1.1088 +	 * There wasn't any data available, so reset the thread and
  1.1089 +	 * try again.
  1.1090 +	 */
  1.1091 +    
  1.1092 +	ResetEvent(infoPtr->readable);
  1.1093 +	SetEvent(infoPtr->startReader);
  1.1094 +    }
  1.1095 +}
  1.1096 +
  1.1097 +/*
  1.1098 + *----------------------------------------------------------------------
  1.1099 + *
  1.1100 + * ConsoleReaderThread --
  1.1101 + *
  1.1102 + *	This function runs in a separate thread and waits for input
  1.1103 + *	to become available on a console.
  1.1104 + *
  1.1105 + * Results:
  1.1106 + *	None.
  1.1107 + *
  1.1108 + * Side effects:
  1.1109 + *	Signals the main thread when input become available.  May
  1.1110 + *	cause the main thread to wake up by posting a message.  May
  1.1111 + *	one line from the console for each wait operation.
  1.1112 + *
  1.1113 + *----------------------------------------------------------------------
  1.1114 + */
  1.1115 +
  1.1116 +static DWORD WINAPI
  1.1117 +ConsoleReaderThread(LPVOID arg)
  1.1118 +{
  1.1119 +    ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
  1.1120 +    HANDLE *handle = infoPtr->handle;
  1.1121 +    DWORD count, waitResult;
  1.1122 +    HANDLE wEvents[2];
  1.1123 +
  1.1124 +    /* The first event takes precedence. */
  1.1125 +    wEvents[0] = infoPtr->stopReader;
  1.1126 +    wEvents[1] = infoPtr->startReader;
  1.1127 +
  1.1128 +    for (;;) {
  1.1129 +	/*
  1.1130 +	 * Wait for the main thread to signal before attempting to wait.
  1.1131 +	 */
  1.1132 +
  1.1133 +	waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
  1.1134 +
  1.1135 +	if (waitResult != (WAIT_OBJECT_0 + 1)) {
  1.1136 +	    /*
  1.1137 +	     * The start event was not signaled.  It must be the stop event
  1.1138 +	     * or an error, so exit this thread.
  1.1139 +	     */
  1.1140 +
  1.1141 +	    break;
  1.1142 +	}
  1.1143 +
  1.1144 +	count = 0;
  1.1145 +
  1.1146 +	/* 
  1.1147 +	 * Look for data on the console, but first ignore any events
  1.1148 +	 * that are not KEY_EVENTs 
  1.1149 +	 */
  1.1150 +	if (ReadConsoleA(handle, infoPtr->buffer, CONSOLE_BUFFER_SIZE,
  1.1151 +		(LPDWORD) &infoPtr->bytesRead, NULL) != FALSE) {
  1.1152 +	    /*
  1.1153 +	     * Data was stored in the buffer.
  1.1154 +	     */
  1.1155 +	    
  1.1156 +	    infoPtr->readFlags |= CONSOLE_BUFFERED;
  1.1157 +	} else {
  1.1158 +	    DWORD err;
  1.1159 +	    err = GetLastError();
  1.1160 +	    
  1.1161 +	    if (err == EOF) {
  1.1162 +		infoPtr->readFlags = CONSOLE_EOF;
  1.1163 +	    }
  1.1164 +	}
  1.1165 +
  1.1166 +	/*
  1.1167 +	 * Signal the main thread by signalling the readable event and
  1.1168 +	 * then waking up the notifier thread.
  1.1169 +	 */
  1.1170 +
  1.1171 +	SetEvent(infoPtr->readable);
  1.1172 +
  1.1173 +	/*
  1.1174 +	 * Alert the foreground thread.  Note that we need to treat this like
  1.1175 +	 * a critical section so the foreground thread does not terminate
  1.1176 +	 * this thread while we are holding a mutex in the notifier code.
  1.1177 +	 */
  1.1178 +
  1.1179 +	Tcl_MutexLock(&consoleMutex);
  1.1180 +	if (infoPtr->threadId != NULL) {
  1.1181 +	    /* TIP #218. When in flight ignore the event, no one will receive it anyway */
  1.1182 +	    Tcl_ThreadAlert(infoPtr->threadId);
  1.1183 +	}
  1.1184 +	Tcl_MutexUnlock(&consoleMutex);
  1.1185 +    }
  1.1186 +
  1.1187 +    return 0;
  1.1188 +}
  1.1189 +
  1.1190 +/*
  1.1191 + *----------------------------------------------------------------------
  1.1192 + *
  1.1193 + * ConsoleWriterThread --
  1.1194 + *
  1.1195 + *	This function runs in a separate thread and writes data
  1.1196 + *	onto a console.
  1.1197 + *
  1.1198 + * Results:
  1.1199 + *	Always returns 0.
  1.1200 + *
  1.1201 + * Side effects:
  1.1202 + *	Signals the main thread when an output operation is completed.
  1.1203 + *	May cause the main thread to wake up by posting a message.  
  1.1204 + *
  1.1205 + *----------------------------------------------------------------------
  1.1206 + */
  1.1207 +
  1.1208 +static DWORD WINAPI
  1.1209 +ConsoleWriterThread(LPVOID arg)
  1.1210 +{
  1.1211 +
  1.1212 +    ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
  1.1213 +    HANDLE *handle = infoPtr->handle;
  1.1214 +    DWORD count, toWrite, waitResult;
  1.1215 +    char *buf;
  1.1216 +    HANDLE wEvents[2];
  1.1217 +
  1.1218 +    /* The first event takes precedence. */
  1.1219 +    wEvents[0] = infoPtr->stopWriter;
  1.1220 +    wEvents[1] = infoPtr->startWriter;
  1.1221 +
  1.1222 +    for (;;) {
  1.1223 +	/*
  1.1224 +	 * Wait for the main thread to signal before attempting to write.
  1.1225 +	 */
  1.1226 +
  1.1227 +	waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
  1.1228 +
  1.1229 +	if (waitResult != (WAIT_OBJECT_0 + 1)) {
  1.1230 +	    /*
  1.1231 +	     * The start event was not signaled.  It must be the stop event
  1.1232 +	     * or an error, so exit this thread.
  1.1233 +	     */
  1.1234 +
  1.1235 +	    break;
  1.1236 +	}
  1.1237 +
  1.1238 +	buf = infoPtr->writeBuf;
  1.1239 +	toWrite = infoPtr->toWrite;
  1.1240 +
  1.1241 +	/*
  1.1242 +	 * Loop until all of the bytes are written or an error occurs.
  1.1243 +	 */
  1.1244 +
  1.1245 +	while (toWrite > 0) {
  1.1246 +	    if (WriteConsoleA(handle, buf, toWrite, &count, NULL) == FALSE) {
  1.1247 +		infoPtr->writeError = GetLastError();
  1.1248 +		break;
  1.1249 +	    } else {
  1.1250 +		toWrite -= count;
  1.1251 +		buf += count;
  1.1252 +	    }
  1.1253 +	}
  1.1254 +
  1.1255 +	/*
  1.1256 +	 * Signal the main thread by signalling the writable event and
  1.1257 +	 * then waking up the notifier thread.
  1.1258 +	 */
  1.1259 +	
  1.1260 +	SetEvent(infoPtr->writable);
  1.1261 +
  1.1262 +	/*
  1.1263 +	 * Alert the foreground thread.  Note that we need to treat this like
  1.1264 +	 * a critical section so the foreground thread does not terminate
  1.1265 +	 * this thread while we are holding a mutex in the notifier code.
  1.1266 +	 */
  1.1267 +
  1.1268 +	Tcl_MutexLock(&consoleMutex);
  1.1269 +	if (infoPtr->threadId != NULL) {
  1.1270 +	    /* TIP #218. When in flight ignore the event, no one will receive it anyway */
  1.1271 +	    Tcl_ThreadAlert(infoPtr->threadId);
  1.1272 +	}
  1.1273 +	Tcl_MutexUnlock(&consoleMutex);
  1.1274 +    }
  1.1275 +
  1.1276 +    return 0;
  1.1277 +}
  1.1278 +
  1.1279 +
  1.1280 +
  1.1281 +/*
  1.1282 + *----------------------------------------------------------------------
  1.1283 + *
  1.1284 + * TclWinOpenConsoleChannel --
  1.1285 + *
  1.1286 + *	Constructs a Console channel for the specified standard OS handle.
  1.1287 + *      This is a helper function to break up the construction of 
  1.1288 + *      channels into File, Console, or Serial.
  1.1289 + *
  1.1290 + * Results:
  1.1291 + *	Returns the new channel, or NULL.
  1.1292 + *
  1.1293 + * Side effects:
  1.1294 + *	May open the channel
  1.1295 + *
  1.1296 + *----------------------------------------------------------------------
  1.1297 + */
  1.1298 +
  1.1299 +Tcl_Channel
  1.1300 +TclWinOpenConsoleChannel(handle, channelName, permissions)
  1.1301 +    HANDLE handle;
  1.1302 +    char *channelName;
  1.1303 +    int permissions;
  1.1304 +{
  1.1305 +    char encoding[4 + TCL_INTEGER_SPACE];
  1.1306 +    ConsoleInfo *infoPtr;
  1.1307 +    DWORD id, modes;
  1.1308 +
  1.1309 +    ConsoleInit();
  1.1310 +
  1.1311 +    /*
  1.1312 +     * See if a channel with this handle already exists.
  1.1313 +     */
  1.1314 +    
  1.1315 +    infoPtr = (ConsoleInfo *) ckalloc((unsigned) sizeof(ConsoleInfo));
  1.1316 +    memset(infoPtr, 0, sizeof(ConsoleInfo));
  1.1317 +
  1.1318 +    infoPtr->validMask = permissions;
  1.1319 +    infoPtr->handle = handle;
  1.1320 +    infoPtr->channel = (Tcl_Channel) NULL;
  1.1321 +
  1.1322 +    wsprintfA(encoding, "cp%d", GetConsoleCP());
  1.1323 +
  1.1324 +    infoPtr->threadId = Tcl_GetCurrentThread();
  1.1325 +
  1.1326 +    /*
  1.1327 +     * Use the pointer for the name of the result channel.
  1.1328 +     * This keeps the channel names unique, since some may share
  1.1329 +     * handles (stdin/stdout/stderr for instance).
  1.1330 +     */
  1.1331 +
  1.1332 +    wsprintfA(channelName, "file%lx", (int) infoPtr);
  1.1333 +    
  1.1334 +    infoPtr->channel = Tcl_CreateChannel(&consoleChannelType, channelName,
  1.1335 +            (ClientData) infoPtr, permissions);
  1.1336 +
  1.1337 +    if (permissions & TCL_READABLE) {
  1.1338 +	/*
  1.1339 +	 * Make sure the console input buffer is ready for only character
  1.1340 +	 * input notifications and the buffer is set for line buffering.
  1.1341 +	 * IOW, we only want to catch when complete lines are ready for
  1.1342 +	 * reading.
  1.1343 +	 */
  1.1344 +	GetConsoleMode(infoPtr->handle, &modes);
  1.1345 +	modes &= ~(ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
  1.1346 +	modes |= ENABLE_LINE_INPUT;
  1.1347 +	SetConsoleMode(infoPtr->handle, modes);
  1.1348 +
  1.1349 +	infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL);
  1.1350 +	infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL);
  1.1351 +	infoPtr->stopReader = CreateEvent(NULL, FALSE, FALSE, NULL);
  1.1352 +	infoPtr->readThread = CreateThread(NULL, 256, ConsoleReaderThread,
  1.1353 +	        infoPtr, 0, &id);
  1.1354 +	SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST);
  1.1355 +    }
  1.1356 +
  1.1357 +    if (permissions & TCL_WRITABLE) {
  1.1358 +	infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL);
  1.1359 +	infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
  1.1360 +	infoPtr->stopWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
  1.1361 +	infoPtr->writeThread = CreateThread(NULL, 256, ConsoleWriterThread,
  1.1362 +	        infoPtr, 0, &id);
  1.1363 +	SetThreadPriority(infoPtr->writeThread, THREAD_PRIORITY_HIGHEST);
  1.1364 +    }
  1.1365 +
  1.1366 +    /*
  1.1367 +     * Files have default translation of AUTO and ^Z eof char, which
  1.1368 +     * means that a ^Z will be accepted as EOF when reading.
  1.1369 +     */
  1.1370 +    
  1.1371 +    Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
  1.1372 +    Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
  1.1373 +    Tcl_SetChannelOption(NULL, infoPtr->channel, "-encoding", encoding);
  1.1374 +
  1.1375 +    return infoPtr->channel;
  1.1376 +}
  1.1377 +
  1.1378 +/*
  1.1379 + *----------------------------------------------------------------------
  1.1380 + *
  1.1381 + * ConsoleThreadActionProc --
  1.1382 + *
  1.1383 + *	Insert or remove any thread local refs to this channel.
  1.1384 + *
  1.1385 + * Results:
  1.1386 + *	None.
  1.1387 + *
  1.1388 + * Side effects:
  1.1389 + *	Changes thread local list of valid channels.
  1.1390 + *
  1.1391 + *----------------------------------------------------------------------
  1.1392 + */
  1.1393 +
  1.1394 +static void
  1.1395 +ConsoleThreadActionProc (instanceData, action)
  1.1396 +     ClientData instanceData;
  1.1397 +     int action;
  1.1398 +{
  1.1399 +    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
  1.1400 +
  1.1401 +    /* We do not access firstConsolePtr in the thread structures. This is
  1.1402 +     * not for all serials managed by the thread, but only those we are
  1.1403 +     * watching. Removal of the filevent handlers before transfer thus
  1.1404 +     * takes care of this structure.
  1.1405 +     */
  1.1406 +
  1.1407 +    Tcl_MutexLock(&consoleMutex);
  1.1408 +    if (action == TCL_CHANNEL_THREAD_INSERT) {
  1.1409 +        /* We can't copy the thread information from the channel when
  1.1410 +	 * the channel is created. At this time the channel back
  1.1411 +	 * pointer has not been set yet. However in that case the
  1.1412 +	 * threadId has already been set by TclpCreateCommandChannel
  1.1413 +	 * itself, so the structure is still good.
  1.1414 +	 */
  1.1415 +
  1.1416 +        ConsoleInit ();
  1.1417 +        if (infoPtr->channel != NULL) {
  1.1418 +	    infoPtr->threadId = Tcl_GetChannelThread (infoPtr->channel);
  1.1419 +	}
  1.1420 +    } else {
  1.1421 +	infoPtr->threadId = NULL;
  1.1422 +    }
  1.1423 +    Tcl_MutexUnlock(&consoleMutex);
  1.1424 +}