os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinConsole.c
First public contribution.
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.
12 * RCS: @(#) $Id: tclWinConsole.c,v 1.11.2.3 2006/03/28 21:02:37 hobbs Exp $
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 stopWriter; /* Auto-reset event used by the main thread to
83 * signal when the writer thread should exit.
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.
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.
97 char *writeBuf; /* Current background output buffer.
98 * Access is synchronized with the writable
100 int writeBufLen; /* Size of write buffer. Access is
101 * synchronized with the writable
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. */
114 typedef struct ThreadSpecificData {
116 * The following pointer refers to the head of the list of consoles
117 * that are being watched for file events.
120 ConsoleInfo *firstConsolePtr;
121 } ThreadSpecificData;
123 static Tcl_ThreadDataKey dataKey;
126 * The following structure is what is added to the Tcl event queue when
127 * console events are generated.
130 typedef struct ConsoleEvent {
131 Tcl_Event header; /* Information that is standard for
133 ConsoleInfo *infoPtr; /* Pointer to console info structure. Note
134 * that we still have to verify that the
135 * console exists before dereferencing this
140 * Declarations for functions used only in this file.
143 static int ConsoleBlockModeProc(ClientData instanceData, int mode);
144 static void ConsoleCheckProc(ClientData clientData, int flags);
145 static int ConsoleCloseProc(ClientData instanceData,
147 static int ConsoleEventProc(Tcl_Event *evPtr, int flags);
148 static void ConsoleExitHandler(ClientData clientData);
149 static int ConsoleGetHandleProc(ClientData instanceData,
150 int direction, ClientData *handlePtr);
151 static void ConsoleInit(void);
152 static int ConsoleInputProc(ClientData instanceData, char *buf,
153 int toRead, int *errorCode);
154 static int ConsoleOutputProc(ClientData instanceData,
155 CONST char *buf, int toWrite, int *errorCode);
156 static DWORD WINAPI ConsoleReaderThread(LPVOID arg);
157 static void ConsoleSetupProc(ClientData clientData, int flags);
158 static void ConsoleWatchProc(ClientData instanceData, int mask);
159 static DWORD WINAPI ConsoleWriterThread(LPVOID arg);
160 static void ProcExitHandler(ClientData clientData);
161 static int WaitForRead(ConsoleInfo *infoPtr, int blocking);
163 static void ConsoleThreadActionProc _ANSI_ARGS_ ((
164 ClientData instanceData, int action));
167 * This structure describes the channel type structure for command console
171 static 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 */
191 *----------------------------------------------------------------------
195 * This function initializes the static variables for this file.
201 * Creates a new event source.
203 *----------------------------------------------------------------------
209 ThreadSpecificData *tsdPtr;
212 * Check the initialized flag first, then check again in the mutex.
213 * This is a speed enhancement.
217 Tcl_MutexLock(&consoleMutex);
220 Tcl_CreateExitHandler(ProcExitHandler, NULL);
222 Tcl_MutexUnlock(&consoleMutex);
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);
235 *----------------------------------------------------------------------
237 * ConsoleExitHandler --
239 * This function is called to cleanup the console module before
246 * Removes the console event source.
248 *----------------------------------------------------------------------
253 ClientData clientData) /* Old window proc */
255 Tcl_DeleteEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
259 *----------------------------------------------------------------------
263 * This function is called to cleanup the process list before
270 * Resets the process list.
272 *----------------------------------------------------------------------
277 ClientData clientData) /* Old window proc */
279 Tcl_MutexLock(&consoleMutex);
281 Tcl_MutexUnlock(&consoleMutex);
285 *----------------------------------------------------------------------
287 * ConsoleSetupProc --
289 * This procedure is invoked before Tcl_DoOneEvent blocks waiting
296 * Adjusts the block time if needed.
298 *----------------------------------------------------------------------
303 ClientData data, /* Not used. */
304 int flags) /* Event flags as passed to Tcl_DoOneEvent. */
306 ConsoleInfo *infoPtr;
307 Tcl_Time blockTime = { 0, 0 };
309 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
311 if (!(flags & TCL_FILE_EVENTS)) {
316 * Look to see if any events are already pending. If they are, poll.
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) {
326 if (infoPtr->watchMask & TCL_READABLE) {
327 if (WaitForRead(infoPtr, 0) >= 0) {
333 Tcl_SetMaxBlockTime(&blockTime);
338 *----------------------------------------------------------------------
340 * ConsoleCheckProc --
342 * This procedure is called by Tcl_DoOneEvent to check the console
343 * event source for events.
349 * May queue an event.
351 *----------------------------------------------------------------------
356 ClientData data, /* Not used. */
357 int flags) /* Event flags as passed to Tcl_DoOneEvent. */
359 ConsoleInfo *infoPtr;
362 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
364 if (!(flags & TCL_FILE_EVENTS)) {
369 * Queue events for any ready consoles that don't already have events
373 for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;
374 infoPtr = infoPtr->nextPtr) {
375 if (infoPtr->flags & CONSOLE_PENDING) {
380 * Queue an event if the console is signaled for reading or writing.
384 if (infoPtr->watchMask & TCL_WRITABLE) {
385 if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
390 if (infoPtr->watchMask & TCL_READABLE) {
391 if (WaitForRead(infoPtr, 0) >= 0) {
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);
408 *----------------------------------------------------------------------
410 * ConsoleBlockModeProc --
412 * Set blocking or non-blocking mode on channel.
415 * 0 if successful, errno when failed.
418 * Sets the device into blocking or non-blocking mode.
420 *----------------------------------------------------------------------
424 ConsoleBlockModeProc(
425 ClientData instanceData, /* Instance data for channel. */
426 int mode) /* TCL_MODE_BLOCKING or
427 * TCL_MODE_NONBLOCKING. */
429 ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
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.
438 if (mode == TCL_MODE_NONBLOCKING) {
439 infoPtr->flags |= CONSOLE_ASYNC;
441 infoPtr->flags &= ~(CONSOLE_ASYNC);
447 *----------------------------------------------------------------------
449 * ConsoleCloseProc --
451 * Closes a console based IO channel.
454 * 0 on success, errno otherwise.
457 * Closes the physical channel.
459 *----------------------------------------------------------------------
464 ClientData instanceData, /* Pointer to ConsoleInfo structure. */
465 Tcl_Interp *interp) /* For error reporting. */
467 ConsoleInfo *consolePtr = (ConsoleInfo *) instanceData;
469 ConsoleInfo *infoPtr, **nextPtrPtr;
470 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
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.
481 if (consolePtr->readThread) {
484 * The thread may already have closed on it's own. Check it's
488 GetExitCodeThread(consolePtr->readThread, &exitCode);
490 if (exitCode == STILL_ACTIVE) {
493 * Set the stop event so that if the reader thread is blocked
494 * in ConsoleReaderThread on WaitForMultipleEvents, it will exit
498 SetEvent(consolePtr->stopReader);
501 * Wait at most 20 milliseconds for the reader thread to close.
504 if (WaitForSingleObject(consolePtr->readThread, 20)
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
514 Tcl_MutexLock(&consoleMutex);
516 /* BUG: this leaks memory. */
517 TerminateThread(consolePtr->readThread, 0);
518 Tcl_MutexUnlock(&consoleMutex);
522 CloseHandle(consolePtr->readThread);
523 CloseHandle(consolePtr->readable);
524 CloseHandle(consolePtr->startReader);
525 CloseHandle(consolePtr->stopReader);
526 consolePtr->readThread = NULL;
528 consolePtr->validMask &= ~TCL_READABLE;
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.
536 if (consolePtr->writeThread) {
537 if (consolePtr->toWrite) {
539 * We only need to wait if there is something to write.
540 * This may prevent infinite wait on exit. [python bug 216289]
542 WaitForSingleObject(consolePtr->writable, INFINITE);
546 * The thread may already have closed on it's own. Check it's
550 GetExitCodeThread(consolePtr->writeThread, &exitCode);
552 if (exitCode == STILL_ACTIVE) {
554 * Set the stop event so that if the reader thread is blocked
555 * in ConsoleWriterThread on WaitForMultipleEvents, it will
559 SetEvent(consolePtr->stopWriter);
562 * Wait at most 20 milliseconds for the writer thread to close.
565 if (WaitForSingleObject(consolePtr->writeThread, 20)
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
575 Tcl_MutexLock(&consoleMutex);
577 /* BUG: this leaks memory. */
578 TerminateThread(consolePtr->writeThread, 0);
579 Tcl_MutexUnlock(&consoleMutex);
583 CloseHandle(consolePtr->writeThread);
584 CloseHandle(consolePtr->writable);
585 CloseHandle(consolePtr->startWriter);
586 CloseHandle(consolePtr->stopWriter);
587 consolePtr->writeThread = NULL;
589 consolePtr->validMask &= ~TCL_WRITABLE;
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.
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());
608 consolePtr->watchMask &= consolePtr->validMask;
611 * Remove the file from the list of watched files.
614 for (nextPtrPtr = &(tsdPtr->firstConsolePtr), infoPtr = *nextPtrPtr;
616 nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
617 if (infoPtr == (ConsoleInfo *)consolePtr) {
618 *nextPtrPtr = infoPtr->nextPtr;
622 if (consolePtr->writeBuf != NULL) {
623 ckfree(consolePtr->writeBuf);
624 consolePtr->writeBuf = 0;
626 ckfree((char*) consolePtr);
632 *----------------------------------------------------------------------
634 * ConsoleInputProc --
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.
640 * A count of how many bytes were read is returned and an error
641 * indication is returned in an output argument.
644 * Reads input from the actual channel.
646 *----------------------------------------------------------------------
651 ClientData instanceData, /* Console state. */
652 char *buf, /* Where to store data read. */
653 int bufSize, /* How much space is available
655 int *errorCode) /* Where to store error code. */
657 ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
658 DWORD count, bytesRead = 0;
664 * Synchronize with the reader thread.
667 result = WaitForRead(infoPtr, (infoPtr->flags & CONSOLE_ASYNC) ? 0 : 1);
670 * If an error occurred, return immediately.
678 if (infoPtr->readFlags & CONSOLE_BUFFERED) {
680 * Data is stored in the buffer.
683 if (bufSize < (infoPtr->bytesRead - infoPtr->offset)) {
684 memcpy(buf, &infoPtr->buffer[infoPtr->offset], (size_t) bufSize);
686 infoPtr->offset += bufSize;
688 memcpy(buf, &infoPtr->buffer[infoPtr->offset], (size_t) bufSize);
689 bytesRead = infoPtr->bytesRead - infoPtr->offset;
695 infoPtr->readFlags &= ~CONSOLE_BUFFERED;
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.
708 if (ReadConsole(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &count,
709 (LPOVERLAPPED) NULL) == TRUE) {
718 *----------------------------------------------------------------------
720 * ConsoleOutputProc --
722 * Writes the given output on the IO channel. Returns count of how
723 * many characters were actually written, and an error indication.
726 * A count of how many characters were written is returned and an
727 * error indication is returned in an output argument.
730 * Writes output on the actual channel.
732 *----------------------------------------------------------------------
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. */
742 ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
743 DWORD bytesWritten, timeout;
746 timeout = (infoPtr->flags & CONSOLE_ASYNC) ? 0 : INFINITE;
747 if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) {
749 * The writer thread is blocked waiting for a write to complete
750 * and the channel is in non-blocking mode.
758 * Check for a background error on the last write.
761 if (infoPtr->writeError) {
762 TclWinConvertError(infoPtr->writeError);
763 infoPtr->writeError = 0;
767 if (infoPtr->flags & CONSOLE_ASYNC) {
769 * The console is non-blocking, so copy the data into the output
770 * buffer and restart the writer thread.
773 if (toWrite > infoPtr->writeBufLen) {
775 * Reallocate the buffer to be large enough to hold the data.
778 if (infoPtr->writeBuf) {
779 ckfree(infoPtr->writeBuf);
781 infoPtr->writeBufLen = toWrite;
782 infoPtr->writeBuf = ckalloc((unsigned int) toWrite);
784 memcpy(infoPtr->writeBuf, buf, (size_t) toWrite);
785 infoPtr->toWrite = toWrite;
786 ResetEvent(infoPtr->writable);
787 SetEvent(infoPtr->startWriter);
788 bytesWritten = toWrite;
791 * In the blocking case, just try to write the buffer directly.
792 * This avoids an unnecessary copy.
795 if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite,
796 &bytesWritten, (LPOVERLAPPED) NULL) == FALSE) {
797 TclWinConvertError(GetLastError());
810 *----------------------------------------------------------------------
812 * ConsoleEventProc --
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.
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.
825 * Whatever the notifier callback does.
827 *----------------------------------------------------------------------
832 Tcl_Event *evPtr, /* Event to service. */
833 int flags) /* Flags that indicate what events to
834 * handle, such as TCL_FILE_EVENTS. */
836 ConsoleEvent *consoleEvPtr = (ConsoleEvent *)evPtr;
837 ConsoleInfo *infoPtr;
839 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
841 if (!(flags & TCL_FILE_EVENTS)) {
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.
852 for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;
853 infoPtr = infoPtr->nextPtr) {
854 if (consoleEvPtr->infoPtr == infoPtr) {
855 infoPtr->flags &= ~(CONSOLE_PENDING);
861 * Remove stale events.
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.
875 if (infoPtr->watchMask & TCL_WRITABLE) {
876 if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
881 if (infoPtr->watchMask & TCL_READABLE) {
882 if (WaitForRead(infoPtr, 0) >= 0) {
883 if (infoPtr->readFlags & CONSOLE_EOF) {
886 mask |= TCL_READABLE;
892 * Inform the channel of the events.
895 Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
900 *----------------------------------------------------------------------
902 * ConsoleWatchProc --
904 * Called by the notifier to set up to watch for events on this
913 *----------------------------------------------------------------------
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. */
923 ConsoleInfo **nextPtrPtr, *ptr;
924 ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
925 int oldMask = infoPtr->watchMask;
926 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
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
934 infoPtr->watchMask = mask & infoPtr->validMask;
935 if (infoPtr->watchMask) {
936 Tcl_Time blockTime = { 0, 0 };
938 infoPtr->nextPtr = tsdPtr->firstConsolePtr;
939 tsdPtr->firstConsolePtr = infoPtr;
941 Tcl_SetMaxBlockTime(&blockTime);
945 * Remove the console from the list of watched consoles.
948 for (nextPtrPtr = &(tsdPtr->firstConsolePtr), ptr = *nextPtrPtr;
950 nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
951 if (infoPtr == ptr) {
952 *nextPtrPtr = ptr->nextPtr;
961 *----------------------------------------------------------------------
963 * ConsoleGetHandleProc --
965 * Called from Tcl_GetChannelHandle to retrieve OS handles from
966 * inside a command consoleline based channel.
969 * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
970 * there is no handle for the specified direction.
975 *----------------------------------------------------------------------
979 ConsoleGetHandleProc(
980 ClientData instanceData, /* The console state. */
981 int direction, /* TCL_READABLE or TCL_WRITABLE */
982 ClientData *handlePtr) /* Where to store the handle. */
984 ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
986 *handlePtr = (ClientData) infoPtr->handle;
991 *----------------------------------------------------------------------
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).
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
1006 * Updates the shared state flags. If no error occurred,
1007 * the reader thread is blocked waiting for a signal from the
1010 *----------------------------------------------------------------------
1015 ConsoleInfo *infoPtr, /* Console state. */
1016 int blocking) /* Indicates whether call should be
1017 * blocking or not. */
1019 DWORD timeout, count;
1020 HANDLE *handle = infoPtr->handle;
1025 * Synchronize with the reader thread.
1028 timeout = blocking ? INFINITE : 0;
1029 if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) {
1031 * The reader thread is blocked waiting for data and the channel
1032 * is in non-blocking mode.
1039 * At this point, the two threads are synchronized, so it is safe
1040 * to access shared state.
1044 * If the console has hit EOF, it is always readable.
1047 if (infoPtr->readFlags & CONSOLE_EOF) {
1051 if (PeekConsoleInput(handle, &input, 1, &count) == FALSE) {
1053 * Check to see if the peek failed because of EOF.
1056 TclWinConvertError(GetLastError());
1059 infoPtr->readFlags |= CONSOLE_EOF;
1064 * Ignore errors if there is data in the buffer.
1067 if (infoPtr->readFlags & CONSOLE_BUFFERED) {
1075 * If there is data in the buffer, the console must be
1076 * readable (since it is a line-oriented device).
1079 if (infoPtr->readFlags & CONSOLE_BUFFERED) {
1085 * There wasn't any data available, so reset the thread and
1089 ResetEvent(infoPtr->readable);
1090 SetEvent(infoPtr->startReader);
1095 *----------------------------------------------------------------------
1097 * ConsoleReaderThread --
1099 * This function runs in a separate thread and waits for input
1100 * to become available on a console.
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.
1110 *----------------------------------------------------------------------
1114 ConsoleReaderThread(LPVOID arg)
1116 ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
1117 HANDLE *handle = infoPtr->handle;
1118 DWORD count, waitResult;
1121 /* The first event takes precedence. */
1122 wEvents[0] = infoPtr->stopReader;
1123 wEvents[1] = infoPtr->startReader;
1127 * Wait for the main thread to signal before attempting to wait.
1130 waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
1132 if (waitResult != (WAIT_OBJECT_0 + 1)) {
1134 * The start event was not signaled. It must be the stop event
1135 * or an error, so exit this thread.
1144 * Look for data on the console, but first ignore any events
1145 * that are not KEY_EVENTs
1147 if (ReadConsoleA(handle, infoPtr->buffer, CONSOLE_BUFFER_SIZE,
1148 (LPDWORD) &infoPtr->bytesRead, NULL) != FALSE) {
1150 * Data was stored in the buffer.
1153 infoPtr->readFlags |= CONSOLE_BUFFERED;
1156 err = GetLastError();
1159 infoPtr->readFlags = CONSOLE_EOF;
1164 * Signal the main thread by signalling the readable event and
1165 * then waking up the notifier thread.
1168 SetEvent(infoPtr->readable);
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.
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);
1181 Tcl_MutexUnlock(&consoleMutex);
1188 *----------------------------------------------------------------------
1190 * ConsoleWriterThread --
1192 * This function runs in a separate thread and writes data
1199 * Signals the main thread when an output operation is completed.
1200 * May cause the main thread to wake up by posting a message.
1202 *----------------------------------------------------------------------
1206 ConsoleWriterThread(LPVOID arg)
1209 ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
1210 HANDLE *handle = infoPtr->handle;
1211 DWORD count, toWrite, waitResult;
1215 /* The first event takes precedence. */
1216 wEvents[0] = infoPtr->stopWriter;
1217 wEvents[1] = infoPtr->startWriter;
1221 * Wait for the main thread to signal before attempting to write.
1224 waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
1226 if (waitResult != (WAIT_OBJECT_0 + 1)) {
1228 * The start event was not signaled. It must be the stop event
1229 * or an error, so exit this thread.
1235 buf = infoPtr->writeBuf;
1236 toWrite = infoPtr->toWrite;
1239 * Loop until all of the bytes are written or an error occurs.
1242 while (toWrite > 0) {
1243 if (WriteConsoleA(handle, buf, toWrite, &count, NULL) == FALSE) {
1244 infoPtr->writeError = GetLastError();
1253 * Signal the main thread by signalling the writable event and
1254 * then waking up the notifier thread.
1257 SetEvent(infoPtr->writable);
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.
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);
1270 Tcl_MutexUnlock(&consoleMutex);
1279 *----------------------------------------------------------------------
1281 * TclWinOpenConsoleChannel --
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.
1288 * Returns the new channel, or NULL.
1291 * May open the channel
1293 *----------------------------------------------------------------------
1297 TclWinOpenConsoleChannel(handle, channelName, permissions)
1302 char encoding[4 + TCL_INTEGER_SPACE];
1303 ConsoleInfo *infoPtr;
1309 * See if a channel with this handle already exists.
1312 infoPtr = (ConsoleInfo *) ckalloc((unsigned) sizeof(ConsoleInfo));
1313 memset(infoPtr, 0, sizeof(ConsoleInfo));
1315 infoPtr->validMask = permissions;
1316 infoPtr->handle = handle;
1317 infoPtr->channel = (Tcl_Channel) NULL;
1319 wsprintfA(encoding, "cp%d", GetConsoleCP());
1321 infoPtr->threadId = Tcl_GetCurrentThread();
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).
1329 wsprintfA(channelName, "file%lx", (int) infoPtr);
1331 infoPtr->channel = Tcl_CreateChannel(&consoleChannelType, channelName,
1332 (ClientData) infoPtr, permissions);
1334 if (permissions & TCL_READABLE) {
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
1341 GetConsoleMode(infoPtr->handle, &modes);
1342 modes &= ~(ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
1343 modes |= ENABLE_LINE_INPUT;
1344 SetConsoleMode(infoPtr->handle, modes);
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,
1351 SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST);
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,
1360 SetThreadPriority(infoPtr->writeThread, THREAD_PRIORITY_HIGHEST);
1364 * Files have default translation of AUTO and ^Z eof char, which
1365 * means that a ^Z will be accepted as EOF when reading.
1368 Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
1369 Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
1370 Tcl_SetChannelOption(NULL, infoPtr->channel, "-encoding", encoding);
1372 return infoPtr->channel;
1376 *----------------------------------------------------------------------
1378 * ConsoleThreadActionProc --
1380 * Insert or remove any thread local refs to this channel.
1386 * Changes thread local list of valid channels.
1388 *----------------------------------------------------------------------
1392 ConsoleThreadActionProc (instanceData, action)
1393 ClientData instanceData;
1396 ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
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.
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.
1414 if (infoPtr->channel != NULL) {
1415 infoPtr->threadId = Tcl_GetChannelThread (infoPtr->channel);
1418 infoPtr->threadId = NULL;
1420 Tcl_MutexUnlock(&consoleMutex);