| ︙ | | |
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
+
+
+
+
+
-
-
+
+
+
+
+
+
-
+
|
* Locks are never held when calling the ReadConsole/WriteConsole API's
* since they may block.
*/
static int gInitialized = 0;
/*
* INPUT_BUFFER_SIZE is size of buffer passed to ReadConsole in bytes.
* Note that ReadConsole will only allow reading of line lengths up to the
* max of 256 and buffer size passed to it. So dropping this below 512
* means user can type at most 256 chars.
*/
* Permit CONSOLE_BUFFER_SIZE to be defined on build command for stress test.
*
#ifndef INPUT_BUFFER_SIZE
#define INPUT_BUFFER_SIZE 8192 /* In bytes, so 4096 chars */
#endif
/*
* CONSOLE_BUFFER_SIZE is size of storage used in ring buffers.
* In theory, at least sizeof(WCHAR) but note the Tcl channel bug
* https://core.tcl-lang.org/tcl/tktview/b3977d199b08e3979a8da970553d5209b3042e9c
* will cause failures in test suite if close to max input line in the suite.
*/
#ifndef CONSOLE_BUFFER_SIZE
#define CONSOLE_BUFFER_SIZE 8000 /* In bytes */
#define CONSOLE_BUFFER_SIZE 8192 /* In bytes */
#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 {
|
| ︙ | | |
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
|
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
|
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
|
*errorCode = EWOULDBLOCK;
numRead = -1;
break;
}
/*
* Blocking read. Just get data from directly from console. There
* is a small complication in that we can only read even number
* of bytes (wide-character API) and the destination buffer should be
* WCHAR aligned. If either condition is not met, we defer to the
* reader thread which handles these case rather than dealing with
* is a small complication in that
* 1. The destination buffer should be WCHAR aligned.
* 2. We can only read even number of bytes (wide-character API).
* 3. Caller has large enough buffer (else length of line user can
* enter will be limited)
* If any condition is not met, we defer to the
* reader thread which handles these cases rather than dealing with
* them here (which is a little trickier than it might sound.)
*
* TODO - not clear this block is a useful optimization. bufSize by
* default is 4K which is < INPUT_BUFFER_SIZE and will rarely be
* increased on stdin.
*/
if ((1 & (size_t)bufPtr) == 0 /* aligned buffer */
&& (1 & bufSize) == 0 /* Even number of bytes */
&& bufSize > 1 /* Not single byte read */
&& bufSize > INPUT_BUFFER_SIZE) {
) {
DWORD lastError;
Tcl_Size numChars;
ReleaseSRWLockExclusive(&handleInfoPtr->lock);
lastError = ReadConsoleChars(chanInfoPtr->handle,
(WCHAR *)bufPtr,
bufSize / sizeof(WCHAR),
&numChars);
|
| ︙ | | |
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
|
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
|
-
+
+
+
|
static DWORD WINAPI
ConsoleReaderThread(
LPVOID arg)
{
ConsoleHandleInfo *handleInfoPtr = (ConsoleHandleInfo *) arg;
ConsoleHandleInfo **iterator;
char inputChars[200]; /* Temporary buffer */
Tcl_Size inputLen = 0;
Tcl_Size inputOffset = 0;
Tcl_Size lastReadSize = 0;
DWORD sleepTime;
char inputChars[INPUT_BUFFER_SIZE];
/*
* Keep looping until one of the following happens.
* - there are no more channels listening on the console
* - the console handle has been closed
*/
|
| ︙ | | |
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
|
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
|
-
|
if (inputLen > 0 || handleInfoPtr->lastError != 0) {
HANDLE consoleHandle;
if (inputLen > 0) {
/* Private buffer has data. Copy it over. */
Tcl_Size nStored;
assert((inputLen - inputOffset) > 0);
nStored = RingBufferIn(&handleInfoPtr->buffer,
inputOffset + inputChars,
inputLen - inputOffset,
1);
inputOffset += nStored;
if (inputOffset == inputLen) {
/* Temp buffer now empty */
|
| ︙ | | |
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
|
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
|
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
+
+
-
+
+
+
-
+
-
|
/*
* Loop back to recheck for exit conditions changes while the
* the lock was not held.
*/
continue;
}
assert(inputLen == 0);
/*
* Read more data in two cases:
* Both shared buffer and private buffer are empty. Need to go get
* data from console but do not want to read ahead because the
* interp thread might change the read mode, e.g. turning off echo
* for password input. So only do so if at least one interpreter has
* requested data.
* 1. The previous read filled the buffer and there could be more
* data in the console internal *text* buffer. Note
* ConsolePendingInput (checked in ConsoleDataAvailable) will NOT
* show this. It holds input events not yet translated to text.
* 2. Tcl threads want more data AND there is data in the
* ConsolePendingInput buffer. The latter check necessary because
* we do not want to read ahead because the interp thread might
* change the read mode, e.g. turning off echo for password
* input. So only do so if at least one interpreter has requested
* data.
*/
if (lastReadSize == sizeof(inputChars)
if ((handleInfoPtr->flags & CONSOLE_DATA_AWAITED)
&& ConsoleDataAvailable(handleInfoPtr->console)) {
|| ((handleInfoPtr->flags & CONSOLE_DATA_AWAITED)
&& ConsoleDataAvailable(handleInfoPtr->console))) {
DWORD error;
/* Do not hold the lock while blocked in console */
ReleaseSRWLockExclusive(&handleInfoPtr->lock);
/*
* Note - the temporary buffer serves two purposes. It
*/
error = ReadConsoleChars(handleInfoPtr->console,
(WCHAR *)inputChars,
sizeof(inputChars) / sizeof(WCHAR),
&inputLen);
AcquireSRWLockExclusive(&handleInfoPtr->lock);
if (error == 0) {
inputLen *= sizeof(WCHAR);
lastReadSize = inputLen;
}
} else {
else {
/*
* We only store the last error. It is up to channel
* handlers whether to close or not in case of errors.
*/
lastReadSize = 0;
handleInfoPtr->lastError = error;
if (handleInfoPtr->lastError == ERROR_INVALID_HANDLE) {
handleInfoPtr->console = INVALID_HANDLE_VALUE;
}
}
}
} else {
else {
/*
* Either no one was asking for data, or no data was available.
* In the former case, wait until someone wakes us asking for
* data. In the latter case, there is no alternative but to
* poll since ReadConsole does not support async operation.
* So sleep for a short while and loop back to retry.
*/
DWORD sleepTime;
sleepTime =
handleInfoPtr->flags & CONSOLE_DATA_AWAITED ? 50 : INFINITE;
SleepConditionVariableSRW(&handleInfoPtr->consoleThreadCV,
&handleInfoPtr->lock,
sleepTime,
0);
}
|
| ︙ | | |