diff options
Diffstat (limited to 'generic/tclIO.c')
| -rw-r--r-- | generic/tclIO.c | 133 |
1 files changed, 96 insertions, 37 deletions
diff --git a/generic/tclIO.c b/generic/tclIO.c index 518adef..2d8b945 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -97,6 +97,7 @@ typedef struct GetsState { typedef struct CopyState { struct Channel *readPtr; /* Pointer to input channel. */ struct Channel *writePtr; /* Pointer to output channel. */ + int refCount; /* Reference counter. */ int readFlags; /* Original read channel flags. */ int writeFlags; /* Original write channel flags. */ Tcl_WideInt toRead; /* Number of bytes to copy, or -1. */ @@ -221,6 +222,7 @@ static int StackSetBlockMode(Channel *chanPtr, int mode); static int SetBlockMode(Tcl_Interp *interp, Channel *chanPtr, int mode); static void StopCopy(CopyState *csPtr); +static void CopyDecrRefCount(CopyState *csPtr); static void TranslateInputEOL(ChannelState *statePtr, char *dst, const char *src, int *dstLenPtr, int *srcLenPtr); static void UpdateInterest(Channel *chanPtr); @@ -2086,7 +2088,7 @@ Tcl_UnstackChannel( return TCL_ERROR; } - statePtr->csPtrR = csPtrR; + statePtr->csPtrR = csPtrR; statePtr->csPtrW = csPtrW; } @@ -3016,6 +3018,34 @@ FlushChannel( return errorCode; } +static void +FreeChannelState( + void *blockPtr) /* Channel state to free. */ +{ + ChannelState *statePtr = (ChannelState *)blockPtr; + /* + * Even after close some members can be filled again (in events etc). + * Test in bug [79474c588] illustrates one leak (on remaining chanMsg). + * Possible other fields need freeing on some constellations. + */ + + DiscardInputQueued(statePtr, 1); + if (statePtr->curOutPtr != NULL) { + ReleaseChannelBuffer(statePtr->curOutPtr); + } + DiscardOutputQueued(statePtr); + + DeleteTimerHandler(statePtr); + + if (statePtr->chanMsg) { + Tcl_DecrRefCount(statePtr->chanMsg); + } + if (statePtr->unreportedMsg) { + Tcl_DecrRefCount(statePtr->unreportedMsg); + } + Tcl_Free(statePtr); +} + /* *---------------------------------------------------------------------- * @@ -3180,7 +3210,7 @@ CloseChannel( ChannelFree(chanPtr); - Tcl_EventuallyFree(statePtr, TCL_DYNAMIC); + Tcl_EventuallyFree(statePtr, FreeChannelState); return errorCode; } @@ -3983,8 +4013,14 @@ Tcl_ClearChannelHandlers( * Cancel any pending copy operation. */ - StopCopy(statePtr->csPtrR); - StopCopy(statePtr->csPtrW); + if (statePtr->csPtrR) { + StopCopy(statePtr->csPtrR); + statePtr->csPtrR = NULL; + } + if (statePtr->csPtrW) { + StopCopy(statePtr->csPtrW); + statePtr->csPtrW = NULL; + } /* * Must set the interest mask now to 0, otherwise infinite loops will @@ -6243,7 +6279,7 @@ ReadChars( if (dstLimit <= 0) { dstLimit = INT_MAX; /* avoid overflow */ } - (void) TclGetStringFromObj(objPtr, &numBytes); + (void)TclGetStringFromObj(objPtr, &numBytes); TclAppendUtfToUtf(objPtr, NULL, dstLimit); if (toRead == srcLen) { Tcl_Size size; @@ -8756,8 +8792,7 @@ ChannelTimerProc( static void DeleteTimerHandler( - ChannelState *statePtr -) + ChannelState *statePtr) { if (statePtr->timer != NULL) { Tcl_DeleteTimerHandler(statePtr->timer); @@ -9344,6 +9379,7 @@ TclCopyChannel( csPtr->bufSize = !moveBytes * inStatePtr->bufSize; csPtr->readPtr = inPtr; csPtr->writePtr = outPtr; + csPtr->refCount = 2; /* two references below (inStatePtr, outStatePtr) */ csPtr->readFlags = readFlags; csPtr->writeFlags = writeFlags; csPtr->toRead = toRead; @@ -9354,7 +9390,10 @@ TclCopyChannel( } csPtr->cmdPtr = cmdPtr; - inStatePtr->csPtrR = csPtr; + TclChannelPreserve(inChan); + TclChannelPreserve(outChan); + + inStatePtr->csPtrR = csPtr; outStatePtr->csPtrW = csPtr; if (moveBytes) { @@ -9368,7 +9407,7 @@ TclCopyChannel( if ((nonBlocking == CHANNEL_NONBLOCKING) && (toRead == 0)) { Tcl_CreateTimerHandler(0, ZeroTransferTimerProc, csPtr); - return 0; + return TCL_OK; } /* @@ -9643,6 +9682,8 @@ CopyData( int moveBytes; int underflow; /* Input underflow */ + csPtr->refCount++; /* avoid freeing during handling */ + inChan = (Tcl_Channel) csPtr->readPtr; outChan = (Tcl_Channel) csPtr->writePtr; inStatePtr = csPtr->readPtr->state; @@ -9787,7 +9828,7 @@ CopyData( TclDecrRefCount(bufObj); bufObj = NULL; } - return TCL_OK; + goto done; } } @@ -9873,7 +9914,7 @@ CopyData( TclDecrRefCount(bufObj); bufObj = NULL; } - return TCL_OK; + goto done; } /* @@ -9895,7 +9936,7 @@ CopyData( TclDecrRefCount(bufObj); bufObj = NULL; } - return TCL_OK; + goto done; } } /* while */ @@ -9947,6 +9988,9 @@ CopyData( } } } + + done: + CopyDecrRefCount(csPtr); return result; } @@ -10056,8 +10100,6 @@ DoRead( code = GetInput(chanPtr); bufPtr = statePtr->inQueueHead; - assert(bufPtr != NULL); - if (GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED)) { /* * Further reads cannot do any more. @@ -10066,20 +10108,21 @@ DoRead( break; } - if (code) { - /* - * Read error - */ - - UpdateInterest(chanPtr); - TclChannelRelease((Tcl_Channel)chanPtr); - return -1; + if (code || !bufPtr) { + /* Read error (or channel dead/closed) */ + goto readErr; } assert(IsBufferFull(bufPtr)); } - assert(bufPtr != NULL); + if (!bufPtr) { + readErr: + + UpdateInterest(chanPtr); + TclChannelRelease((Tcl_Channel)chanPtr); + return -1; + } bytesRead = BytesLeft(bufPtr); bytesWritten = bytesToRead; @@ -10249,20 +10292,13 @@ Lossless( return inStatePtr->inEofChar == '\0' /* No eofChar to stop input */ && inStatePtr->inputTranslation == TCL_TRANSLATE_LF && outStatePtr->outputTranslation == TCL_TRANSLATE_LF - && ( - ( - inStatePtr->encoding == GetBinaryEncoding() - && - outStatePtr->encoding == GetBinaryEncoding() - ) - || - ( - toRead == -1 + && ((inStatePtr->encoding == GetBinaryEncoding() + && outStatePtr->encoding == GetBinaryEncoding()) + || (toRead == -1 && inStatePtr->encoding == outStatePtr->encoding && ENCODING_PROFILE_GET(inStatePtr->inputEncodingFlags) == TCL_ENCODING_PROFILE_TCL8 && ENCODING_PROFILE_GET(outStatePtr->inputEncodingFlags) == TCL_ENCODING_PROFILE_TCL8 - ) - ); + )); } /* @@ -10328,9 +10364,32 @@ StopCopy( Tcl_DeleteChannelHandler(inChan, MBEvent, csPtr); Tcl_DeleteChannelHandler(outChan, MBEvent, csPtr); TclDecrRefCount(csPtr->cmdPtr); + csPtr->cmdPtr = NULL; + } + + if (inStatePtr->csPtrR) { + assert(inStatePtr->csPtrR == csPtr); + inStatePtr->csPtrR = NULL; + CopyDecrRefCount(csPtr); + } + if (outStatePtr->csPtrW) { + assert(outStatePtr->csPtrW == csPtr); + outStatePtr->csPtrW = NULL; + CopyDecrRefCount(csPtr); + } +} + +static void +CopyDecrRefCount( + CopyState *csPtr) +{ + if (csPtr->refCount-- > 1) { + return; } - inStatePtr->csPtrR = NULL; - outStatePtr->csPtrW = NULL; + + TclChannelRelease((Tcl_Channel)csPtr->readPtr); + TclChannelRelease((Tcl_Channel)csPtr->writePtr); + Tcl_Free(csPtr); } @@ -11080,7 +11139,7 @@ Tcl_SetChannelError( Tcl_Channel chan, /* Channel to store the data into. */ Tcl_Obj *msg) /* Error message to store. */ { - ChannelState *statePtr = ((Channel *) chan)->state; + ChannelState *statePtr = ((Channel *)chan)->state; Tcl_Obj *disposePtr = statePtr->chanMsg; if (msg != NULL) { |
