| ︙ | | | ︙ | |
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
*
* - Unlike files, sockets etc. where there is a one-to-one
* correspondence between Tcl channels and operating system handles,
* std* channels are shared amongst threads which means there can be
* multiple Tcl channels corresponding to a single console handle.
*
* - Even with multiple threads, more than one file event handler is
* unlikely. It does not make sense for multiple threads to register
* handlers for stdin because the input would be randomly fragmented amongst
* the threads.
*
* Various design factors are driven by the above, e.g. use of lists instead
* of hash tables (at most 3 console handles) and use of global instead of
* per thread queues which simplifies lock management particularly because
* thread-console relation is not one-one and is likely more performant as
* well with fewer locks needing to be obtained.
*
* Some additional design notes/reminders for the future:
*
* Aligned, synchronous reads are done directly by interpreter thread.
* Unaligned or asynchronous reads are done through the reader thread.
*
* The reader thread does not read ahead. That is, it will not post a read
* until some interpreter thread is actually requesting a read. This is
* because an interpreter may (for example) turn off echo for passwords and
* the read ahead would come in the way of that.
*
* If multiple threads are reading from stdin, the input is sprayed in
* random fashion. This is not good application design and hence no plan to
* address this (not clear what should be done even in theory)
*
* For output, we do not restrict all output to the console writer threads.
* See ConsoleOutputProc for the conditions.
*
* Locks are never held when calling the ReadConsole/WriteConsole API's
* since they may block.
*/
|
|
|
|
|
|
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
*
* - Unlike files, sockets etc. where there is a one-to-one
* correspondence between Tcl channels and operating system handles,
* std* channels are shared amongst threads which means there can be
* multiple Tcl channels corresponding to a single console handle.
*
* - Even with multiple threads, more than one file event handler is
* unlikely. It does not make sense for multiple threads to register
* handlers for stdin because the input would be randomly fragmented amongst
* the threads.
*
* Various design factors are driven by the above, e.g. use of lists instead
* of hash tables (at most 3 console handles) and use of global instead of
* per thread queues which simplifies lock management particularly because
* thread-console relation is not one-one and is likely more performant as
* well with fewer locks needing to be obtained.
*
* Some additional design notes/reminders for the future:
*
* Aligned, synchronous reads are done directly by interpreter thread.
* Unaligned or asynchronous reads are done through the reader thread.
*
* The reader thread does not read ahead. That is, it will not post a read
* until some interpreter thread is actually requesting a read. This is
* because an interpreter may (for example) turn off echo for passwords and
* the read ahead would come in the way of that.
*
* If multiple threads are reading from stdin, the input is sprayed in
* random fashion. This is not good application design and hence no plan to
* address this (not clear what should be done even in theory).
*
* For output, we do not restrict all output to the console writer threads.
* See ConsoleOutputProc for the conditions.
*
* Locks are never held when calling the ReadConsole/WriteConsole API's
* since they may block.
*/
|
| ︙ | | | ︙ | |
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
#endif
/*
* Ring buffer for storing data. Actual data is from bufPtr[start]:bufPtr[size-1]
* and bufPtr[0]:bufPtr[length - (size-start)].
*/
typedef struct RingBuffer {
char *bufPtr; /* Pointer to buffer storage */
Tcl_Size capacity; /* Size of the buffer in RingBufferChar */
Tcl_Size start; /* Start of the data within the buffer. */
Tcl_Size length; /* Number of RingBufferChar*/
} RingBuffer;
#define RingBufferLength(ringPtr_) ((ringPtr_)->length)
#define RingBufferHasFreeSpace(ringPtr_) ((ringPtr_)->length < (ringPtr_)->capacity)
#define RINGBUFFER_ASSERT(ringPtr_) assert(RingBufferCheck(ringPtr_))
/*
* The Win32 console API does not support non-blocking I/O in any form. Thus
* the actual calls are made on a separate thread. Moreover, separate
* threads are needed for each handle because (for example) blocking on user
* input on stdin should not prevent output to stdout when non-blocking i/o
* is configured at the script level.
|
|
|
|
|
>
|
>
|
>
|
>
|
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
#endif
/*
* Ring buffer for storing data. Actual data is from bufPtr[start]:bufPtr[size-1]
* and bufPtr[0]:bufPtr[length - (size-start)].
*/
typedef struct RingBuffer {
char *bufPtr; /* Pointer to buffer storage */
Tcl_Size capacity; /* Size of the buffer in RingBufferChar */
Tcl_Size start; /* Start of the data within the buffer. */
Tcl_Size length; /* Number of RingBufferChar*/
} RingBuffer;
#define RingBufferLength(ringPtr_) \
((ringPtr_)->length)
#define RingBufferHasFreeSpace(ringPtr_) \
((ringPtr_)->length < (ringPtr_)->capacity)
#define RINGBUFFER_ASSERT(ringPtr_) \
assert(RingBufferCheck(ringPtr_))
/*
* The Win32 console API does not support non-blocking I/O in any form. Thus
* the actual calls are made on a separate thread. Moreover, separate
* threads are needed for each handle because (for example) blocking on user
* input on stdin should not prevent output to stdout when non-blocking i/o
* is configured at the script level.
|
| ︙ | | | ︙ | |
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
* Note on reference counting - a ConsoleHandleInfo instance has multiple
* references to it - one each from every channel that is attached to it
* plus one from the console thread itself which also serves as the reference
* from gConsoleHandleInfoList.
*/
typedef struct ConsoleHandleInfo {
struct ConsoleHandleInfo *nextPtr; /* Process-global list of consoles */
HANDLE console; /* Console handle */
HANDLE consoleThread; /* Handle to thread doing actual i/o on the console */
SRWLOCK lock; /* Controls access to this structure.
* Cheaper than CRITICAL_SECTION but note does not
* support recursive locks or Try* style attempts.*/
CONDITION_VARIABLE consoleThreadCV;/* For awakening console thread */
CONDITION_VARIABLE interpThreadCV; /* For awakening interpthread(s) */
RingBuffer buffer; /* Buffer for data transferred between console
* threads and Tcl threads. For input consoles,
* written by the console thread and read by Tcl
* threads. The converse for output threads */
DWORD initMode; /* Initial console mode. */
DWORD lastError; /* An error caused by the last background
* operation. Set to 0 if no error has been
* detected. */
int numRefs; /* See comments above */
int permissions; /* TCL_READABLE for input consoles, TCL_WRITABLE
* for output. Only one or the other can be set. */
int flags;
#define CONSOLE_DATA_AWAITED 0x0001 /* An interpreter is awaiting data */
} ConsoleHandleInfo;
/*
* This structure describes per-instance data for a console based channel.
*
* Note on locking - this structure has no locks because it is accessed
* only from the thread owning channel EXCEPT when a console traverses it
* looking for a channel that is watching for events on the console. Even
|
|
|
>
|
|
|
>
|
|
|
|
|
|
|
|
|
|
|
>
|
>
|
|
>
>
|
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
* Note on reference counting - a ConsoleHandleInfo instance has multiple
* references to it - one each from every channel that is attached to it
* plus one from the console thread itself which also serves as the reference
* from gConsoleHandleInfoList.
*/
typedef struct ConsoleHandleInfo {
struct ConsoleHandleInfo *nextPtr; /* Process-global list of consoles */
HANDLE console; /* Console handle */
HANDLE consoleThread; /* Handle to thread doing actual i/o on the
* console */
SRWLOCK lock; /* Controls access to this structure. Cheaper
* than CRITICAL_SECTION but note does not
* support recursive locks or Try* style
* attempts. */
CONDITION_VARIABLE consoleThreadCV;/* For awakening console thread */
CONDITION_VARIABLE interpThreadCV; /* For awakening interpthread(s) */
RingBuffer buffer; /* Buffer for data transferred between console
* threads and Tcl threads. For input consoles,
* written by the console thread and read by Tcl
* threads. The converse for output threads */
DWORD initMode; /* Initial console mode. */
DWORD lastError; /* An error caused by the last background
* operation. Set to 0 if no error has been
* detected. */
int numRefs; /* See comments above */
int permissions; /* TCL_READABLE for input consoles,
* TCL_WRITABLE for output. Only one or the
* other can be set. */
int flags; /* State flags */
} ConsoleHandleInfo;
enum ConsoleHandleInfoFlags {
CONSOLE_DATA_AWAITED = 1 /* An interpreter is awaiting data */
};
/*
* This structure describes per-instance data for a console based channel.
*
* Note on locking - this structure has no locks because it is accessed
* only from the thread owning channel EXCEPT when a console traverses it
* looking for a channel that is watching for events on the console. Even
|
| ︙ | | | ︙ | |
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
int permissions; /* OR'ed combination of TCL_READABLE,
* TCL_WRITABLE, or TCL_EXCEPTION: indicates
* which operations are valid on the file. */
int watchMask; /* OR'ed combination of TCL_READABLE,
* TCL_WRITABLE, or TCL_EXCEPTION: indicates
* which events should be reported. */
int flags; /* State flags */
#define CONSOLE_EVENT_QUEUED 0x0001 /* Notification event already queued */
#define CONSOLE_ASYNC 0x0002 /* Channel is non-blocking. */
#define CONSOLE_READ_OPS 0x0004 /* Channel supports read-related ops. */
} ConsoleChannelInfo;
/*
* The following structure is what is added to the Tcl event queue when
* console events are generated.
*/
typedef struct {
|
>
>
>
|
|
|
<
>
|
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
int permissions; /* OR'ed combination of TCL_READABLE,
* TCL_WRITABLE, or TCL_EXCEPTION: indicates
* which operations are valid on the file. */
int watchMask; /* OR'ed combination of TCL_READABLE,
* TCL_WRITABLE, or TCL_EXCEPTION: indicates
* which events should be reported. */
int flags; /* State flags */
} ConsoleChannelInfo;
enum ConsoleChannelInfoFlags {
CONSOLE_EVENT_QUEUED = 1, /* Notification event already queued */
CONSOLE_ASYNC = 2, /* Channel is non-blocking. */
CONSOLE_READ_OPS = 4 /* Channel supports read-related ops. */
};
/*
* The following structure is what is added to the Tcl event queue when
* console events are generated.
*/
typedef struct {
|
| ︙ | | | ︙ | |
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
|
/* srcLen > endSpace */
memmove(endSpaceStart + ringPtr->bufPtr, srcPtr, endSpace);
memmove(ringPtr->bufPtr, endSpace + srcPtr, srcLen - endSpace);
}
} else {
/* No room at the back. Existing data wrap to front. */
Tcl_Size wrapLen =
ringPtr->start + ringPtr->length - ringPtr->capacity;
memmove(wrapLen + ringPtr->bufPtr, srcPtr, srcLen);
}
ringPtr->length += srcLen;
RINGBUFFER_ASSERT(ringPtr);
|
|
|
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
|
/* srcLen > endSpace */
memmove(endSpaceStart + ringPtr->bufPtr, srcPtr, endSpace);
memmove(ringPtr->bufPtr, endSpace + srcPtr, srcLen - endSpace);
}
} else {
/* No room at the back. Existing data wrap to front. */
Tcl_Size wrapLen =
ringPtr->start + ringPtr->length - ringPtr->capacity;
memmove(wrapLen + ringPtr->bufPtr, srcPtr, srcLen);
}
ringPtr->length += srcLen;
RINGBUFFER_ASSERT(ringPtr);
|
| ︙ | | | ︙ | |
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
|
if (dstPtr) {
memmove(dstPtr, ringPtr->start + ringPtr->bufPtr, dstCapacity);
}
ringPtr->start += dstCapacity;
} else {
Tcl_Size wrapLen = dstCapacity - leadLen;
if (dstPtr) {
memmove(dstPtr,
ringPtr->start + ringPtr->bufPtr,
leadLen);
memmove(
leadLen + dstPtr, ringPtr->bufPtr, wrapLen);
}
ringPtr->start = wrapLen;
}
ringPtr->length -= dstCapacity;
if (ringPtr->start == ringPtr->capacity || ringPtr->length == 0) {
ringPtr->start = 0;
|
<
|
<
<
|
|
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
|
if (dstPtr) {
memmove(dstPtr, ringPtr->start + ringPtr->bufPtr, dstCapacity);
}
ringPtr->start += dstCapacity;
} else {
Tcl_Size wrapLen = dstCapacity - leadLen;
if (dstPtr) {
memmove(dstPtr, ringPtr->start + ringPtr->bufPtr, leadLen);
memmove(leadLen + dstPtr, ringPtr->bufPtr, wrapLen);
}
ringPtr->start = wrapLen;
}
ringPtr->length -= dstCapacity;
if (ringPtr->start == ringPtr->capacity || ringPtr->length == 0) {
ringPtr->start = 0;
|
| ︙ | | | ︙ | |
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
|
ReadConsoleChars(
HANDLE hConsole,
WCHAR *lpBuffer,
Tcl_Size nChars,
Tcl_Size *nCharsReadPtr)
{
DWORD nRead;
BOOL result;
/*
* If user types a Ctrl-Break or Ctrl-C, ReadConsole will return success
* with ntchars == 0 and GetLastError() will be ERROR_OPERATION_ABORTED.
* If no Ctrl signal handlers have been established, the default signal
* OS handler in a separate thread will terminate the program. If a Ctrl
* signal handler has been established (through an extension for
|
<
|
545
546
547
548
549
550
551
552
553
554
555
556
557
558
|
ReadConsoleChars(
HANDLE hConsole,
WCHAR *lpBuffer,
Tcl_Size nChars,
Tcl_Size *nCharsReadPtr)
{
DWORD nRead;
/*
* If user types a Ctrl-Break or Ctrl-C, ReadConsole will return success
* with ntchars == 0 and GetLastError() will be ERROR_OPERATION_ABORTED.
* If no Ctrl signal handlers have been established, the default signal
* OS handler in a separate thread will terminate the program. If a Ctrl
* signal handler has been established (through an extension for
|
| ︙ | | | ︙ | |
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
|
* In both cases above we will return success but with nbytesread as 0.
* This allows caller to check for thread termination etc.
*
* See https://bugs.python.org/issue30237
* or https://github.com/microsoft/terminal/issues/12143
*/
nRead = (DWORD)-1;
result = ReadConsoleW(hConsole, lpBuffer, nChars, &nRead, NULL);
if (result) {
if ((nRead == 0 || nRead == (DWORD)-1)
&& GetLastError() == ERROR_OPERATION_ABORTED) {
nRead = 0;
}
*nCharsReadPtr = nRead;
return 0;
} else {
return GetLastError();
}
}
/*
*------------------------------------------------------------------------
*
* WriteConsoleChars --
*
|
|
|
>
|
|
|
|
|
|
<
<
<
|
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
|
* In both cases above we will return success but with nbytesread as 0.
* This allows caller to check for thread termination etc.
*
* See https://bugs.python.org/issue30237
* or https://github.com/microsoft/terminal/issues/12143
*/
nRead = (DWORD)-1;
if (!ReadConsoleW(hConsole, lpBuffer, nChars, &nRead, NULL)) {
return GetLastError();
}
if ((nRead == 0 || nRead == (DWORD)-1)
&& GetLastError() == ERROR_OPERATION_ABORTED) {
nRead = 0;
}
*nCharsReadPtr = nRead;
return 0;
}
/*
*------------------------------------------------------------------------
*
* WriteConsoleChars --
*
|
| ︙ | | | ︙ | |
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
|
WriteConsoleChars(
HANDLE hConsole,
const WCHAR *lpBuffer,
Tcl_Size nChars,
Tcl_Size *nCharsWrittenPtr)
{
DWORD nCharsWritten;
BOOL result;
/* See comments in ReadConsoleChars, not sure that applies here */
nCharsWritten = (DWORD)-1;
result = WriteConsoleW(hConsole, lpBuffer, nChars, &nCharsWritten, NULL);
if (result) {
if (nCharsWritten == (DWORD) -1) {
nCharsWritten = 0;
}
*nCharsWrittenPtr = nCharsWritten;
return 0;
} else {
return GetLastError();
}
}
/*
*----------------------------------------------------------------------
*
* ConsoleInit --
*
|
<
|
|
>
|
|
|
|
|
<
<
<
|
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
|
WriteConsoleChars(
HANDLE hConsole,
const WCHAR *lpBuffer,
Tcl_Size nChars,
Tcl_Size *nCharsWrittenPtr)
{
DWORD nCharsWritten;
/* See comments in ReadConsoleChars, not sure that applies here */
nCharsWritten = (DWORD)-1;
if (!WriteConsoleW(hConsole, lpBuffer, nChars, &nCharsWritten, NULL)) {
return GetLastError();
}
if (nCharsWritten == (DWORD) -1) {
nCharsWritten = 0;
}
*nCharsWrittenPtr = nCharsWritten;
return 0;
}
/*
*----------------------------------------------------------------------
*
* ConsoleInit --
*
|
| ︙ | | | ︙ | |
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
|
* Walk the list of channels. See general comments for struct
* ConsoleChannelInfo with regard to locking and field access.
*/
AcquireSRWLockShared(&gConsoleLock); /* READ lock - no data modification */
for (chanInfoPtr = gWatchingChannelList; block && chanInfoPtr != NULL;
chanInfoPtr = chanInfoPtr->nextWatchingChannelPtr) {
ConsoleHandleInfo *handleInfoPtr;
handleInfoPtr = FindConsoleInfo(chanInfoPtr);
if (handleInfoPtr != NULL) {
AcquireSRWLockShared(&handleInfoPtr->lock);
/* Remember at most one of READABLE, WRITABLE set */
if (chanInfoPtr->watchMask & TCL_READABLE) {
if (RingBufferLength(&handleInfoPtr->buffer) > 0
|| handleInfoPtr->lastError != ERROR_SUCCESS) {
block = 0; /* Input data available */
|
|
<
|
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
|
* Walk the list of channels. See general comments for struct
* ConsoleChannelInfo with regard to locking and field access.
*/
AcquireSRWLockShared(&gConsoleLock); /* READ lock - no data modification */
for (chanInfoPtr = gWatchingChannelList; block && chanInfoPtr != NULL;
chanInfoPtr = chanInfoPtr->nextWatchingChannelPtr) {
ConsoleHandleInfo *handleInfoPtr = FindConsoleInfo(chanInfoPtr);
if (handleInfoPtr != NULL) {
AcquireSRWLockShared(&handleInfoPtr->lock);
/* Remember at most one of READABLE, WRITABLE set */
if (chanInfoPtr->watchMask & TCL_READABLE) {
if (RingBufferLength(&handleInfoPtr->buffer) > 0
|| handleInfoPtr->lastError != ERROR_SUCCESS) {
block = 0; /* Input data available */
|
| ︙ | | | ︙ | |
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
|
TCL_UNUSED(int) /*direction*/,
void **handlePtr) /* Where to store the handle. */
{
ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
if (chanInfoPtr->handle == INVALID_HANDLE_VALUE) {
return TCL_ERROR;
} else {
*handlePtr = chanInfoPtr->handle;
return TCL_OK;
}
}
/*
*------------------------------------------------------------------------
*
* ConsoleDataAvailable --
*
* Checks if there is data in the console input queue.
*
* Results:
* Returns 1 if the input queue has data, -1 on error else 0 if empty.
*
* Side effects:
* None.
*
*------------------------------------------------------------------------
*/
static int
ConsoleDataAvailable(
HANDLE consoleHandle)
{
INPUT_RECORD input[10];
DWORD count;
DWORD i;
/*
|
<
>
|
|
<
|
|
|
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
|
TCL_UNUSED(int) /*direction*/,
void **handlePtr) /* Where to store the handle. */
{
ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
if (chanInfoPtr->handle == INVALID_HANDLE_VALUE) {
return TCL_ERROR;
}
*handlePtr = chanInfoPtr->handle;
return TCL_OK;
}
/*
*------------------------------------------------------------------------
*
* ConsoleDataAvailable --
*
* Checks if there is data in the console input queue.
*
* Results:
* Returns 1 if the input queue has data, -1 on error else 0 if empty.
*
* Side effects:
* None.
*
*------------------------------------------------------------------------
*/
static int
ConsoleDataAvailable(
HANDLE consoleHandle)
{
INPUT_RECORD input[10];
DWORD count;
DWORD i;
/*
|
| ︙ | | | ︙ | |
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
|
if (input[i].EventType == KEY_EVENT
&& input[i].Event.KeyEvent.bKeyDown) {
return 1;
}
}
return 0;
}
/*
*----------------------------------------------------------------------
*
* ConsoleReaderThread --
*
* This function runs in a separate thread and waits for input to become
* available on a console.
*
* Results:
* Always 0.
*
* Side effects:
* Signals the main thread when input become available.
*
*----------------------------------------------------------------------
*/
static DWORD WINAPI
ConsoleReaderThread(
LPVOID arg)
{
ConsoleHandleInfo *handleInfoPtr = (ConsoleHandleInfo *) arg;
ConsoleHandleInfo **iterator;
Tcl_Size inputLen = 0;
|
|
<
|
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
|
if (input[i].EventType == KEY_EVENT
&& input[i].Event.KeyEvent.bKeyDown) {
return 1;
}
}
return 0;
}
/*
*----------------------------------------------------------------------
*
* ConsoleReaderThread --
*
* This function runs in a separate thread and waits for input to become
* available on a console.
*
* Results:
* Always 0.
*
* Side effects:
* Signals the main thread when input become available.
*
*----------------------------------------------------------------------
*/
static DWORD WINAPI
ConsoleReaderThread(
LPVOID arg)
{
ConsoleHandleInfo *handleInfoPtr = (ConsoleHandleInfo *) arg;
ConsoleHandleInfo **iterator;
Tcl_Size inputLen = 0;
|
| ︙ | | | ︙ | |
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
|
*iterator = handleInfoPtr->nextPtr;
break;
}
}
ReleaseSRWLockExclusive(&gConsoleLock);
RingBufferClear(&handleInfoPtr->buffer);
Tcl_Free(handleInfoPtr);
return 0;
}
/*
*------------------------------------------------------------------------
|
<
|
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
|
*iterator = handleInfoPtr->nextPtr;
break;
}
}
ReleaseSRWLockExclusive(&gConsoleLock);
RingBufferClear(&handleInfoPtr->buffer);
Tcl_Free(handleInfoPtr);
return 0;
}
/*
*------------------------------------------------------------------------
|
| ︙ | | | ︙ | |
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
|
GetConsoleMode(consoleHandle, &handleInfoPtr->initMode);
consoleMode = handleInfoPtr->initMode;
consoleMode &= ~(ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
consoleMode |= ENABLE_LINE_INPUT;
SetConsoleMode(consoleHandle, consoleMode);
}
handleInfoPtr->consoleThread = CreateThread(
NULL, /* default security descriptor */
2*CONSOLE_BUFFER_SIZE, /* Stack size - gets rounded up to granularity */
permissions == TCL_READABLE ? ConsoleReaderThread : ConsoleWriterThread,
handleInfoPtr, /* Pass to thread */
0, /* Flags - no special cases */
NULL); /* Don't care about thread id */
if (handleInfoPtr->consoleThread == NULL) {
/* Note - SRWLock and condition variables do not need finalization */
RingBufferClear(&handleInfoPtr->buffer);
Tcl_Free(handleInfoPtr);
return NULL;
}
|
|
|
|
|
|
|
|
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
|
GetConsoleMode(consoleHandle, &handleInfoPtr->initMode);
consoleMode = handleInfoPtr->initMode;
consoleMode &= ~(ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
consoleMode |= ENABLE_LINE_INPUT;
SetConsoleMode(consoleHandle, consoleMode);
}
handleInfoPtr->consoleThread = CreateThread(
NULL, /* default security descriptor */
2*CONSOLE_BUFFER_SIZE, /* Stack size - gets rounded up to granularity */
permissions == TCL_READABLE ? ConsoleReaderThread : ConsoleWriterThread,
handleInfoPtr, /* Pass to thread */
0, /* Flags - no special cases */
NULL); /* Don't care about thread id */
if (handleInfoPtr->consoleThread == NULL) {
/* Note - SRWLock and condition variables do not need finalization */
RingBufferClear(&handleInfoPtr->buffer);
Tcl_Free(handleInfoPtr);
return NULL;
}
|
| ︙ | | | ︙ | |
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
|
*------------------------------------------------------------------------
*/
static ConsoleHandleInfo *
FindConsoleInfo(
const ConsoleChannelInfo *chanInfoPtr)
{
ConsoleHandleInfo *handleInfoPtr;
for (handleInfoPtr = gConsoleHandleInfoList; handleInfoPtr; handleInfoPtr = handleInfoPtr->nextPtr) {
if (handleInfoPtr->console == chanInfoPtr->handle) {
return handleInfoPtr;
}
}
return NULL;
}
/*
*----------------------------------------------------------------------
*
* TclWinOpenConsoleChannel --
*
* Constructs a Console channel for the specified standard OS handle.
* This is a helper function to break up the construction of channels
|
|
>
|
|
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
|
*------------------------------------------------------------------------
*/
static ConsoleHandleInfo *
FindConsoleInfo(
const ConsoleChannelInfo *chanInfoPtr)
{
ConsoleHandleInfo *handleInfoPtr;
for (handleInfoPtr = gConsoleHandleInfoList; handleInfoPtr;
handleInfoPtr = handleInfoPtr->nextPtr) {
if (handleInfoPtr->console == chanInfoPtr->handle) {
return handleInfoPtr;
}
}
return NULL;
}
/*
*----------------------------------------------------------------------
*
* TclWinOpenConsoleChannel --
*
* Constructs a Console channel for the specified standard OS handle.
* This is a helper function to break up the construction of channels
|
| ︙ | | | ︙ | |
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
|
* May modify an option on a console. Sets Error message if needed (by
* calling Tcl_BadChannelOption).
*
*----------------------------------------------------------------------
*/
static int
ConsoleSetOptionProc(
void *instanceData, /* File state. */
Tcl_Interp *interp, /* For error reporting - can be NULL. */
const char *optionName, /* Which option to set? */
const char *value) /* New value for option. */
{
ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
int len = strlen(optionName);
int vlen = strlen(value);
|
|
|
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
|
* May modify an option on a console. Sets Error message if needed (by
* calling Tcl_BadChannelOption).
*
*----------------------------------------------------------------------
*/
static int
ConsoleSetOptionProc(
void *instanceData, /* File state. */
Tcl_Interp *interp, /* For error reporting - can be NULL. */
const char *optionName, /* Which option to set? */
const char *value) /* New value for option. */
{
ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
int len = strlen(optionName);
int vlen = strlen(value);
|
| ︙ | | | ︙ | |
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
|
* (by calling Tcl_BadChannelOption).
*
*----------------------------------------------------------------------
*/
static int
ConsoleGetOptionProc(
void *instanceData, /* File state. */
Tcl_Interp *interp, /* For error reporting - can be NULL. */
const char *optionName, /* Option to get. */
Tcl_DString *dsPtr) /* Where to store value(s). */
{
ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
int valid = 0; /* Flag if valid option parsed. */
unsigned int len;
|
|
|
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
|
* (by calling Tcl_BadChannelOption).
*
*----------------------------------------------------------------------
*/
static int
ConsoleGetOptionProc(
void *instanceData, /* File state. */
Tcl_Interp *interp, /* For error reporting - can be NULL. */
const char *optionName, /* Option to get. */
Tcl_DString *dsPtr) /* Where to store value(s). */
{
ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
int valid = 0; /* Flag if valid option parsed. */
unsigned int len;
|
| ︙ | | | ︙ | |