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