4 * This file implements the Windows-specific console functions,
5 * and the "console" channel driver.
7 * Copyright (c) 1999 by Scriptics Corp.
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15 #include "tclWinInt.h"
22 * The following variable is used to tell whether this module has been
26 static int initialized = 0;
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.
34 TCL_DECLARE_MUTEX(consoleMutex)
37 * Bit masks used in the flags field of the ConsoleInfo structure below.
40 #define CONSOLE_PENDING (1<<0) /* Message is pending in the queue. */
41 #define CONSOLE_ASYNC (1<<1) /* Channel is non-blocking. */
44 * Bit masks used in the sharedFlags field of the ConsoleInfo structure below.
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
51 #define CONSOLE_BUFFER_SIZE (8*1024)
53 * This structure describes per-instance data for a console based channel.
56 typedef struct ConsoleInfo {
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
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
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 startReader; /* Auto-reset event used by the main thread to
83 * signal when the reader thread should attempt
84 * to read from the console. */
85 DWORD writeError; /* An error caused by the last background
86 * write. Set to 0 if no error has been
87 * detected. This word is shared with the
88 * writer thread so access must be
89 * synchronized with the writable object.
91 char *writeBuf; /* Current background output buffer.
92 * Access is synchronized with the writable
94 int writeBufLen; /* Size of write buffer. Access is
95 * synchronized with the writable
97 int toWrite; /* Current amount to be written. Access is
98 * synchronized with the writable object. */
99 int readFlags; /* Flags that are shared with the reader
100 * thread. Access is synchronized with the
101 * readable object. */
102 int bytesRead; /* number of bytes in the buffer */
103 int offset; /* number of bytes read out of the buffer */
104 char buffer[CONSOLE_BUFFER_SIZE];
105 /* Data consumed by reader thread. */
108 typedef struct ThreadSpecificData {
110 * The following pointer refers to the head of the list of consoles
111 * that are being watched for file events.
114 ConsoleInfo *firstConsolePtr;
115 } ThreadSpecificData;
117 static Tcl_ThreadDataKey dataKey;
120 * The following structure is what is added to the Tcl event queue when
121 * console events are generated.
124 typedef struct ConsoleEvent {
125 Tcl_Event header; /* Information that is standard for
127 ConsoleInfo *infoPtr; /* Pointer to console info structure. Note
128 * that we still have to verify that the
129 * console exists before dereferencing this
134 * Declarations for functions used only in this file.
137 static int ConsoleBlockModeProc(ClientData instanceData, int mode);
138 static void ConsoleCheckProc(ClientData clientData, int flags);
139 static int ConsoleCloseProc(ClientData instanceData,
141 static int ConsoleEventProc(Tcl_Event *evPtr, int flags);
142 static void ConsoleExitHandler(ClientData clientData);
143 static int ConsoleGetHandleProc(ClientData instanceData,
144 int direction, ClientData *handlePtr);
145 static ThreadSpecificData *ConsoleInit(void);
146 static int ConsoleInputProc(ClientData instanceData, char *buf,
147 int toRead, int *errorCode);
148 static int ConsoleOutputProc(ClientData instanceData,
149 CONST char *buf, int toWrite, int *errorCode);
150 static DWORD WINAPI ConsoleReaderThread(LPVOID arg);
151 static void ConsoleSetupProc(ClientData clientData, int flags);
152 static void ConsoleWatchProc(ClientData instanceData, int mask);
153 static DWORD WINAPI ConsoleWriterThread(LPVOID arg);
154 static void ProcExitHandler(ClientData clientData);
155 static int WaitForRead(ConsoleInfo *infoPtr, int blocking);
158 * This structure describes the channel type structure for command console
162 static Tcl_ChannelType consoleChannelType = {
163 "console", /* Type name. */
164 TCL_CHANNEL_VERSION_2, /* v2 channel */
165 ConsoleCloseProc, /* Close proc. */
166 ConsoleInputProc, /* Input proc. */
167 ConsoleOutputProc, /* Output proc. */
168 NULL, /* Seek proc. */
169 NULL, /* Set option proc. */
170 NULL, /* Get option proc. */
171 ConsoleWatchProc, /* Set up notifier to watch the channel. */
172 ConsoleGetHandleProc, /* Get an OS handle from channel. */
173 NULL, /* close2proc. */
174 ConsoleBlockModeProc, /* Set blocking or non-blocking mode.*/
175 NULL, /* flush proc. */
176 NULL, /* handler proc. */
180 *----------------------------------------------------------------------
184 * This function initializes the static variables for this file.
190 * Creates a new event source.
192 *----------------------------------------------------------------------
195 static ThreadSpecificData *
198 ThreadSpecificData *tsdPtr;
201 * Check the initialized flag first, then check again in the mutex.
202 * This is a speed enhancement.
206 Tcl_MutexLock(&consoleMutex);
209 Tcl_CreateExitHandler(ProcExitHandler, NULL);
211 Tcl_MutexUnlock(&consoleMutex);
214 tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
215 if (tsdPtr == NULL) {
216 tsdPtr = TCL_TSD_INIT(&dataKey);
217 tsdPtr->firstConsolePtr = NULL;
218 Tcl_CreateEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
219 Tcl_CreateThreadExitHandler(ConsoleExitHandler, NULL);
225 *----------------------------------------------------------------------
227 * ConsoleExitHandler --
229 * This function is called to cleanup the console module before
236 * Removes the console event source.
238 *----------------------------------------------------------------------
243 ClientData clientData) /* Old window proc */
245 Tcl_DeleteEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
249 *----------------------------------------------------------------------
253 * This function is called to cleanup the process list before
260 * Resets the process list.
262 *----------------------------------------------------------------------
267 ClientData clientData) /* Old window proc */
269 Tcl_MutexLock(&consoleMutex);
271 Tcl_MutexUnlock(&consoleMutex);
275 *----------------------------------------------------------------------
277 * ConsoleSetupProc --
279 * This procedure is invoked before Tcl_DoOneEvent blocks waiting
286 * Adjusts the block time if needed.
288 *----------------------------------------------------------------------
293 ClientData data, /* Not used. */
294 int flags) /* Event flags as passed to Tcl_DoOneEvent. */
296 ConsoleInfo *infoPtr;
297 Tcl_Time blockTime = { 0, 0 };
299 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
301 if (!(flags & TCL_FILE_EVENTS)) {
306 * Look to see if any events are already pending. If they are, poll.
309 for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;
310 infoPtr = infoPtr->nextPtr) {
311 if (infoPtr->watchMask & TCL_WRITABLE) {
312 if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
316 if (infoPtr->watchMask & TCL_READABLE) {
317 if (WaitForRead(infoPtr, 0) >= 0) {
323 Tcl_SetMaxBlockTime(&blockTime);
328 *----------------------------------------------------------------------
330 * ConsoleCheckProc --
332 * This procedure is called by Tcl_DoOneEvent to check the console
333 * event source for events.
339 * May queue an event.
341 *----------------------------------------------------------------------
346 ClientData data, /* Not used. */
347 int flags) /* Event flags as passed to Tcl_DoOneEvent. */
349 ConsoleInfo *infoPtr;
352 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
354 if (!(flags & TCL_FILE_EVENTS)) {
359 * Queue events for any ready consoles that don't already have events
363 for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;
364 infoPtr = infoPtr->nextPtr) {
365 if (infoPtr->flags & CONSOLE_PENDING) {
370 * Queue an event if the console is signaled for reading or writing.
374 if (infoPtr->watchMask & TCL_WRITABLE) {
375 if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
380 if (infoPtr->watchMask & TCL_READABLE) {
381 if (WaitForRead(infoPtr, 0) >= 0) {
387 infoPtr->flags |= CONSOLE_PENDING;
388 evPtr = (ConsoleEvent *) ckalloc(sizeof(ConsoleEvent));
389 evPtr->header.proc = ConsoleEventProc;
390 evPtr->infoPtr = infoPtr;
391 Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
398 *----------------------------------------------------------------------
400 * ConsoleBlockModeProc --
402 * Set blocking or non-blocking mode on channel.
405 * 0 if successful, errno when failed.
408 * Sets the device into blocking or non-blocking mode.
410 *----------------------------------------------------------------------
414 ConsoleBlockModeProc(
415 ClientData instanceData, /* Instance data for channel. */
416 int mode) /* TCL_MODE_BLOCKING or
417 * TCL_MODE_NONBLOCKING. */
419 ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
422 * Consoles on Windows can not be switched between blocking and nonblocking,
423 * hence we have to emulate the behavior. This is done in the input
424 * function by checking against a bit in the state. We set or unset the
425 * bit here to cause the input function to emulate the correct behavior.
428 if (mode == TCL_MODE_NONBLOCKING) {
429 infoPtr->flags |= CONSOLE_ASYNC;
431 infoPtr->flags &= ~(CONSOLE_ASYNC);
437 *----------------------------------------------------------------------
439 * ConsoleCloseProc --
441 * Closes a console based IO channel.
444 * 0 on success, errno otherwise.
447 * Closes the physical channel.
449 *----------------------------------------------------------------------
454 ClientData instanceData, /* Pointer to ConsoleInfo structure. */
455 Tcl_Interp *interp) /* For error reporting. */
457 ConsoleInfo *consolePtr = (ConsoleInfo *) instanceData;
459 ConsoleInfo *infoPtr, **nextPtrPtr;
460 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
465 * Clean up the background thread if necessary. Note that this
466 * must be done before we can close the file, since the
467 * thread may be blocking trying to read from the console.
470 if (consolePtr->readThread) {
472 * Forcibly terminate the background thread. We cannot rely on the
473 * thread to cleanly terminate itself because we have no way of
474 * closing the handle without blocking in the case where the
475 * thread is in the middle of an I/O operation. Note that we need
476 * to guard against terminating the thread while it is in the
477 * middle of Tcl_ThreadAlert because it won't be able to release
481 Tcl_MutexLock(&consoleMutex);
482 TerminateThread(consolePtr->readThread, 0);
485 * Wait for the thread to terminate. This ensures that we are
486 * completely cleaned up before we leave this function.
489 WaitForSingleObject(consolePtr->readThread, INFINITE);
490 Tcl_MutexUnlock(&consoleMutex);
492 CloseHandle(consolePtr->readThread);
493 CloseHandle(consolePtr->readable);
494 CloseHandle(consolePtr->startReader);
495 consolePtr->readThread = NULL;
497 consolePtr->validMask &= ~TCL_READABLE;
500 * Wait for the writer thread to finish the current buffer, then
501 * terminate the thread and close the handles. If the channel is
502 * nonblocking, there should be no pending write operations.
505 if (consolePtr->writeThread) {
506 if (consolePtr->toWrite) {
508 * We only need to wait if there is something to write.
509 * This may prevent infinite wait on exit. [python bug 216289]
511 WaitForSingleObject(consolePtr->writable, INFINITE);
515 * Forcibly terminate the background thread. We cannot rely on the
516 * thread to cleanly terminate itself because we have no way of
517 * closing the handle without blocking in the case where the
518 * thread is in the middle of an I/O operation. Note that we need
519 * to guard against terminating the thread while it is in the
520 * middle of Tcl_ThreadAlert because it won't be able to release
524 Tcl_MutexLock(&consoleMutex);
525 TerminateThread(consolePtr->writeThread, 0);
528 * Wait for the thread to terminate. This ensures that we are
529 * completely cleaned up before we leave this function.
532 WaitForSingleObject(consolePtr->writeThread, INFINITE);
533 Tcl_MutexUnlock(&consoleMutex);
535 CloseHandle(consolePtr->writeThread);
536 CloseHandle(consolePtr->writable);
537 CloseHandle(consolePtr->startWriter);
538 consolePtr->writeThread = NULL;
540 consolePtr->validMask &= ~TCL_WRITABLE;
544 * Don't close the Win32 handle if the handle is a standard channel
545 * during the exit process. Otherwise, one thread may kill the stdio
550 || ((GetStdHandle(STD_INPUT_HANDLE) != consolePtr->handle)
551 && (GetStdHandle(STD_OUTPUT_HANDLE) != consolePtr->handle)
552 && (GetStdHandle(STD_ERROR_HANDLE) != consolePtr->handle))) {
553 if (CloseHandle(consolePtr->handle) == FALSE) {
554 TclWinConvertError(GetLastError());
559 consolePtr->watchMask &= consolePtr->validMask;
562 * Remove the file from the list of watched files.
565 for (nextPtrPtr = &(tsdPtr->firstConsolePtr), infoPtr = *nextPtrPtr;
567 nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
568 if (infoPtr == (ConsoleInfo *)consolePtr) {
569 *nextPtrPtr = infoPtr->nextPtr;
573 if (consolePtr->writeBuf != NULL) {
574 ckfree(consolePtr->writeBuf);
575 consolePtr->writeBuf = 0;
577 ckfree((char*) consolePtr);
583 *----------------------------------------------------------------------
585 * ConsoleInputProc --
587 * Reads input from the IO channel into the buffer given. Returns
588 * count of how many bytes were actually read, and an error indication.
591 * A count of how many bytes were read is returned and an error
592 * indication is returned in an output argument.
595 * Reads input from the actual channel.
597 *----------------------------------------------------------------------
602 ClientData instanceData, /* Console state. */
603 char *buf, /* Where to store data read. */
604 int bufSize, /* How much space is available
606 int *errorCode) /* Where to store error code. */
608 ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
609 DWORD count, bytesRead = 0;
615 * Synchronize with the reader thread.
618 result = WaitForRead(infoPtr, (infoPtr->flags & CONSOLE_ASYNC) ? 0 : 1);
621 * If an error occurred, return immediately.
629 if (infoPtr->readFlags & CONSOLE_BUFFERED) {
631 * Data is stored in the buffer.
634 if (bufSize < (infoPtr->bytesRead - infoPtr->offset)) {
635 memcpy(buf, &infoPtr->buffer[infoPtr->offset], (size_t) bufSize);
637 infoPtr->offset += bufSize;
639 memcpy(buf, &infoPtr->buffer[infoPtr->offset], (size_t) bufSize);
640 bytesRead = infoPtr->bytesRead - infoPtr->offset;
646 infoPtr->readFlags &= ~CONSOLE_BUFFERED;
654 * Attempt to read bufSize bytes. The read will return immediately
655 * if there is any data available. Otherwise it will block until
656 * at least one byte is available or an EOF occurs.
659 if (ReadConsole(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &count,
660 (LPOVERLAPPED) NULL) == TRUE) {
669 *----------------------------------------------------------------------
671 * ConsoleOutputProc --
673 * Writes the given output on the IO channel. Returns count of how
674 * many characters were actually written, and an error indication.
677 * A count of how many characters were written is returned and an
678 * error indication is returned in an output argument.
681 * Writes output on the actual channel.
683 *----------------------------------------------------------------------
688 ClientData instanceData, /* Console state. */
689 CONST char *buf, /* The data buffer. */
690 int toWrite, /* How many bytes to write? */
691 int *errorCode) /* Where to store error code. */
693 ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
694 DWORD bytesWritten, timeout;
697 timeout = (infoPtr->flags & CONSOLE_ASYNC) ? 0 : INFINITE;
698 if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) {
700 * The writer thread is blocked waiting for a write to complete
701 * and the channel is in non-blocking mode.
709 * Check for a background error on the last write.
712 if (infoPtr->writeError) {
713 TclWinConvertError(infoPtr->writeError);
714 infoPtr->writeError = 0;
718 if (infoPtr->flags & CONSOLE_ASYNC) {
720 * The console is non-blocking, so copy the data into the output
721 * buffer and restart the writer thread.
724 if (toWrite > infoPtr->writeBufLen) {
726 * Reallocate the buffer to be large enough to hold the data.
729 if (infoPtr->writeBuf) {
730 ckfree(infoPtr->writeBuf);
732 infoPtr->writeBufLen = toWrite;
733 infoPtr->writeBuf = ckalloc((unsigned int) toWrite);
735 memcpy(infoPtr->writeBuf, buf, (size_t) toWrite);
736 infoPtr->toWrite = toWrite;
737 ResetEvent(infoPtr->writable);
738 SetEvent(infoPtr->startWriter);
739 bytesWritten = toWrite;
742 * In the blocking case, just try to write the buffer directly.
743 * This avoids an unnecessary copy.
746 if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite,
747 &bytesWritten, (LPOVERLAPPED) NULL) == FALSE) {
748 TclWinConvertError(GetLastError());
761 *----------------------------------------------------------------------
763 * ConsoleEventProc --
765 * This function is invoked by Tcl_ServiceEvent when a file event
766 * reaches the front of the event queue. This procedure invokes
767 * Tcl_NotifyChannel on the console.
770 * Returns 1 if the event was handled, meaning it should be removed
771 * from the queue. Returns 0 if the event was not handled, meaning
772 * it should stay on the queue. The only time the event isn't
773 * handled is if the TCL_FILE_EVENTS flag bit isn't set.
776 * Whatever the notifier callback does.
778 *----------------------------------------------------------------------
783 Tcl_Event *evPtr, /* Event to service. */
784 int flags) /* Flags that indicate what events to
785 * handle, such as TCL_FILE_EVENTS. */
787 ConsoleEvent *consoleEvPtr = (ConsoleEvent *)evPtr;
788 ConsoleInfo *infoPtr;
790 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
792 if (!(flags & TCL_FILE_EVENTS)) {
797 * Search through the list of watched consoles for the one whose handle
798 * matches the event. We do this rather than simply dereferencing
799 * the handle in the event so that consoles can be deleted while the
800 * event is in the queue.
803 for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;
804 infoPtr = infoPtr->nextPtr) {
805 if (consoleEvPtr->infoPtr == infoPtr) {
806 infoPtr->flags &= ~(CONSOLE_PENDING);
812 * Remove stale events.
820 * Check to see if the console is readable. Note
821 * that we can't tell if a console is writable, so we always report it
822 * as being writable unless we have detected EOF.
826 if (infoPtr->watchMask & TCL_WRITABLE) {
827 if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
832 if (infoPtr->watchMask & TCL_READABLE) {
833 if (WaitForRead(infoPtr, 0) >= 0) {
834 if (infoPtr->readFlags & CONSOLE_EOF) {
837 mask |= TCL_READABLE;
843 * Inform the channel of the events.
846 Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
851 *----------------------------------------------------------------------
853 * ConsoleWatchProc --
855 * Called by the notifier to set up to watch for events on this
864 *----------------------------------------------------------------------
869 ClientData instanceData, /* Console state. */
870 int mask) /* What events to watch for, OR-ed
871 * combination of TCL_READABLE,
872 * TCL_WRITABLE and TCL_EXCEPTION. */
874 ConsoleInfo **nextPtrPtr, *ptr;
875 ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
876 int oldMask = infoPtr->watchMask;
877 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
880 * Since most of the work is handled by the background threads,
881 * we just need to update the watchMask and then force the notifier
885 infoPtr->watchMask = mask & infoPtr->validMask;
886 if (infoPtr->watchMask) {
887 Tcl_Time blockTime = { 0, 0 };
889 infoPtr->nextPtr = tsdPtr->firstConsolePtr;
890 tsdPtr->firstConsolePtr = infoPtr;
892 Tcl_SetMaxBlockTime(&blockTime);
896 * Remove the console from the list of watched consoles.
899 for (nextPtrPtr = &(tsdPtr->firstConsolePtr), ptr = *nextPtrPtr;
901 nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
902 if (infoPtr == ptr) {
903 *nextPtrPtr = ptr->nextPtr;
912 *----------------------------------------------------------------------
914 * ConsoleGetHandleProc --
916 * Called from Tcl_GetChannelHandle to retrieve OS handles from
917 * inside a command consoleline based channel.
920 * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
921 * there is no handle for the specified direction.
926 *----------------------------------------------------------------------
930 ConsoleGetHandleProc(
931 ClientData instanceData, /* The console state. */
932 int direction, /* TCL_READABLE or TCL_WRITABLE */
933 ClientData *handlePtr) /* Where to store the handle. */
935 ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
937 *handlePtr = (ClientData) infoPtr->handle;
942 *----------------------------------------------------------------------
946 * Wait until some data is available, the console is at
947 * EOF or the reader thread is blocked waiting for data (if the
948 * channel is in non-blocking mode).
951 * Returns 1 if console is readable. Returns 0 if there is no data
952 * on the console, but there is buffered data. Returns -1 if an
953 * error occurred. If an error occurred, the threads may not
957 * Updates the shared state flags. If no error occurred,
958 * the reader thread is blocked waiting for a signal from the
961 *----------------------------------------------------------------------
966 ConsoleInfo *infoPtr, /* Console state. */
967 int blocking) /* Indicates whether call should be
968 * blocking or not. */
970 DWORD timeout, count;
971 HANDLE *handle = infoPtr->handle;
976 * Synchronize with the reader thread.
979 timeout = blocking ? INFINITE : 0;
980 if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) {
982 * The reader thread is blocked waiting for data and the channel
983 * is in non-blocking mode.
990 * At this point, the two threads are synchronized, so it is safe
991 * to access shared state.
995 * If the console has hit EOF, it is always readable.
998 if (infoPtr->readFlags & CONSOLE_EOF) {
1002 if (PeekConsoleInput(handle, &input, 1, &count) == FALSE) {
1004 * Check to see if the peek failed because of EOF.
1007 TclWinConvertError(GetLastError());
1010 infoPtr->readFlags |= CONSOLE_EOF;
1015 * Ignore errors if there is data in the buffer.
1018 if (infoPtr->readFlags & CONSOLE_BUFFERED) {
1026 * If there is data in the buffer, the console must be
1027 * readable (since it is a line-oriented device).
1030 if (infoPtr->readFlags & CONSOLE_BUFFERED) {
1036 * There wasn't any data available, so reset the thread and
1040 ResetEvent(infoPtr->readable);
1041 SetEvent(infoPtr->startReader);
1046 *----------------------------------------------------------------------
1048 * ConsoleReaderThread --
1050 * This function runs in a separate thread and waits for input
1051 * to become available on a console.
1057 * Signals the main thread when input become available. May
1058 * cause the main thread to wake up by posting a message. May
1059 * one line from the console for each wait operation.
1061 *----------------------------------------------------------------------
1065 ConsoleReaderThread(LPVOID arg)
1067 ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
1068 HANDLE *handle = infoPtr->handle;
1073 * Wait for the main thread to signal before attempting to wait.
1076 WaitForSingleObject(infoPtr->startReader, INFINITE);
1081 * Look for data on the console, but first ignore any events
1082 * that are not KEY_EVENTs
1084 if (ReadConsole(handle, infoPtr->buffer, CONSOLE_BUFFER_SIZE,
1085 (LPDWORD) &infoPtr->bytesRead, NULL) != FALSE) {
1087 * Data was stored in the buffer.
1090 infoPtr->readFlags |= CONSOLE_BUFFERED;
1093 err = GetLastError();
1096 infoPtr->readFlags = CONSOLE_EOF;
1101 * Signal the main thread by signalling the readable event and
1102 * then waking up the notifier thread.
1105 SetEvent(infoPtr->readable);
1108 * Alert the foreground thread. Note that we need to treat this like
1109 * a critical section so the foreground thread does not terminate
1110 * this thread while we are holding a mutex in the notifier code.
1113 Tcl_MutexLock(&consoleMutex);
1114 Tcl_ThreadAlert(infoPtr->threadId);
1115 Tcl_MutexUnlock(&consoleMutex);
1117 return 0; /* NOT REACHED */
1121 *----------------------------------------------------------------------
1123 * ConsoleWriterThread --
1125 * This function runs in a separate thread and writes data
1132 * Signals the main thread when an output operation is completed.
1133 * May cause the main thread to wake up by posting a message.
1135 *----------------------------------------------------------------------
1139 ConsoleWriterThread(LPVOID arg)
1142 ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
1143 HANDLE *handle = infoPtr->handle;
1144 DWORD count, toWrite;
1149 * Wait for the main thread to signal before attempting to write.
1152 WaitForSingleObject(infoPtr->startWriter, INFINITE);
1154 buf = infoPtr->writeBuf;
1155 toWrite = infoPtr->toWrite;
1158 * Loop until all of the bytes are written or an error occurs.
1161 while (toWrite > 0) {
1162 if (WriteFile(handle, buf, toWrite, &count, NULL) == FALSE) {
1163 infoPtr->writeError = GetLastError();
1172 * Signal the main thread by signalling the writable event and
1173 * then waking up the notifier thread.
1176 SetEvent(infoPtr->writable);
1179 * Alert the foreground thread. Note that we need to treat this like
1180 * a critical section so the foreground thread does not terminate
1181 * this thread while we are holding a mutex in the notifier code.
1184 Tcl_MutexLock(&consoleMutex);
1185 Tcl_ThreadAlert(infoPtr->threadId);
1186 Tcl_MutexUnlock(&consoleMutex);
1188 return 0; /* NOT REACHED */
1194 *----------------------------------------------------------------------
1196 * TclWinOpenConsoleChannel --
1198 * Constructs a Console channel for the specified standard OS handle.
1199 * This is a helper function to break up the construction of
1200 * channels into File, Console, or Serial.
1203 * Returns the new channel, or NULL.
1206 * May open the channel
1208 *----------------------------------------------------------------------
1212 TclWinOpenConsoleChannel(handle, channelName, permissions)
1217 char encoding[4 + TCL_INTEGER_SPACE];
1218 ConsoleInfo *infoPtr;
1219 ThreadSpecificData *tsdPtr;
1222 tsdPtr = ConsoleInit();
1225 * See if a channel with this handle already exists.
1228 infoPtr = (ConsoleInfo *) ckalloc((unsigned) sizeof(ConsoleInfo));
1229 memset(infoPtr, 0, sizeof(ConsoleInfo));
1231 infoPtr->validMask = permissions;
1232 infoPtr->handle = handle;
1234 wsprintfA(encoding, "cp%d", GetConsoleCP());
1237 * Use the pointer for the name of the result channel.
1238 * This keeps the channel names unique, since some may share
1239 * handles (stdin/stdout/stderr for instance).
1242 wsprintfA(channelName, "file%lx", (int) infoPtr);
1244 infoPtr->channel = Tcl_CreateChannel(&consoleChannelType, channelName,
1245 (ClientData) infoPtr, permissions);
1247 infoPtr->threadId = Tcl_GetCurrentThread();
1249 if (permissions & TCL_READABLE) {
1250 infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL);
1251 infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL);
1252 infoPtr->readThread = CreateThread(NULL, 8000, ConsoleReaderThread,
1254 SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST);
1257 if (permissions & TCL_WRITABLE) {
1258 infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL);
1259 infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
1260 infoPtr->writeThread = CreateThread(NULL, 8000, ConsoleWriterThread,
1265 * Files have default translation of AUTO and ^Z eof char, which
1266 * means that a ^Z will be accepted as EOF when reading.
1269 Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
1270 Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
1271 Tcl_SetChannelOption(NULL, infoPtr->channel, "-encoding", encoding);
1273 return infoPtr->channel;