Diff
Not logged in

Differences From Artifact [9949d14d8e]:

To Artifact [f7134a68f8]:


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
80
81
82
83
84
85
86







87
88



89
90
91
92
93
94
95
96
97
98







-
-
-
-
-
-
-


-
-
-
+
+
+







#define CONSOLE_BUFFER_SIZE 8000 /* In bytes */
#endif

/*
 * Ring buffer for storing data. Actual data is from bufPtr[start]:bufPtr[size-1]
 * and bufPtr[0]:bufPtr[length - (size-start)].
 */
#if TCL_MAJOR_VERSION > 8
typedef ptrdiff_t RingSizeT; /* Tcl9 TODO */
#define RingSizeT_MAX PTRDIFF_MAX
#else
typedef int RingSizeT;
#define RingSizeT_MAX INT_MAX
#endif
typedef struct RingBuffer {
    char *bufPtr;	/* Pointer to buffer storage */
    RingSizeT capacity;	/* Size of the buffer in RingBufferChar */
    RingSizeT start;	/* Start of the data within the buffer. */
    RingSizeT length;	/* Number of RingBufferChar*/
    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
230
231
232
233
234
235
236
237

238
239
240
241



242
243
244
245
246




247
248
249
250
251
252
253
223
224
225
226
227
228
229

230
231



232
233
234
235




236
237
238
239
240
241
242
243
244
245
246







-
+

-
-
-
+
+
+

-
-
-
-
+
+
+
+







		    Tcl_Interp *interp, const char *optionName,
		    const char *value);
static void	ConsoleSetupProc(void *clientData, int flags);
static void	ConsoleWatchProc(void *instanceData, int mask);
static void	ProcExitHandler(void *clientData);
static void	ConsoleThreadActionProc(void *instanceData, int action);
static DWORD	ReadConsoleChars(HANDLE hConsole, WCHAR *lpBuffer,
		    RingSizeT nChars, RingSizeT *nCharsReadPtr);
		    Tcl_Size nChars, Tcl_Size *nCharsReadPtr);
static DWORD	WriteConsoleChars(HANDLE hConsole,
		    const WCHAR *lpBuffer, RingSizeT nChars,
		    RingSizeT *nCharsWritten);
static void	RingBufferInit(RingBuffer *ringPtr, RingSizeT capacity);
		    const WCHAR *lpBuffer, Tcl_Size nChars,
		    Tcl_Size *nCharsWritten);
static void	RingBufferInit(RingBuffer *ringPtr, Tcl_Size capacity);
static void	RingBufferClear(RingBuffer *ringPtr);
static RingSizeT	RingBufferIn(RingBuffer *ringPtr, const char *srcPtr,
			    RingSizeT srcLen, int partialCopyOk);
static RingSizeT	RingBufferOut(RingBuffer *ringPtr, char *dstPtr,
			    RingSizeT dstCapacity, int partialCopyOk);
static Tcl_Size	RingBufferIn(RingBuffer *ringPtr, const char *srcPtr,
			    Tcl_Size srcLen, int partialCopyOk);
static Tcl_Size	RingBufferOut(RingBuffer *ringPtr, char *dstPtr,
			    Tcl_Size dstCapacity, int partialCopyOk);
static ConsoleHandleInfo *AllocateConsoleHandleInfo(HANDLE consoleHandle,
			    int permissions);
static ConsoleHandleInfo *FindConsoleInfo(const ConsoleChannelInfo *);
static DWORD WINAPI	ConsoleReaderThread(LPVOID arg);
static DWORD WINAPI	ConsoleWriterThread(LPVOID arg);
static void		NudgeWatchers(HANDLE consoleHandle);
#ifndef NDEBUG
327
328
329
330
331
332
333
334

335
336

337
338
339
340
341
342
343
320
321
322
323
324
325
326

327
328

329
330
331
332
333
334
335
336







-
+

-
+







 *
 * Side effects:
 *    Panics on allocation failure.
 *
 *------------------------------------------------------------------------
 */
static void
RingBufferInit(RingBuffer *ringPtr, RingSizeT capacity)
RingBufferInit(RingBuffer *ringPtr, Tcl_Size capacity)
{
    if (capacity <= 0 || capacity > RingSizeT_MAX) {
    if (capacity <= 0 || capacity > TCL_SIZE_MAX) {
	Tcl_Panic("Internal error: invalid ring buffer capacity requested.");
    }
    ringPtr->bufPtr = (char *)Tcl_Alloc(capacity);
    ringPtr->capacity = capacity;
    ringPtr->start    = 0;
    ringPtr->length   = 0;
}
380
381
382
383
384
385
386
387

388
389
390
391

392
393
394
395

396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411


412
413
414
415
416
417
418
419
420
421
422

423
424
425
426
427
428
429
373
374
375
376
377
378
379

380
381
382
383

384
385
386
387

388
389
390
391
392
393
394
395
396
397
398
399
400
401
402


403
404
405
406
407
408
409
410
411
412
413
414

415
416
417
418
419
420
421
422







-
+



-
+



-
+














-
-
+
+










-
+







 *    Returns number of bytes copied.
 *
 * Side effects:
 *    Internal buffer is updated.
 *
 *------------------------------------------------------------------------
 */
static RingSizeT
static Tcl_Size
RingBufferIn(
    RingBuffer *ringPtr,
    const char *srcPtr, /* Source to be copied */
    RingSizeT srcLen,	  /* Length of source */
    Tcl_Size srcLen,	  /* Length of source */
    int partialCopyOk 		  /* If true, partial copy is permitted */
    )
{
    RingSizeT freeSpace;
    Tcl_Size freeSpace;

    RINGBUFFER_ASSERT(ringPtr);

    freeSpace = ringPtr->capacity - ringPtr->length;
    if (freeSpace < srcLen) {
	if (!partialCopyOk) {
	    return 0;
	}
	/* Copy only as much as free space allows */
	srcLen = freeSpace;
    }

    if (ringPtr->capacity - ringPtr->start > ringPtr->length) {
	/* There is room at the back */
	RingSizeT endSpaceStart = ringPtr->start + ringPtr->length;
	RingSizeT endSpace      = ringPtr->capacity - endSpaceStart;
	Tcl_Size endSpaceStart = ringPtr->start + ringPtr->length;
	Tcl_Size endSpace      = ringPtr->capacity - endSpaceStart;
	if (endSpace >= srcLen) {
	    /* Everything fits at the back */
	    memmove(endSpaceStart + ringPtr->bufPtr, srcPtr, srcLen);
	} else {
	    /* 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. */
	RingSizeT wrapLen =
	Tcl_Size wrapLen =
	    ringPtr->start + ringPtr->length - ringPtr->capacity;
	memmove(wrapLen + ringPtr->bufPtr, srcPtr, srcLen);
    }

    ringPtr->length += srcLen;

    RINGBUFFER_ASSERT(ringPtr);
443
444
445
446
447
448
449
450

451
452
453

454
455
456

457
458
459
460
461
462
463
436
437
438
439
440
441
442

443
444
445

446
447
448

449
450
451
452
453
454
455
456







-
+


-
+


-
+







 *    Returns number of bytes copied or removed.
 *
 * Side effects:
 *    Internal buffer is updated.
 *
 *------------------------------------------------------------------------
 */
static RingSizeT
static Tcl_Size
RingBufferOut(RingBuffer *ringPtr,
	      char *dstPtr,	      /* Buffer for output data. May be NULL */
	      RingSizeT dstCapacity,  /* Size of buffer */
	      Tcl_Size dstCapacity,  /* Size of buffer */
	      int partialCopyOk)      /* If true, return what's available */
{
    RingSizeT leadLen;
    Tcl_Size leadLen;

    RINGBUFFER_ASSERT(ringPtr);

    if (dstCapacity > ringPtr->length) {
	if (dstPtr && !partialCopyOk) {
	    return 0;
	}
473
474
475
476
477
478
479
480

481
482
483
484
485
486
487
466
467
468
469
470
471
472

473
474
475
476
477
478
479
480







-
+







    }
    if (leadLen >= dstCapacity) {
	if (dstPtr) {
	    memmove(dstPtr, ringPtr->start + ringPtr->bufPtr, dstCapacity);
	}
	ringPtr->start += dstCapacity;
    } else {
	RingSizeT wrapLen = dstCapacity - leadLen;
	Tcl_Size wrapLen = dstCapacity - leadLen;
	if (dstPtr) {
	    memmove(dstPtr,
		    ringPtr->start + ringPtr->bufPtr,
		    leadLen);
	    memmove(
		leadLen + dstPtr, ringPtr->bufPtr, wrapLen);
	}
525
526
527
528
529
530
531
532
533


534
535
536
537
538
539
540
518
519
520
521
522
523
524


525
526
527
528
529
530
531
532
533







-
-
+
+







 *
 *------------------------------------------------------------------------
 */
static DWORD
ReadConsoleChars(
    HANDLE hConsole,
    WCHAR *lpBuffer,
    RingSizeT nChars,
    RingSizeT *nCharsReadPtr)
    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.
585
586
587
588
589
590
591
592
593


594
595
596
597
598
599
600
578
579
580
581
582
583
584


585
586
587
588
589
590
591
592
593







-
-
+
+







 *------------------------------------------------------------------------
 */

static DWORD
WriteConsoleChars(
    HANDLE hConsole,
    const WCHAR *lpBuffer,
    RingSizeT nChars,
    RingSizeT *nCharsWrittenPtr)
    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);
1086
1087
1088
1089
1090
1091
1092
1093

1094
1095
1096
1097
1098
1099
1100
1079
1080
1081
1082
1083
1084
1085

1086
1087
1088
1089
1090
1091
1092
1093







-
+







    char *bufPtr,		/* Where to store data read. */
    int bufSize,		/* How much space is available in the
				 * buffer? */
    int *errorCode)		/* Where to store error code. */
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
    ConsoleHandleInfo *handleInfoPtr;
    RingSizeT numRead;
    Tcl_Size numRead;

    if (chanInfoPtr->handle == INVALID_HANDLE_VALUE) {
	return 0; /* EOF */
    }

    *errorCode = 0;

1156
1157
1158
1159
1160
1161
1162
1163

1164
1165
1166
1167
1168
1169
1170
1149
1150
1151
1152
1153
1154
1155

1156
1157
1158
1159
1160
1161
1162
1163







-
+







	 * reader thread which handles these case rather than dealing with
	 * them here (which is a little trickier than it might sound.)
	 */
	if ((1 & (ptrdiff_t)bufPtr) == 0 /* aligned buffer */
	    && bufSize > 1         /* Not single byte read */
	) {
	    DWORD lastError;
	    RingSizeT numChars;
	    Tcl_Size numChars;
	    ReleaseSRWLockExclusive(&handleInfoPtr->lock);
	    lastError = ReadConsoleChars(chanInfoPtr->handle,
					 (WCHAR *)bufPtr,
					 bufSize / sizeof(WCHAR),
					 &numChars);
	    /* NOTE lock released so DON'T break. Return instead */
	    if (lastError != ERROR_SUCCESS) {
1238
1239
1240
1241
1242
1243
1244
1245

1246
1247
1248
1249
1250
1251
1252
1231
1232
1233
1234
1235
1236
1237

1238
1239
1240
1241
1242
1243
1244
1245







-
+







    void *instanceData,	/* Console state. */
    const char *buf,		/* The data buffer. */
    int toWrite,		/* How many bytes to write? */
    int *errorCode)		/* Where to store error code. */
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
    ConsoleHandleInfo *handleInfoPtr;
    RingSizeT numWritten;
    Tcl_Size numWritten;

    *errorCode = 0;

    if (chanInfoPtr->handle == INVALID_HANDLE_VALUE) {
	/* Some other thread would have *previously* closed the stdio handle */
	*errorCode = EPIPE;
	return -1;
1634
1635
1636
1637
1638
1639
1640
1641
1642


1643
1644
1645
1646
1647
1648
1649
1627
1628
1629
1630
1631
1632
1633


1634
1635
1636
1637
1638
1639
1640
1641
1642







-
-
+
+







static DWORD WINAPI
ConsoleReaderThread(
    LPVOID arg)
{
    ConsoleHandleInfo *handleInfoPtr = (ConsoleHandleInfo *) arg;
    ConsoleHandleInfo **iterator;
    char inputChars[200]; /* Temporary buffer */
    RingSizeT inputLen = 0;
    RingSizeT inputOffset = 0;
    Tcl_Size inputLen = 0;
    Tcl_Size inputOffset = 0;

    /*
     * Keep looping until one of the following happens.
     * - there are no more channels listening on the console
     * - the console handle has been closed
     */

1666
1667
1668
1669
1670
1671
1672
1673

1674
1675
1676
1677
1678
1679
1680
1659
1660
1661
1662
1663
1664
1665

1666
1667
1668
1669
1670
1671
1672
1673







-
+







	 * copy that. Else check if there has been an error. In both cases
	 * notify the interp threads.
	 */
	if (inputLen > 0 || handleInfoPtr->lastError != 0) {
	    HANDLE consoleHandle;
	    if (inputLen > 0) {
		/* Private buffer has data. Copy it over. */
		RingSizeT nStored;
		Tcl_Size nStored;

		assert((inputLen - inputOffset) > 0);

		nStored = RingBufferIn(&handleInfoPtr->buffer,
				       inputOffset + inputChars,
				       inputLen - inputOffset,
				       1);
1829
1830
1831
1832
1833
1834
1835
1836

1837
1838
1839
1840
1841
1842
1843
1822
1823
1824
1825
1826
1827
1828

1829
1830
1831
1832
1833
1834
1835
1836







-
+







 */
static DWORD WINAPI
ConsoleWriterThread(LPVOID arg)
{
    ConsoleHandleInfo *handleInfoPtr = (ConsoleHandleInfo *) arg;
    ConsoleHandleInfo **iterator;
    BOOL success;
    RingSizeT numBytes;
    Tcl_Size numBytes;
    /*
     * This buffer size has no relation really with the size of the shared
     * buffer. Could be bigger or smaller. Make larger as multiple threads
     * could potentially be writing to it.
     */
    char buffer[2*CONSOLE_BUFFER_SIZE];

1900
1901
1902
1903
1904
1905
1906
1907

1908
1909
1910
1911
1912
1913
1914
1893
1894
1895
1896
1897
1898
1899

1900
1901
1902
1903
1904
1905
1906
1907







-
+







	RingBufferOut(&handleInfoPtr->buffer, buffer, numBytes, 0);

	consoleHandle = handleInfoPtr->console;
	WakeConditionVariable(&handleInfoPtr->interpThreadCV);
	ReleaseSRWLockExclusive(&handleInfoPtr->lock);
	offset = 0;
	while (numBytes > 0) {
	    RingSizeT numWChars = numBytes / sizeof(WCHAR);
	    Tcl_Size numWChars = numBytes / sizeof(WCHAR);
	    DWORD status;
	    status = WriteConsoleChars(handleInfoPtr->console,
				       (WCHAR *)(offset + buffer),
				       numWChars,
				       &numWChars);
	    if (status != 0) {
		/* Only overwrite if no previous error */