1/*
2 * tclWinConsole.c --
3 *
4 *	This file implements the Windows-specific console functions,
5 *	and the "console" channel driver.
6 *
7 * Copyright (c) 1999 by Scriptics Corp.
8 *
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id: tclWinConsole.c,v 1.11.2.3 2006/03/28 21:02:37 hobbs Exp $
13 */
14
15#include "tclWinInt.h"
16
17#include <fcntl.h>
18#include <io.h>
19#include <sys/stat.h>
20
21/*
22 * The following variable is used to tell whether this module has been
23 * initialized.
24 */
25
26static int initialized = 0;
27
28/*
29 * The consoleMutex locks around access to the initialized variable, and it is
30 * used to protect background threads from being terminated while they are
31 * using APIs that hold locks.
32 */
33
34TCL_DECLARE_MUTEX(consoleMutex)
35
36/*
37 * Bit masks used in the flags field of the ConsoleInfo structure below.
38 */
39
40#define CONSOLE_PENDING	(1<<0)	/* Message is pending in the queue. */
41#define CONSOLE_ASYNC	(1<<1)	/* Channel is non-blocking. */
42
43/*
44 * Bit masks used in the sharedFlags field of the ConsoleInfo structure below.
45 */
46
47#define CONSOLE_EOF	  (1<<2)  /* Console has reached EOF. */
48#define CONSOLE_BUFFERED  (1<<3)  /* data was read into a buffer by the reader
49				     thread */
50
51#define CONSOLE_BUFFER_SIZE (8*1024)
52/*
53 * This structure describes per-instance data for a console based channel.
54 */
55
56typedef struct ConsoleInfo {
57    HANDLE handle;
58    int type;
59    struct ConsoleInfo *nextPtr;/* Pointer to next registered console. */
60    Tcl_Channel channel;	/* Pointer to channel structure. */
61    int validMask;		/* OR'ed combination of TCL_READABLE,
62				 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
63				 * which operations are valid on the file. */
64    int watchMask;		/* OR'ed combination of TCL_READABLE,
65				 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
66				 * which events should be reported. */
67    int flags;			/* State flags, see above for a list. */
68    Tcl_ThreadId threadId;	/* Thread to which events should be reported.
69				 * This value is used by the reader/writer
70				 * threads. */
71    HANDLE writeThread;		/* Handle to writer thread. */
72    HANDLE readThread;		/* Handle to reader thread. */
73    HANDLE writable;		/* Manual-reset event to signal when the
74				 * writer thread has finished waiting for
75				 * the current buffer to be written. */
76    HANDLE readable;		/* Manual-reset event to signal when the
77				 * reader thread has finished waiting for
78				 * input. */
79    HANDLE startWriter;		/* Auto-reset event used by the main thread to
80				 * signal when the writer thread should attempt
81				 * to write to the console. */
82    HANDLE stopWriter;		/* Auto-reset event used by the main thread to
83				 * signal when the writer thread should exit.
84				 */
85    HANDLE startReader;		/* Auto-reset event used by the main thread to
86				 * signal when the reader thread should attempt
87				 * to read from the console. */
88    HANDLE stopReader;		/* Auto-reset event used by the main thread to
89				 * signal when the reader thread should exit.
90				 */
91    DWORD writeError;		/* An error caused by the last background
92				 * write.  Set to 0 if no error has been
93				 * detected.  This word is shared with the
94				 * writer thread so access must be
95				 * synchronized with the writable object.
96				 */
97    char *writeBuf;		/* Current background output buffer.
98				 * Access is synchronized with the writable
99				 * object. */
100    int writeBufLen;		/* Size of write buffer.  Access is
101				 * synchronized with the writable
102				 * object. */
103    int toWrite;		/* Current amount to be written.  Access is
104				 * synchronized with the writable object. */
105    int readFlags;		/* Flags that are shared with the reader
106				 * thread.  Access is synchronized with the
107				 * readable object.  */
108    int bytesRead;              /* number of bytes in the buffer */
109    int offset;                 /* number of bytes read out of the buffer */
110    char buffer[CONSOLE_BUFFER_SIZE];
111                                /* Data consumed by reader thread. */
112} ConsoleInfo;
113
114typedef struct ThreadSpecificData {
115    /*
116     * The following pointer refers to the head of the list of consoles
117     * that are being watched for file events.
118     */
119
120    ConsoleInfo *firstConsolePtr;
121} ThreadSpecificData;
122
123static Tcl_ThreadDataKey dataKey;
124
125/*
126 * The following structure is what is added to the Tcl event queue when
127 * console events are generated.
128 */
129
130typedef struct ConsoleEvent {
131    Tcl_Event header;		/* Information that is standard for
132				 * all events. */
133    ConsoleInfo *infoPtr;	/* Pointer to console info structure.  Note
134				 * that we still have to verify that the
135				 * console exists before dereferencing this
136				 * pointer. */
137} ConsoleEvent;
138
139/*
140 * Declarations for functions used only in this file.
141 */
142
143static int		ConsoleBlockModeProc(ClientData instanceData, int mode);
144static void		ConsoleCheckProc(ClientData clientData, int flags);
145static int		ConsoleCloseProc(ClientData instanceData,
146			    Tcl_Interp *interp);
147static int		ConsoleEventProc(Tcl_Event *evPtr, int flags);
148static void		ConsoleExitHandler(ClientData clientData);
149static int		ConsoleGetHandleProc(ClientData instanceData,
150			    int direction, ClientData *handlePtr);
151static void             ConsoleInit(void);
152static int		ConsoleInputProc(ClientData instanceData, char *buf,
153			    int toRead, int *errorCode);
154static int		ConsoleOutputProc(ClientData instanceData,
155			    CONST char *buf, int toWrite, int *errorCode);
156static DWORD WINAPI	ConsoleReaderThread(LPVOID arg);
157static void		ConsoleSetupProc(ClientData clientData, int flags);
158static void		ConsoleWatchProc(ClientData instanceData, int mask);
159static DWORD WINAPI	ConsoleWriterThread(LPVOID arg);
160static void		ProcExitHandler(ClientData clientData);
161static int		WaitForRead(ConsoleInfo *infoPtr, int blocking);
162
163static void             ConsoleThreadActionProc _ANSI_ARGS_ ((
164			   ClientData instanceData, int action));
165
166/*
167 * This structure describes the channel type structure for command console
168 * based IO.
169 */
170
171static Tcl_ChannelType consoleChannelType = {
172    "console",			/* Type name. */
173    TCL_CHANNEL_VERSION_4,	/* v4 channel */
174    ConsoleCloseProc,		/* Close proc. */
175    ConsoleInputProc,		/* Input proc. */
176    ConsoleOutputProc,		/* Output proc. */
177    NULL,			/* Seek proc. */
178    NULL,			/* Set option proc. */
179    NULL,			/* Get option proc. */
180    ConsoleWatchProc,		/* Set up notifier to watch the channel. */
181    ConsoleGetHandleProc,	/* Get an OS handle from channel. */
182    NULL,			/* close2proc. */
183    ConsoleBlockModeProc,	/* Set blocking or non-blocking mode.*/
184    NULL,			/* flush proc. */
185    NULL,			/* handler proc. */
186    NULL,                       /* wide seek proc */
187    ConsoleThreadActionProc,    /* thread action proc */
188};
189
190/*
191 *----------------------------------------------------------------------
192 *
193 * ConsoleInit --
194 *
195 *	This function initializes the static variables for this file.
196 *
197 * Results:
198 *	None.
199 *
200 * Side effects:
201 *	Creates a new event source.
202 *
203 *----------------------------------------------------------------------
204 */
205
206static void
207ConsoleInit()
208{
209    ThreadSpecificData *tsdPtr;
210
211    /*
212     * Check the initialized flag first, then check again in the mutex.
213     * This is a speed enhancement.
214     */
215
216    if (!initialized) {
217	Tcl_MutexLock(&consoleMutex);
218	if (!initialized) {
219	    initialized = 1;
220	    Tcl_CreateExitHandler(ProcExitHandler, NULL);
221	}
222	Tcl_MutexUnlock(&consoleMutex);
223    }
224
225    tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
226    if (tsdPtr == NULL) {
227	tsdPtr = TCL_TSD_INIT(&dataKey);
228	tsdPtr->firstConsolePtr = NULL;
229	Tcl_CreateEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
230	Tcl_CreateThreadExitHandler(ConsoleExitHandler, NULL);
231    }
232}
233
234/*
235 *----------------------------------------------------------------------
236 *
237 * ConsoleExitHandler --
238 *
239 *	This function is called to cleanup the console module before
240 *	Tcl is unloaded.
241 *
242 * Results:
243 *	None.
244 *
245 * Side effects:
246 *	Removes the console event source.
247 *
248 *----------------------------------------------------------------------
249 */
250
251static void
252ConsoleExitHandler(
253    ClientData clientData)	/* Old window proc */
254{
255    Tcl_DeleteEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
256}
257
258/*
259 *----------------------------------------------------------------------
260 *
261 * ProcExitHandler --
262 *
263 *	This function is called to cleanup the process list before
264 *	Tcl is unloaded.
265 *
266 * Results:
267 *	None.
268 *
269 * Side effects:
270 *	Resets the process list.
271 *
272 *----------------------------------------------------------------------
273 */
274
275static void
276ProcExitHandler(
277    ClientData clientData)	/* Old window proc */
278{
279    Tcl_MutexLock(&consoleMutex);
280    initialized = 0;
281    Tcl_MutexUnlock(&consoleMutex);
282}
283
284/*
285 *----------------------------------------------------------------------
286 *
287 * ConsoleSetupProc --
288 *
289 *	This procedure is invoked before Tcl_DoOneEvent blocks waiting
290 *	for an event.
291 *
292 * Results:
293 *	None.
294 *
295 * Side effects:
296 *	Adjusts the block time if needed.
297 *
298 *----------------------------------------------------------------------
299 */
300
301void
302ConsoleSetupProc(
303    ClientData data,		/* Not used. */
304    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
305{
306    ConsoleInfo *infoPtr;
307    Tcl_Time blockTime = { 0, 0 };
308    int block = 1;
309    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
310
311    if (!(flags & TCL_FILE_EVENTS)) {
312	return;
313    }
314
315    /*
316     * Look to see if any events are already pending.  If they are, poll.
317     */
318
319    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;
320	    infoPtr = infoPtr->nextPtr) {
321	if (infoPtr->watchMask & TCL_WRITABLE) {
322	    if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
323		block = 0;
324	    }
325	}
326	if (infoPtr->watchMask & TCL_READABLE) {
327	    if (WaitForRead(infoPtr, 0) >= 0) {
328		block = 0;
329	    }
330	}
331    }
332    if (!block) {
333	Tcl_SetMaxBlockTime(&blockTime);
334    }
335}
336
337/*
338 *----------------------------------------------------------------------
339 *
340 * ConsoleCheckProc --
341 *
342 *	This procedure is called by Tcl_DoOneEvent to check the console
343 *	event source for events.
344 *
345 * Results:
346 *	None.
347 *
348 * Side effects:
349 *	May queue an event.
350 *
351 *----------------------------------------------------------------------
352 */
353
354static void
355ConsoleCheckProc(
356    ClientData data,		/* Not used. */
357    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
358{
359    ConsoleInfo *infoPtr;
360    ConsoleEvent *evPtr;
361    int needEvent;
362    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
363
364    if (!(flags & TCL_FILE_EVENTS)) {
365	return;
366    }
367
368    /*
369     * Queue events for any ready consoles that don't already have events
370     * queued.
371     */
372
373    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;
374	    infoPtr = infoPtr->nextPtr) {
375	if (infoPtr->flags & CONSOLE_PENDING) {
376	    continue;
377	}
378
379	/*
380	 * Queue an event if the console is signaled for reading or writing.
381	 */
382
383	needEvent = 0;
384	if (infoPtr->watchMask & TCL_WRITABLE) {
385	    if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
386		needEvent = 1;
387	    }
388	}
389
390	if (infoPtr->watchMask & TCL_READABLE) {
391	    if (WaitForRead(infoPtr, 0) >= 0) {
392		needEvent = 1;
393	    }
394	}
395
396	if (needEvent) {
397	    infoPtr->flags |= CONSOLE_PENDING;
398	    evPtr = (ConsoleEvent *) ckalloc(sizeof(ConsoleEvent));
399	    evPtr->header.proc = ConsoleEventProc;
400	    evPtr->infoPtr = infoPtr;
401	    Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
402	}
403    }
404}
405
406
407/*
408 *----------------------------------------------------------------------
409 *
410 * ConsoleBlockModeProc --
411 *
412 *	Set blocking or non-blocking mode on channel.
413 *
414 * Results:
415 *	0 if successful, errno when failed.
416 *
417 * Side effects:
418 *	Sets the device into blocking or non-blocking mode.
419 *
420 *----------------------------------------------------------------------
421 */
422
423static int
424ConsoleBlockModeProc(
425    ClientData instanceData,	/* Instance data for channel. */
426    int mode)			/* TCL_MODE_BLOCKING or
427                                 * TCL_MODE_NONBLOCKING. */
428{
429    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
430
431    /*
432     * Consoles on Windows can not be switched between blocking and nonblocking,
433     * hence we have to emulate the behavior. This is done in the input
434     * function by checking against a bit in the state. We set or unset the
435     * bit here to cause the input function to emulate the correct behavior.
436     */
437
438    if (mode == TCL_MODE_NONBLOCKING) {
439	infoPtr->flags |= CONSOLE_ASYNC;
440    } else {
441	infoPtr->flags &= ~(CONSOLE_ASYNC);
442    }
443    return 0;
444}
445
446/*
447 *----------------------------------------------------------------------
448 *
449 * ConsoleCloseProc --
450 *
451 *	Closes a console based IO channel.
452 *
453 * Results:
454 *	0 on success, errno otherwise.
455 *
456 * Side effects:
457 *	Closes the physical channel.
458 *
459 *----------------------------------------------------------------------
460 */
461
462static int
463ConsoleCloseProc(
464    ClientData instanceData,	/* Pointer to ConsoleInfo structure. */
465    Tcl_Interp *interp)		/* For error reporting. */
466{
467    ConsoleInfo *consolePtr = (ConsoleInfo *) instanceData;
468    int errorCode;
469    ConsoleInfo *infoPtr, **nextPtrPtr;
470    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
471    DWORD exitCode;
472
473    errorCode = 0;
474
475    /*
476     * Clean up the background thread if necessary.  Note that this
477     * must be done before we can close the file, since the
478     * thread may be blocking trying to read from the console.
479     */
480
481    if (consolePtr->readThread) {
482
483	/*
484	 * The thread may already have closed on it's own.  Check it's
485	 * exit code.
486	 */
487
488	GetExitCodeThread(consolePtr->readThread, &exitCode);
489
490	if (exitCode == STILL_ACTIVE) {
491
492	    /*
493	     * Set the stop event so that if the reader thread is blocked
494	     * in ConsoleReaderThread on WaitForMultipleEvents, it will exit
495	     * cleanly.
496	     */
497
498	    SetEvent(consolePtr->stopReader);
499
500	    /*
501	     * Wait at most 20 milliseconds for the reader thread to close.
502	     */
503
504	    if (WaitForSingleObject(consolePtr->readThread, 20)
505		    == WAIT_TIMEOUT) {
506		/*
507		 * Forcibly terminate the background thread as a last
508		 * resort.  Note that we need to guard against
509		 * terminating the thread while it is in the middle of
510		 * Tcl_ThreadAlert because it won't be able to release
511		 * the notifier lock.
512		 */
513
514		Tcl_MutexLock(&consoleMutex);
515
516		/* BUG: this leaks memory. */
517		TerminateThread(consolePtr->readThread, 0);
518		Tcl_MutexUnlock(&consoleMutex);
519	    }
520	}
521
522	CloseHandle(consolePtr->readThread);
523	CloseHandle(consolePtr->readable);
524	CloseHandle(consolePtr->startReader);
525	CloseHandle(consolePtr->stopReader);
526	consolePtr->readThread = NULL;
527    }
528    consolePtr->validMask &= ~TCL_READABLE;
529
530    /*
531     * Wait for the writer thread to finish the current buffer, then
532     * terminate the thread and close the handles.  If the channel is
533     * nonblocking, there should be no pending write operations.
534     */
535
536    if (consolePtr->writeThread) {
537	if (consolePtr->toWrite) {
538	    /*
539	     * We only need to wait if there is something to write.
540	     * This may prevent infinite wait on exit. [python bug 216289]
541	     */
542	    WaitForSingleObject(consolePtr->writable, INFINITE);
543	}
544
545	/*
546	 * The thread may already have closed on it's own.  Check it's
547	 * exit code.
548	 */
549
550	GetExitCodeThread(consolePtr->writeThread, &exitCode);
551
552	if (exitCode == STILL_ACTIVE) {
553	    /*
554	     * Set the stop event so that if the reader thread is blocked
555	     * in ConsoleWriterThread on WaitForMultipleEvents, it will
556	     * exit cleanly.
557	     */
558
559	    SetEvent(consolePtr->stopWriter);
560
561	    /*
562	     * Wait at most 20 milliseconds for the writer thread to close.
563	     */
564
565	    if (WaitForSingleObject(consolePtr->writeThread, 20)
566		    == WAIT_TIMEOUT) {
567		/*
568		 * Forcibly terminate the background thread as a last
569		 * resort.  Note that we need to guard against
570		 * terminating the thread while it is in the middle of
571		 * Tcl_ThreadAlert because it won't be able to release
572		 * the notifier lock.
573		 */
574
575		Tcl_MutexLock(&consoleMutex);
576
577		/* BUG: this leaks memory. */
578		TerminateThread(consolePtr->writeThread, 0);
579		Tcl_MutexUnlock(&consoleMutex);
580	    }
581	}
582
583	CloseHandle(consolePtr->writeThread);
584	CloseHandle(consolePtr->writable);
585	CloseHandle(consolePtr->startWriter);
586	CloseHandle(consolePtr->stopWriter);
587	consolePtr->writeThread = NULL;
588    }
589    consolePtr->validMask &= ~TCL_WRITABLE;
590
591
592    /*
593     * Don't close the Win32 handle if the handle is a standard channel
594     * during the thread exit process.  Otherwise, one thread may kill
595     * the stdio of another.
596     */
597
598    if (!TclInThreadExit()
599	    || ((GetStdHandle(STD_INPUT_HANDLE) != consolePtr->handle)
600		&& (GetStdHandle(STD_OUTPUT_HANDLE) != consolePtr->handle)
601		&& (GetStdHandle(STD_ERROR_HANDLE) != consolePtr->handle))) {
602	if (CloseHandle(consolePtr->handle) == FALSE) {
603	    TclWinConvertError(GetLastError());
604	    errorCode = errno;
605	}
606    }
607
608    consolePtr->watchMask &= consolePtr->validMask;
609
610    /*
611     * Remove the file from the list of watched files.
612     */
613
614    for (nextPtrPtr = &(tsdPtr->firstConsolePtr), infoPtr = *nextPtrPtr;
615	    infoPtr != NULL;
616	    nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
617	if (infoPtr == (ConsoleInfo *)consolePtr) {
618	    *nextPtrPtr = infoPtr->nextPtr;
619	    break;
620	}
621    }
622    if (consolePtr->writeBuf != NULL) {
623	ckfree(consolePtr->writeBuf);
624	consolePtr->writeBuf = 0;
625    }
626    ckfree((char*) consolePtr);
627
628    return errorCode;
629}
630
631/*
632 *----------------------------------------------------------------------
633 *
634 * ConsoleInputProc --
635 *
636 *	Reads input from the IO channel into the buffer given. Returns
637 *	count of how many bytes were actually read, and an error indication.
638 *
639 * Results:
640 *	A count of how many bytes were read is returned and an error
641 *	indication is returned in an output argument.
642 *
643 * Side effects:
644 *	Reads input from the actual channel.
645 *
646 *----------------------------------------------------------------------
647 */
648
649static int
650ConsoleInputProc(
651    ClientData instanceData,		/* Console state. */
652    char *buf,				/* Where to store data read. */
653    int bufSize,			/* How much space is available
654                                         * in the buffer? */
655    int *errorCode)			/* Where to store error code. */
656{
657    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
658    DWORD count, bytesRead = 0;
659    int result;
660
661    *errorCode = 0;
662
663    /*
664     * Synchronize with the reader thread.
665     */
666
667    result = WaitForRead(infoPtr, (infoPtr->flags & CONSOLE_ASYNC) ? 0 : 1);
668
669    /*
670     * If an error occurred, return immediately.
671     */
672
673    if (result == -1) {
674	*errorCode = errno;
675	return -1;
676    }
677
678    if (infoPtr->readFlags & CONSOLE_BUFFERED) {
679	/*
680	 * Data is stored in the buffer.
681	 */
682
683	if (bufSize < (infoPtr->bytesRead - infoPtr->offset)) {
684	    memcpy(buf, &infoPtr->buffer[infoPtr->offset], (size_t) bufSize);
685	    bytesRead = bufSize;
686	    infoPtr->offset += bufSize;
687	} else {
688	    memcpy(buf, &infoPtr->buffer[infoPtr->offset], (size_t) bufSize);
689	    bytesRead = infoPtr->bytesRead - infoPtr->offset;
690
691	    /*
692	     * Reset the buffer
693	     */
694
695	    infoPtr->readFlags &= ~CONSOLE_BUFFERED;
696	    infoPtr->offset = 0;
697	}
698
699	return bytesRead;
700    }
701
702    /*
703     * Attempt to read bufSize bytes.  The read will return immediately
704     * if there is any data available.  Otherwise it will block until
705     * at least one byte is available or an EOF occurs.
706     */
707
708    if (ReadConsole(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &count,
709		    NULL) == TRUE) {
710	buf[count] = '\0';
711	return count;
712    }
713
714    return -1;
715}
716
717/*
718 *----------------------------------------------------------------------
719 *
720 * ConsoleOutputProc --
721 *
722 *	Writes the given output on the IO channel. Returns count of how
723 *	many characters were actually written, and an error indication.
724 *
725 * Results:
726 *	A count of how many characters were written is returned and an
727 *	error indication is returned in an output argument.
728 *
729 * Side effects:
730 *	Writes output on the actual channel.
731 *
732 *----------------------------------------------------------------------
733 */
734
735static int
736ConsoleOutputProc(
737    ClientData instanceData,		/* Console state. */
738    CONST char *buf,			/* The data buffer. */
739    int toWrite,			/* How many bytes to write? */
740    int *errorCode)			/* Where to store error code. */
741{
742    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
743    DWORD bytesWritten, timeout;
744
745    *errorCode = 0;
746    timeout = (infoPtr->flags & CONSOLE_ASYNC) ? 0 : INFINITE;
747    if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) {
748	/*
749	 * The writer thread is blocked waiting for a write to complete
750	 * and the channel is in non-blocking mode.
751	 */
752
753	errno = EAGAIN;
754	goto error;
755    }
756
757    /*
758     * Check for a background error on the last write.
759     */
760
761    if (infoPtr->writeError) {
762	TclWinConvertError(infoPtr->writeError);
763	infoPtr->writeError = 0;
764	goto error;
765    }
766
767    if (infoPtr->flags & CONSOLE_ASYNC) {
768	/*
769	 * The console is non-blocking, so copy the data into the output
770	 * buffer and restart the writer thread.
771	 */
772
773	if (toWrite > infoPtr->writeBufLen) {
774	    /*
775	     * Reallocate the buffer to be large enough to hold the data.
776	     */
777
778	    if (infoPtr->writeBuf) {
779		ckfree(infoPtr->writeBuf);
780	    }
781	    infoPtr->writeBufLen = toWrite;
782	    infoPtr->writeBuf = ckalloc((unsigned int) toWrite);
783	}
784	memcpy(infoPtr->writeBuf, buf, (size_t) toWrite);
785	infoPtr->toWrite = toWrite;
786	ResetEvent(infoPtr->writable);
787	SetEvent(infoPtr->startWriter);
788	bytesWritten = toWrite;
789    } else {
790	/*
791	 * In the blocking case, just try to write the buffer directly.
792	 * This avoids an unnecessary copy.
793	 */
794
795	if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite,
796		&bytesWritten, (LPOVERLAPPED) NULL) == FALSE) {
797	    TclWinConvertError(GetLastError());
798	    goto error;
799	}
800    }
801    return bytesWritten;
802
803    error:
804    *errorCode = errno;
805    return -1;
806
807}
808
809/*
810 *----------------------------------------------------------------------
811 *
812 * ConsoleEventProc --
813 *
814 *	This function is invoked by Tcl_ServiceEvent when a file event
815 *	reaches the front of the event queue.  This procedure invokes
816 *	Tcl_NotifyChannel on the console.
817 *
818 * Results:
819 *	Returns 1 if the event was handled, meaning it should be removed
820 *	from the queue.  Returns 0 if the event was not handled, meaning
821 *	it should stay on the queue.  The only time the event isn't
822 *	handled is if the TCL_FILE_EVENTS flag bit isn't set.
823 *
824 * Side effects:
825 *	Whatever the notifier callback does.
826 *
827 *----------------------------------------------------------------------
828 */
829
830static int
831ConsoleEventProc(
832    Tcl_Event *evPtr,		/* Event to service. */
833    int flags)			/* Flags that indicate what events to
834				 * handle, such as TCL_FILE_EVENTS. */
835{
836    ConsoleEvent *consoleEvPtr = (ConsoleEvent *)evPtr;
837    ConsoleInfo *infoPtr;
838    int mask;
839    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
840
841    if (!(flags & TCL_FILE_EVENTS)) {
842	return 0;
843    }
844
845    /*
846     * Search through the list of watched consoles for the one whose handle
847     * matches the event.  We do this rather than simply dereferencing
848     * the handle in the event so that consoles can be deleted while the
849     * event is in the queue.
850     */
851
852    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;
853	    infoPtr = infoPtr->nextPtr) {
854	if (consoleEvPtr->infoPtr == infoPtr) {
855	    infoPtr->flags &= ~(CONSOLE_PENDING);
856	    break;
857	}
858    }
859
860    /*
861     * Remove stale events.
862     */
863
864    if (!infoPtr) {
865	return 1;
866    }
867
868    /*
869     * Check to see if the console is readable.  Note
870     * that we can't tell if a console is writable, so we always report it
871     * as being writable unless we have detected EOF.
872     */
873
874    mask = 0;
875    if (infoPtr->watchMask & TCL_WRITABLE) {
876	if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
877	    mask = TCL_WRITABLE;
878	}
879    }
880
881    if (infoPtr->watchMask & TCL_READABLE) {
882	if (WaitForRead(infoPtr, 0) >= 0) {
883	    if (infoPtr->readFlags & CONSOLE_EOF) {
884		mask = TCL_READABLE;
885	    } else {
886		mask |= TCL_READABLE;
887	    }
888	}
889    }
890
891    /*
892     * Inform the channel of the events.
893     */
894
895    Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
896    return 1;
897}
898
899/*
900 *----------------------------------------------------------------------
901 *
902 * ConsoleWatchProc --
903 *
904 *	Called by the notifier to set up to watch for events on this
905 *	channel.
906 *
907 * Results:
908 *	None.
909 *
910 * Side effects:
911 *	None.
912 *
913 *----------------------------------------------------------------------
914 */
915
916static void
917ConsoleWatchProc(
918    ClientData instanceData,		/* Console state. */
919    int mask)				/* What events to watch for, OR-ed
920                                         * combination of TCL_READABLE,
921                                         * TCL_WRITABLE and TCL_EXCEPTION. */
922{
923    ConsoleInfo **nextPtrPtr, *ptr;
924    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
925    int oldMask = infoPtr->watchMask;
926    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
927
928    /*
929     * Since most of the work is handled by the background threads,
930     * we just need to update the watchMask and then force the notifier
931     * to poll once.
932     */
933
934    infoPtr->watchMask = mask & infoPtr->validMask;
935    if (infoPtr->watchMask) {
936	Tcl_Time blockTime = { 0, 0 };
937	if (!oldMask) {
938	    infoPtr->nextPtr = tsdPtr->firstConsolePtr;
939	    tsdPtr->firstConsolePtr = infoPtr;
940	}
941	Tcl_SetMaxBlockTime(&blockTime);
942    } else {
943	if (oldMask) {
944	    /*
945	     * Remove the console from the list of watched consoles.
946	     */
947
948	    for (nextPtrPtr = &(tsdPtr->firstConsolePtr), ptr = *nextPtrPtr;
949		 ptr != NULL;
950		 nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
951		if (infoPtr == ptr) {
952		    *nextPtrPtr = ptr->nextPtr;
953		    break;
954		}
955	    }
956	}
957    }
958}
959
960/*
961 *----------------------------------------------------------------------
962 *
963 * ConsoleGetHandleProc --
964 *
965 *	Called from Tcl_GetChannelHandle to retrieve OS handles from
966 *	inside a command consoleline based channel.
967 *
968 * Results:
969 *	Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
970 *	there is no handle for the specified direction.
971 *
972 * Side effects:
973 *	None.
974 *
975 *----------------------------------------------------------------------
976 */
977
978static int
979ConsoleGetHandleProc(
980    ClientData instanceData,	/* The console state. */
981    int direction,		/* TCL_READABLE or TCL_WRITABLE */
982    ClientData *handlePtr)	/* Where to store the handle.  */
983{
984    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
985
986    *handlePtr = (ClientData) infoPtr->handle;
987    return TCL_OK;
988}
989
990/*
991 *----------------------------------------------------------------------
992 *
993 * WaitForRead --
994 *
995 *	Wait until some data is available, the console is at
996 *	EOF or the reader thread is blocked waiting for data (if the
997 *	channel is in non-blocking mode).
998 *
999 * Results:
1000 *	Returns 1 if console is readable.  Returns 0 if there is no data
1001 *	on the console, but there is buffered data.  Returns -1 if an
1002 *	error occurred.  If an error occurred, the threads may not
1003 *	be synchronized.
1004 *
1005 * Side effects:
1006 *	Updates the shared state flags.  If no error occurred,
1007 *      the reader thread is blocked waiting for a signal from the
1008 *      main thread.
1009 *
1010 *----------------------------------------------------------------------
1011 */
1012
1013static int
1014WaitForRead(
1015    ConsoleInfo *infoPtr,		/* Console state. */
1016    int blocking)		/* Indicates whether call should be
1017				 * blocking or not. */
1018{
1019    DWORD timeout, count;
1020    HANDLE *handle = infoPtr->handle;
1021    INPUT_RECORD input;
1022
1023    while (1) {
1024	/*
1025	 * Synchronize with the reader thread.
1026	 */
1027
1028	timeout = blocking ? INFINITE : 0;
1029	if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) {
1030	    /*
1031	     * The reader thread is blocked waiting for data and the channel
1032	     * is in non-blocking mode.
1033	     */
1034	    errno = EAGAIN;
1035	    return -1;
1036	}
1037
1038	/*
1039	 * At this point, the two threads are synchronized, so it is safe
1040	 * to access shared state.
1041	 */
1042
1043	/*
1044	 * If the console has hit EOF, it is always readable.
1045	 */
1046
1047	if (infoPtr->readFlags & CONSOLE_EOF) {
1048	    return 1;
1049	}
1050
1051	if (PeekConsoleInput(handle, &input, 1, &count) == FALSE) {
1052            /*
1053	     * Check to see if the peek failed because of EOF.
1054	     */
1055
1056	    TclWinConvertError(GetLastError());
1057
1058	    if (errno == EOF) {
1059		infoPtr->readFlags |= CONSOLE_EOF;
1060		return 1;
1061	    }
1062
1063	    /*
1064	     * Ignore errors if there is data in the buffer.
1065	     */
1066
1067	    if (infoPtr->readFlags & CONSOLE_BUFFERED) {
1068		return 0;
1069	    } else {
1070		return -1;
1071	    }
1072	}
1073
1074	/*
1075	 * If there is data in the buffer, the console must be
1076	 * readable (since it is a line-oriented device).
1077	 */
1078
1079	if (infoPtr->readFlags & CONSOLE_BUFFERED) {
1080	    return 1;
1081	}
1082
1083
1084	/*
1085	 * There wasn't any data available, so reset the thread and
1086	 * try again.
1087	 */
1088
1089	ResetEvent(infoPtr->readable);
1090	SetEvent(infoPtr->startReader);
1091    }
1092}
1093
1094/*
1095 *----------------------------------------------------------------------
1096 *
1097 * ConsoleReaderThread --
1098 *
1099 *	This function runs in a separate thread and waits for input
1100 *	to become available on a console.
1101 *
1102 * Results:
1103 *	None.
1104 *
1105 * Side effects:
1106 *	Signals the main thread when input become available.  May
1107 *	cause the main thread to wake up by posting a message.  May
1108 *	one line from the console for each wait operation.
1109 *
1110 *----------------------------------------------------------------------
1111 */
1112
1113static DWORD WINAPI
1114ConsoleReaderThread(LPVOID arg)
1115{
1116    ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
1117    HANDLE *handle = infoPtr->handle;
1118    DWORD count, waitResult;
1119    HANDLE wEvents[2];
1120
1121    /* The first event takes precedence. */
1122    wEvents[0] = infoPtr->stopReader;
1123    wEvents[1] = infoPtr->startReader;
1124
1125    for (;;) {
1126	/*
1127	 * Wait for the main thread to signal before attempting to wait.
1128	 */
1129
1130	waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
1131
1132	if (waitResult != (WAIT_OBJECT_0 + 1)) {
1133	    /*
1134	     * The start event was not signaled.  It must be the stop event
1135	     * or an error, so exit this thread.
1136	     */
1137
1138	    break;
1139	}
1140
1141	count = 0;
1142
1143	/*
1144	 * Look for data on the console, but first ignore any events
1145	 * that are not KEY_EVENTs
1146	 */
1147	if (ReadConsoleA(handle, infoPtr->buffer, CONSOLE_BUFFER_SIZE,
1148		(LPDWORD) &infoPtr->bytesRead, NULL) != FALSE) {
1149	    /*
1150	     * Data was stored in the buffer.
1151	     */
1152
1153	    infoPtr->readFlags |= CONSOLE_BUFFERED;
1154	} else {
1155	    DWORD err;
1156	    err = GetLastError();
1157
1158	    if (err == EOF) {
1159		infoPtr->readFlags = CONSOLE_EOF;
1160	    }
1161	}
1162
1163	/*
1164	 * Signal the main thread by signalling the readable event and
1165	 * then waking up the notifier thread.
1166	 */
1167
1168	SetEvent(infoPtr->readable);
1169
1170	/*
1171	 * Alert the foreground thread.  Note that we need to treat this like
1172	 * a critical section so the foreground thread does not terminate
1173	 * this thread while we are holding a mutex in the notifier code.
1174	 */
1175
1176	Tcl_MutexLock(&consoleMutex);
1177	if (infoPtr->threadId != NULL) {
1178	    /* TIP #218. When in flight ignore the event, no one will receive it anyway */
1179	    Tcl_ThreadAlert(infoPtr->threadId);
1180	}
1181	Tcl_MutexUnlock(&consoleMutex);
1182    }
1183
1184    return 0;
1185}
1186
1187/*
1188 *----------------------------------------------------------------------
1189 *
1190 * ConsoleWriterThread --
1191 *
1192 *	This function runs in a separate thread and writes data
1193 *	onto a console.
1194 *
1195 * Results:
1196 *	Always returns 0.
1197 *
1198 * Side effects:
1199 *	Signals the main thread when an output operation is completed.
1200 *	May cause the main thread to wake up by posting a message.
1201 *
1202 *----------------------------------------------------------------------
1203 */
1204
1205static DWORD WINAPI
1206ConsoleWriterThread(LPVOID arg)
1207{
1208
1209    ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
1210    HANDLE *handle = infoPtr->handle;
1211    DWORD count, toWrite, waitResult;
1212    char *buf;
1213    HANDLE wEvents[2];
1214
1215    /* The first event takes precedence. */
1216    wEvents[0] = infoPtr->stopWriter;
1217    wEvents[1] = infoPtr->startWriter;
1218
1219    for (;;) {
1220	/*
1221	 * Wait for the main thread to signal before attempting to write.
1222	 */
1223
1224	waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
1225
1226	if (waitResult != (WAIT_OBJECT_0 + 1)) {
1227	    /*
1228	     * The start event was not signaled.  It must be the stop event
1229	     * or an error, so exit this thread.
1230	     */
1231
1232	    break;
1233	}
1234
1235	buf = infoPtr->writeBuf;
1236	toWrite = infoPtr->toWrite;
1237
1238	/*
1239	 * Loop until all of the bytes are written or an error occurs.
1240	 */
1241
1242	while (toWrite > 0) {
1243	    if (WriteConsoleA(handle, buf, toWrite, &count, NULL) == FALSE) {
1244		infoPtr->writeError = GetLastError();
1245		break;
1246	    } else {
1247		toWrite -= count;
1248		buf += count;
1249	    }
1250	}
1251
1252	/*
1253	 * Signal the main thread by signalling the writable event and
1254	 * then waking up the notifier thread.
1255	 */
1256
1257	SetEvent(infoPtr->writable);
1258
1259	/*
1260	 * Alert the foreground thread.  Note that we need to treat this like
1261	 * a critical section so the foreground thread does not terminate
1262	 * this thread while we are holding a mutex in the notifier code.
1263	 */
1264
1265	Tcl_MutexLock(&consoleMutex);
1266	if (infoPtr->threadId != NULL) {
1267	    /* TIP #218. When in flight ignore the event, no one will receive it anyway */
1268	    Tcl_ThreadAlert(infoPtr->threadId);
1269	}
1270	Tcl_MutexUnlock(&consoleMutex);
1271    }
1272
1273    return 0;
1274}
1275
1276
1277
1278/*
1279 *----------------------------------------------------------------------
1280 *
1281 * TclWinOpenConsoleChannel --
1282 *
1283 *	Constructs a Console channel for the specified standard OS handle.
1284 *      This is a helper function to break up the construction of
1285 *      channels into File, Console, or Serial.
1286 *
1287 * Results:
1288 *	Returns the new channel, or NULL.
1289 *
1290 * Side effects:
1291 *	May open the channel
1292 *
1293 *----------------------------------------------------------------------
1294 */
1295
1296Tcl_Channel
1297TclWinOpenConsoleChannel(handle, channelName, permissions)
1298    HANDLE handle;
1299    char *channelName;
1300    int permissions;
1301{
1302    char encoding[4 + TCL_INTEGER_SPACE];
1303    ConsoleInfo *infoPtr;
1304    DWORD id, modes;
1305
1306    ConsoleInit();
1307
1308    /*
1309     * See if a channel with this handle already exists.
1310     */
1311
1312    infoPtr = (ConsoleInfo *) ckalloc((unsigned) sizeof(ConsoleInfo));
1313    memset(infoPtr, 0, sizeof(ConsoleInfo));
1314
1315    infoPtr->validMask = permissions;
1316    infoPtr->handle = handle;
1317    infoPtr->channel = (Tcl_Channel) NULL;
1318
1319    wsprintfA(encoding, "cp%d", GetConsoleCP());
1320
1321    infoPtr->threadId = Tcl_GetCurrentThread();
1322
1323    /*
1324     * Use the pointer for the name of the result channel.
1325     * This keeps the channel names unique, since some may share
1326     * handles (stdin/stdout/stderr for instance).
1327     */
1328
1329    wsprintfA(channelName, "file%lx", (int) infoPtr);
1330
1331    infoPtr->channel = Tcl_CreateChannel(&consoleChannelType, channelName,
1332            (ClientData) infoPtr, permissions);
1333
1334    if (permissions & TCL_READABLE) {
1335	/*
1336	 * Make sure the console input buffer is ready for only character
1337	 * input notifications and the buffer is set for line buffering.
1338	 * IOW, we only want to catch when complete lines are ready for
1339	 * reading.
1340	 */
1341	GetConsoleMode(infoPtr->handle, &modes);
1342	modes &= ~(ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
1343	modes |= ENABLE_LINE_INPUT;
1344	SetConsoleMode(infoPtr->handle, modes);
1345
1346	infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL);
1347	infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL);
1348	infoPtr->stopReader = CreateEvent(NULL, FALSE, FALSE, NULL);
1349	infoPtr->readThread = CreateThread(NULL, 256, ConsoleReaderThread,
1350	        infoPtr, 0, &id);
1351	SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST);
1352    }
1353
1354    if (permissions & TCL_WRITABLE) {
1355	infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL);
1356	infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
1357	infoPtr->stopWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
1358	infoPtr->writeThread = CreateThread(NULL, 256, ConsoleWriterThread,
1359	        infoPtr, 0, &id);
1360	SetThreadPriority(infoPtr->writeThread, THREAD_PRIORITY_HIGHEST);
1361    }
1362
1363    /*
1364     * Files have default translation of AUTO and ^Z eof char, which
1365     * means that a ^Z will be accepted as EOF when reading.
1366     */
1367
1368    Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
1369    Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
1370    Tcl_SetChannelOption(NULL, infoPtr->channel, "-encoding", encoding);
1371
1372    return infoPtr->channel;
1373}
1374
1375/*
1376 *----------------------------------------------------------------------
1377 *
1378 * ConsoleThreadActionProc --
1379 *
1380 *	Insert or remove any thread local refs to this channel.
1381 *
1382 * Results:
1383 *	None.
1384 *
1385 * Side effects:
1386 *	Changes thread local list of valid channels.
1387 *
1388 *----------------------------------------------------------------------
1389 */
1390
1391static void
1392ConsoleThreadActionProc (instanceData, action)
1393     ClientData instanceData;
1394     int action;
1395{
1396    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
1397
1398    /* We do not access firstConsolePtr in the thread structures. This is
1399     * not for all serials managed by the thread, but only those we are
1400     * watching. Removal of the filevent handlers before transfer thus
1401     * takes care of this structure.
1402     */
1403
1404    Tcl_MutexLock(&consoleMutex);
1405    if (action == TCL_CHANNEL_THREAD_INSERT) {
1406        /* We can't copy the thread information from the channel when
1407	 * the channel is created. At this time the channel back
1408	 * pointer has not been set yet. However in that case the
1409	 * threadId has already been set by TclpCreateCommandChannel
1410	 * itself, so the structure is still good.
1411	 */
1412
1413        ConsoleInit ();
1414        if (infoPtr->channel != NULL) {
1415	    infoPtr->threadId = Tcl_GetChannelThread (infoPtr->channel);
1416	}
1417    } else {
1418	infoPtr->threadId = NULL;
1419    }
1420    Tcl_MutexUnlock(&consoleMutex);
1421}
1422