Tcl Source Code

Check-in [13886141d8]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Added a refcounting mechanism to ChannelBuffers. Other edits to stop segfaults in tests iocmd-21.2[12].
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | core-8-5-branch
Files: files | file ages | folders
SHA1: 13886141d8b3acc777afc94fb9c24b9e10578566
User & Date: dgp 2014-04-21 18:55:41
References
2022-04-20
12:03 New ticket [a12ad5c4bd] buffer allocation on every call to Write in tclIO.c. artifact: 9f3f644b4e user: pooryorick
Context
2014-04-22
14:57
Add refcounting and preservation to [testchannel transform] to stop segfault in test iogt-2.4. check-in: 1f873e8005 user: dgp tags: core-8-5-branch
13:25
Memory leak after thread exit, fixed (alloc cache released by exit), belong to ticket [3493120]

M... check-in: c251d61424 user: sebres tags: bug-3493120

2014-04-21
20:01
merge 8.5 check-in: 621d5cbc6e user: dgp tags: dgp-read-bytes
19:04
Merge refcounting machinery for ChannelBuffer. check-in: 0c1015d94d user: dgp tags: trunk
18:55
Added a refcounting mechanism to ChannelBuffers. Other edits to stop segfaults in tests iocmd-21.2[... check-in: 13886141d8 user: dgp tags: core-8-5-branch
2014-04-17
19:58
Another test exposing another segfault. check-in: dbd29ee73d user: dgp tags: core-8-5-branch
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to generic/tclIO.c.

158
159
160
161
162
163
164


165
166
167
168
169
170
171
} CloseCallback;

/*
 * Static functions in this file:
 */

static ChannelBuffer *	AllocChannelBuffer(int length);


static void		ChannelTimerProc(ClientData clientData);
static int		CheckChannelErrors(ChannelState *statePtr,
			    int direction);
static int		CheckForDeadChannel(Tcl_Interp *interp,
			    ChannelState *statePtr);
static void		CheckForStdChannelsBeingClosed(Tcl_Channel chan);
static void		CleanupChannelHandlers(Tcl_Interp *interp,







>
>







158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
} CloseCallback;

/*
 * Static functions in this file:
 */

static ChannelBuffer *	AllocChannelBuffer(int length);
static void		PreserveChannelBuffer(ChannelBuffer *bufPtr);
static void		ReleaseChannelBuffer(ChannelBuffer *bufPtr);
static void		ChannelTimerProc(ClientData clientData);
static int		CheckChannelErrors(ChannelState *statePtr,
			    int direction);
static int		CheckForDeadChannel(Tcl_Interp *interp,
			    ChannelState *statePtr);
static void		CheckForStdChannelsBeingClosed(Tcl_Channel chan);
static void		CleanupChannelHandlers(Tcl_Interp *interp,
348
349
350
351
352
353
354

355
356
357
358
359
360
361
ChanRead(
    Channel *chanPtr,
    char *dst,
    int dstSize,
    int *errnoPtr)
{
    if (WillRead(chanPtr) < 0) {

        return -1;
    }

    return chanPtr->typePtr->inputProc(chanPtr->instanceData, dst, dstSize,
	    errnoPtr);
}








>







350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
ChanRead(
    Channel *chanPtr,
    char *dst,
    int dstSize,
    int *errnoPtr)
{
    if (WillRead(chanPtr) < 0) {
	*errnoPtr = Tcl_GetErrno();
        return -1;
    }

    return chanPtr->typePtr->inputProc(chanPtr->instanceData, dst, dstSize,
	    errnoPtr);
}

2212
2213
2214
2215
2216
2217
2218

2219
2220

















2221
2222
2223
2224
2225
2226
2227

    n = length + CHANNELBUFFER_HEADER_SIZE + BUFFER_PADDING + BUFFER_PADDING;
    bufPtr = (ChannelBuffer *) ckalloc((unsigned) n);
    bufPtr->nextAdded	= BUFFER_PADDING;
    bufPtr->nextRemoved	= BUFFER_PADDING;
    bufPtr->bufLength	= length + BUFFER_PADDING;
    bufPtr->nextPtr	= NULL;

    return bufPtr;
}


















/*
 *----------------------------------------------------------------------
 *
 * RecycleBuffer --
 *
 *	Helper function to recycle input and output buffers. Ensures that two







>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248

    n = length + CHANNELBUFFER_HEADER_SIZE + BUFFER_PADDING + BUFFER_PADDING;
    bufPtr = (ChannelBuffer *) ckalloc((unsigned) n);
    bufPtr->nextAdded	= BUFFER_PADDING;
    bufPtr->nextRemoved	= BUFFER_PADDING;
    bufPtr->bufLength	= length + BUFFER_PADDING;
    bufPtr->nextPtr	= NULL;
    bufPtr->refCount	= 1;
    return bufPtr;
}

static void
PreserveChannelBuffer(
    ChannelBuffer *bufPtr)
{
    bufPtr->refCount++;
}

static void
ReleaseChannelBuffer(
    ChannelBuffer *bufPtr)
{
    if (--bufPtr->refCount) {
	return;
    }
    ckfree((char *) bufPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * RecycleBuffer --
 *
 *	Helper function to recycle input and output buffers. Ensures that two
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
				 * always. */
{
    /*
     * Do we have to free the buffer to the OS?
     */

    if (mustDiscard) {
	ckfree((char *) bufPtr);
	return;
    }

    /*
     * Only save buffers which are at least as big as the requested buffersize
     * for the channel. This is to honor dynamic changes of the buffersize
     * made by the user.
     */

    if ((bufPtr->bufLength - BUFFER_PADDING) < statePtr->bufSize) {
	ckfree((char *) bufPtr);
	return;
    }

    /*
     * Only save buffers for the input queue if the channel is readable.
     */








|










|







2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
				 * always. */
{
    /*
     * Do we have to free the buffer to the OS?
     */

    if (mustDiscard) {
	ReleaseChannelBuffer(bufPtr);
	return;
    }

    /*
     * Only save buffers which are at least as big as the requested buffersize
     * for the channel. This is to honor dynamic changes of the buffersize
     * made by the user.
     */

    if ((bufPtr->bufLength - BUFFER_PADDING) < statePtr->bufSize) {
	ReleaseChannelBuffer(bufPtr);
	return;
    }

    /*
     * Only save buffers for the input queue if the channel is readable.
     */

2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
	}
    }

    /*
     * If we reached this code we return the buffer to the OS.
     */

    ckfree((char *) bufPtr);
    return;

  keepBuffer:
    bufPtr->nextRemoved = BUFFER_PADDING;
    bufPtr->nextAdded = BUFFER_PADDING;
    bufPtr->nextPtr = NULL;
}







|







2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
	}
    }

    /*
     * If we reached this code we return the buffer to the OS.
     */

    ReleaseChannelBuffer(bufPtr);
    return;

  keepBuffer:
    bufPtr->nextRemoved = BUFFER_PADDING;
    bufPtr->nextAdded = BUFFER_PADDING;
    bufPtr->nextPtr = NULL;
}
2466
2467
2468
2469
2470
2471
2472

2473
2474
2475
2476
2477
2478
2479
	    break;	/* Out of the "while (1)". */
	}

	/*
	 * Produce the output on the channel.
	 */


	toWrite = BytesLeft(bufPtr);
	if (toWrite == 0) {
	    written = 0;
	} else {
	    written = (chanPtr->typePtr->outputProc)(chanPtr->instanceData,
		RemovePoint(bufPtr), toWrite, &errorCode);
	}







>







2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
	    break;	/* Out of the "while (1)". */
	}

	/*
	 * Produce the output on the channel.
	 */

	PreserveChannelBuffer(bufPtr);
	toWrite = BytesLeft(bufPtr);
	if (toWrite == 0) {
	    written = 0;
	} else {
	    written = (chanPtr->typePtr->outputProc)(chanPtr->instanceData,
		RemovePoint(bufPtr), toWrite, &errorCode);
	}
2593
2594
2595
2596
2597
2598
2599

2600
2601
2602
2603
2604
2605
2606
	if (IsBufferEmpty(bufPtr)) {
	    statePtr->outQueueHead = bufPtr->nextPtr;
	    if (statePtr->outQueueHead == NULL) {
		statePtr->outQueueTail = NULL;
	    }
	    RecycleBuffer(statePtr, bufPtr, 0);
	}

    }	/* Closes "while (1)". */

    /*
     * If we wrote some data while flushing in the background, we are done.
     * We can't finish the background flush until we run out of data and the
     * channel becomes writable again. This ensures that all of the pending
     * data has been flushed at the system level.







>







2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
	if (IsBufferEmpty(bufPtr)) {
	    statePtr->outQueueHead = bufPtr->nextPtr;
	    if (statePtr->outQueueHead == NULL) {
		statePtr->outQueueTail = NULL;
	    }
	    RecycleBuffer(statePtr, bufPtr, 0);
	}
	ReleaseChannelBuffer(bufPtr);
    }	/* Closes "while (1)". */

    /*
     * If we wrote some data while flushing in the background, we are done.
     * We can't finish the background flush until we run out of data and the
     * channel becomes writable again. This ensures that all of the pending
     * data has been flushed at the system level.
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
    DiscardInputQueued(statePtr, 1);

    /*
     * Discard a leftover buffer in the current output buffer field.
     */

    if (statePtr->curOutPtr != NULL) {
	ckfree((char *) statePtr->curOutPtr);
	statePtr->curOutPtr = NULL;
    }

    /*
     * The caller guarantees that there are no more buffers queued for output.
     */








|







2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
    DiscardInputQueued(statePtr, 1);

    /*
     * Discard a leftover buffer in the current output buffer field.
     */

    if (statePtr->curOutPtr != NULL) {
	ReleaseChannelBuffer(statePtr->curOutPtr);
	statePtr->curOutPtr = NULL;
    }

    /*
     * The caller guarantees that there are no more buffers queued for output.
     */

3562
3563
3564
3565
3566
3567
3568





3569
3570
3571
3572
3573
3574
3575
        DiscardInputQueued(chanPtr->state, 0);
        ChanSeek(chanPtr, - inputBuffered, SEEK_CUR, &ignore);
    }
}

static int WillRead(Channel *chanPtr)
{





    if ((chanPtr->typePtr->seekProc != NULL)
        && (Tcl_OutputBuffered((Tcl_Channel) chanPtr) > 0)) {
        if ((chanPtr->state->curOutPtr != NULL)
            && IsBufferReady(chanPtr->state->curOutPtr)) {
            SetFlag(chanPtr->state, BUFFER_READY);
        }
        if (FlushChannel(NULL, chanPtr, 0) != 0) {







>
>
>
>
>







3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
        DiscardInputQueued(chanPtr->state, 0);
        ChanSeek(chanPtr, - inputBuffered, SEEK_CUR, &ignore);
    }
}

static int WillRead(Channel *chanPtr)
{
    if (chanPtr->typePtr == NULL) {
	/* Prevent read attempts on a closed channel */
	Tcl_SetErrno(EINVAL);
	return -1;
    }
    if ((chanPtr->typePtr->seekProc != NULL)
        && (Tcl_OutputBuffered((Tcl_Channel) chanPtr) > 0)) {
        if ((chanPtr->state->curOutPtr != NULL)
            && IsBufferReady(chanPtr->state->curOutPtr)) {
            SetFlag(chanPtr->state, BUFFER_READY);
        }
        if (FlushChannel(NULL, chanPtr, 0) != 0) {
3648
3649
3650
3651
3652
3653
3654

3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667

3668
3669
3670
3671
3672
3673
3674
	     * that we need to stick at the beginning of this buffer.
	     */

	    memcpy(InsertPoint(bufPtr), safe, (size_t) saved);
	    bufPtr->nextAdded += saved;
	    saved = 0;
	}

	dst = InsertPoint(bufPtr);
	dstLen = SpaceLeft(bufPtr);

	result = Tcl_UtfToExternal(NULL, encoding, src, srcLimit,
		statePtr->outputEncodingFlags,
		&statePtr->outputEncodingState, dst,
		dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL);

	/* See chan-io-1.[89]. Tcl Bug 506297. */
	statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;
	
	if ((result != TCL_OK) && (srcRead + dstWrote == 0)) {
	    /* We're reading from invalid/incomplete UTF-8 */

	    if (total == 0) {
		Tcl_SetErrno(EINVAL);
		return -1;
	    }
	    break;
	}








>













>







3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
	     * that we need to stick at the beginning of this buffer.
	     */

	    memcpy(InsertPoint(bufPtr), safe, (size_t) saved);
	    bufPtr->nextAdded += saved;
	    saved = 0;
	}
	PreserveChannelBuffer(bufPtr);
	dst = InsertPoint(bufPtr);
	dstLen = SpaceLeft(bufPtr);

	result = Tcl_UtfToExternal(NULL, encoding, src, srcLimit,
		statePtr->outputEncodingFlags,
		&statePtr->outputEncodingState, dst,
		dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL);

	/* See chan-io-1.[89]. Tcl Bug 506297. */
	statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;
	
	if ((result != TCL_OK) && (srcRead + dstWrote == 0)) {
	    /* We're reading from invalid/incomplete UTF-8 */
	    ReleaseChannelBuffer(bufPtr);
	    if (total == 0) {
		Tcl_SetErrno(EINVAL);
		return -1;
	    }
	    break;
	}

3744
3745
3746
3747
3748
3749
3750

3751
3752
3753
3754
3755
3756
3757
		return -1;
	    }
	    flushed += statePtr->bufSize;
	    if (saved == 0 || src[-1] != '\n') {
		needNlFlush = 0;
	    }
	}

    }
    if ((flushed < total) && (statePtr->flags & CHANNEL_UNBUFFERED ||
	    (needNlFlush && statePtr->flags & CHANNEL_LINEBUFFERED))) {
	SetFlag(statePtr, BUFFER_READY);
	if (FlushChannel(NULL, chanPtr, 0) != 0) {
	    return -1;
	}







>







3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
		return -1;
	    }
	    flushed += statePtr->bufSize;
	    if (saved == 0 || src[-1] != '\n') {
		needNlFlush = 0;
	    }
	}
	ReleaseChannelBuffer(bufPtr);
    }
    if ((flushed < total) && (statePtr->flags & CHANNEL_UNBUFFERED ||
	    (needNlFlush && statePtr->flags & CHANNEL_LINEBUFFERED))) {
	SetFlag(statePtr, BUFFER_READY);
	if (FlushChannel(NULL, chanPtr, 0) != 0) {
	    return -1;
	}
5931
5932
5933
5934
5935
5936
5937
5938
5939
5940
5941
5942
5943
5944
5945

    /*
     * If discardSavedBuffers is nonzero, must also discard any previously
     * saved buffer in the saveInBufPtr field.
     */

    if (discardSavedBuffers && statePtr->saveInBufPtr != NULL) {
	ckfree((char *) statePtr->saveInBufPtr);
	statePtr->saveInBufPtr = NULL;
    }
}

/*
 *---------------------------------------------------------------------------
 *







|







5962
5963
5964
5965
5966
5967
5968
5969
5970
5971
5972
5973
5974
5975
5976

    /*
     * If discardSavedBuffers is nonzero, must also discard any previously
     * saved buffer in the saveInBufPtr field.
     */

    if (discardSavedBuffers && statePtr->saveInBufPtr != NULL) {
	ReleaseChannelBuffer(statePtr->saveInBufPtr);
	statePtr->saveInBufPtr = NULL;
    }
}

/*
 *---------------------------------------------------------------------------
 *
6024
6025
6026
6027
6028
6029
6030
6031
6032
6033
6034
6035
6036
6037
6038
	 * Check the actual buffersize against the requested buffersize.
	 * Buffers which are smaller than requested are squashed. This is done
	 * to honor dynamic changes of the buffersize made by the user.
	 */

	if ((bufPtr != NULL)
		&& (bufPtr->bufLength - BUFFER_PADDING < statePtr->bufSize)) {
	    ckfree((char *) bufPtr);
	    bufPtr = NULL;
	}

	if (bufPtr == NULL) {
	    bufPtr = AllocChannelBuffer(statePtr->bufSize);
	}
	bufPtr->nextPtr = NULL;







|







6055
6056
6057
6058
6059
6060
6061
6062
6063
6064
6065
6066
6067
6068
6069
	 * Check the actual buffersize against the requested buffersize.
	 * Buffers which are smaller than requested are squashed. This is done
	 * to honor dynamic changes of the buffersize made by the user.
	 */

	if ((bufPtr != NULL)
		&& (bufPtr->bufLength - BUFFER_PADDING < statePtr->bufSize)) {
	    ReleaseChannelBuffer(bufPtr);
	    bufPtr = NULL;
	}

	if (bufPtr == NULL) {
	    bufPtr = AllocChannelBuffer(statePtr->bufSize);
	}
	bufPtr->nextPtr = NULL;
6086
6087
6088
6089
6090
6091
6092

6093
6094
6095
6096
6097
6098
6099

6100
6101
6102
6103
6104
6105
6106
	 */

	nread = -1;
	result = EWOULDBLOCK;
    } else {
#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */


	nread = ChanRead(chanPtr, InsertPoint(bufPtr), toRead, &result);

#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
    }
#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */

    if (nread > 0) {

	bufPtr->nextAdded += nread;

	/*
	 * If we get a short read, signal up that we may be BLOCKED. We should
	 * avoid calling the driver because on some platforms we will block in
	 * the low level reading code even though the channel is set into
	 * nonblocking mode.







>







>







6117
6118
6119
6120
6121
6122
6123
6124
6125
6126
6127
6128
6129
6130
6131
6132
6133
6134
6135
6136
6137
6138
6139
	 */

	nread = -1;
	result = EWOULDBLOCK;
    } else {
#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */

	PreserveChannelBuffer(bufPtr);
	nread = ChanRead(chanPtr, InsertPoint(bufPtr), toRead, &result);

#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
    }
#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */

    if (nread > 0) {
	result = 0;
	bufPtr->nextAdded += nread;

	/*
	 * If we get a short read, signal up that we may be BLOCKED. We should
	 * avoid calling the driver because on some platforms we will block in
	 * the low level reading code even though the channel is set into
	 * nonblocking mode.
6118
6119
6120
6121
6122
6123
6124

6125
6126
6127
6128
6129
6130
6131
6132
6133
6134

6135
6136
6137
6138
6139
6140
6141
6142
	     */

	    ResetFlag(statePtr, CHANNEL_HAS_MORE_DATA);
	}
#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */

    } else if (nread == 0) {

	SetFlag(statePtr, CHANNEL_EOF);
	statePtr->inputEncodingFlags |= TCL_ENCODING_END;
    } else if (nread < 0) {
	if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
	    SetFlag(statePtr, CHANNEL_BLOCKED);
	    result = EAGAIN;
	}
	Tcl_SetErrno(result);
	return result;
    }

    return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_Seek --
 *







>








<

>
|







6151
6152
6153
6154
6155
6156
6157
6158
6159
6160
6161
6162
6163
6164
6165
6166

6167
6168
6169
6170
6171
6172
6173
6174
6175
6176
	     */

	    ResetFlag(statePtr, CHANNEL_HAS_MORE_DATA);
	}
#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */

    } else if (nread == 0) {
	result = 0;
	SetFlag(statePtr, CHANNEL_EOF);
	statePtr->inputEncodingFlags |= TCL_ENCODING_END;
    } else if (nread < 0) {
	if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
	    SetFlag(statePtr, CHANNEL_BLOCKED);
	    result = EAGAIN;
	}
	Tcl_SetErrno(result);

    }
    ReleaseChannelBuffer(bufPtr);
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_Seek --
 *

Changes to generic/tclIO.h.

32
33
34
35
36
37
38

39
40
41
42
43
44
45
/*
 * struct ChannelBuffer:
 *
 * Buffers data being sent to or from a channel.
 */

typedef struct ChannelBuffer {

    int nextAdded;		/* The next position into which a character
				 * will be put in the buffer. */
    int nextRemoved;		/* Position of next byte to be removed from
				 * the buffer. */
    int bufLength;		/* How big is the buffer? */
    struct ChannelBuffer *nextPtr;
    				/* Next buffer in chain. */







>







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/*
 * struct ChannelBuffer:
 *
 * Buffers data being sent to or from a channel.
 */

typedef struct ChannelBuffer {
    int refCount;		/* Current uses count */
    int nextAdded;		/* The next position into which a character
				 * will be put in the buffer. */
    int nextRemoved;		/* Position of next byte to be removed from
				 * the buffer. */
    int bufLength;		/* How big is the buffer? */
    struct ChannelBuffer *nextPtr;
    				/* Next buffer in chain. */

Changes to generic/tclIOCmd.c.

443
444
445
446
447
448
449

450
451
452
453
454
455
456
457
458
459
460
461
462
463
464

465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482

483
484
485
486
487
488
489
	    Tcl_SetErrorCode(interp, "TCL", "VALUE", "NUMBER", NULL);
	    return TCL_ERROR;
	}
    }

    resultPtr = Tcl_NewObj();
    Tcl_IncrRefCount(resultPtr);

    charactersRead = Tcl_ReadChars(chan, resultPtr, toRead, 0);
    if (charactersRead < 0) {
	/*
	 * TIP #219.
	 * Capture error messages put by the driver into the bypass area and
	 * put them into the regular interpreter result. Fall back to the
	 * regular message if nothing was found in the bypass.
	 */

	if (!TclChanCaughtErrorBypass(interp, chan)) {
	    Tcl_ResetResult(interp);
	    Tcl_AppendResult(interp, "error reading \"",
		    TclGetString(chanObjPtr), "\": ",
		    Tcl_PosixError(interp), NULL);
	}

	Tcl_DecrRefCount(resultPtr);
	return TCL_ERROR;
    }

    /*
     * If requested, remove the last newline in the channel if at EOF.
     */

    if ((charactersRead > 0) && (newline != 0)) {
	char *result;
	int length;

	result = TclGetStringFromObj(resultPtr, &length);
	if (result[length - 1] == '\n') {
	    Tcl_SetObjLength(resultPtr, length - 1);
	}
    }
    Tcl_SetObjResult(interp, resultPtr);

    Tcl_DecrRefCount(resultPtr);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *







>















>


















>







443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
	    Tcl_SetErrorCode(interp, "TCL", "VALUE", "NUMBER", NULL);
	    return TCL_ERROR;
	}
    }

    resultPtr = Tcl_NewObj();
    Tcl_IncrRefCount(resultPtr);
    Tcl_Preserve(chan);
    charactersRead = Tcl_ReadChars(chan, resultPtr, toRead, 0);
    if (charactersRead < 0) {
	/*
	 * TIP #219.
	 * Capture error messages put by the driver into the bypass area and
	 * put them into the regular interpreter result. Fall back to the
	 * regular message if nothing was found in the bypass.
	 */

	if (!TclChanCaughtErrorBypass(interp, chan)) {
	    Tcl_ResetResult(interp);
	    Tcl_AppendResult(interp, "error reading \"",
		    TclGetString(chanObjPtr), "\": ",
		    Tcl_PosixError(interp), NULL);
	}
	Tcl_Release(chan);
	Tcl_DecrRefCount(resultPtr);
	return TCL_ERROR;
    }

    /*
     * If requested, remove the last newline in the channel if at EOF.
     */

    if ((charactersRead > 0) && (newline != 0)) {
	char *result;
	int length;

	result = TclGetStringFromObj(resultPtr, &length);
	if (result[length - 1] == '\n') {
	    Tcl_SetObjLength(resultPtr, length - 1);
	}
    }
    Tcl_SetObjResult(interp, resultPtr);
    Tcl_Release(chan);
    Tcl_DecrRefCount(resultPtr);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *

Changes to tests/ioCmd.test.

781
782
783
784
785
786
787
788
789
790
791
792
















793
794
795
796
797
798
799
	} finalize {} watch {} read {
	    close $chan
	    return a
	}
    }
    set ch [chan create read foo]
} -body {
    read $ch 1
} -cleanup {
    close $ch
    rename foo {}
} -result a

















# --- --- --- --------- --------- ---------
# Helper commands to record the arguments to handler methods.

# Stored in a script so that the threads and interpreters needing this
# code do not need their own copy but can access this variable.








|



|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
	} finalize {} watch {} read {
	    close $chan
	    return a
	}
    }
    set ch [chan create read foo]
} -body {
    read $ch 0
} -cleanup {
    close $ch
    rename foo {}
} -result {}
test iocmd-21.22 {[close] in [read] segfaults} -setup {
    proc foo {method chan args} {
	switch -- $method initialize {
	    return {initialize finalize watch read}
	} finalize {} watch {} read {
	    catch {close $chan}
	    return a
	}
    }
    set ch [chan create read foo]
} -body {
    read $ch 1
} -returnCodes error -cleanup {
    catch {close $ch}
    rename foo {}
} -match glob -result {*invalid argument*}

# --- --- --- --------- --------- ---------
# Helper commands to record the arguments to handler methods.

# Stored in a script so that the threads and interpreters needing this
# code do not need their own copy but can access this variable.