summaryrefslogtreecommitdiffstats
path: root/generic/tclIO.c
diff options
context:
space:
mode:
authorsebres <sebres@users.sourceforge.net>2024-05-21 10:33:56 (GMT)
committersebres <sebres@users.sourceforge.net>2024-05-21 10:33:56 (GMT)
commitc40badc1d3027d5db084ff9970bf3dc5377c2f0e (patch)
tree816d415cc650bea382fd9210fc280b4460ad4c2e /generic/tclIO.c
parentd2433e65fa461c3df0432993584aa77913874c2d (diff)
parent246de88c7debb246492a55e1fc3bfe8f7768a434 (diff)
downloadtcl-c40badc1d3027d5db084ff9970bf3dc5377c2f0e.zip
tcl-c40badc1d3027d5db084ff9970bf3dc5377c2f0e.tar.gz
tcl-c40badc1d3027d5db084ff9970bf3dc5377c2f0e.tar.bz2
merge 8.7
Diffstat (limited to 'generic/tclIO.c')
-rw-r--r--generic/tclIO.c111
1 files changed, 89 insertions, 22 deletions
diff --git a/generic/tclIO.c b/generic/tclIO.c
index eec6062..8f3f728 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. */
@@ -222,6 +223,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);
@@ -2087,7 +2089,7 @@ Tcl_UnstackChannel(
return TCL_ERROR;
}
- statePtr->csPtrR = csPtrR;
+ statePtr->csPtrR = csPtrR;
statePtr->csPtrW = csPtrW;
}
@@ -3017,6 +3019,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);
+}
+
/*
*----------------------------------------------------------------------
*
@@ -3181,7 +3211,7 @@ CloseChannel(
ChannelFree(chanPtr);
- Tcl_EventuallyFree(statePtr, TCL_DYNAMIC);
+ Tcl_EventuallyFree(statePtr, FreeChannelState);
return errorCode;
}
@@ -3989,8 +4019,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
@@ -9390,6 +9426,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;
@@ -9400,7 +9437,10 @@ TclCopyChannel(
}
csPtr->cmdPtr = cmdPtr;
- inStatePtr->csPtrR = csPtr;
+ TclChannelPreserve(inChan);
+ TclChannelPreserve(outChan);
+
+ inStatePtr->csPtrR = csPtr;
outStatePtr->csPtrW = csPtr;
if (moveBytes) {
@@ -9414,7 +9454,7 @@ TclCopyChannel(
if ((nonBlocking == CHANNEL_NONBLOCKING) && (toRead == 0)) {
Tcl_CreateTimerHandler(0, ZeroTransferTimerProc, csPtr);
- return 0;
+ return TCL_OK;
}
/*
@@ -9689,6 +9729,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;
@@ -9833,7 +9875,7 @@ CopyData(
TclDecrRefCount(bufObj);
bufObj = NULL;
}
- return TCL_OK;
+ goto done;
}
}
@@ -9919,7 +9961,7 @@ CopyData(
TclDecrRefCount(bufObj);
bufObj = NULL;
}
- return TCL_OK;
+ goto done;
}
/*
@@ -9941,7 +9983,7 @@ CopyData(
TclDecrRefCount(bufObj);
bufObj = NULL;
}
- return TCL_OK;
+ goto done;
}
} /* while */
@@ -9993,6 +10035,9 @@ CopyData(
}
}
}
+
+ done:
+ CopyDecrRefCount(csPtr);
return result;
}
@@ -10102,8 +10147,6 @@ DoRead(
code = GetInput(chanPtr);
bufPtr = statePtr->inQueueHead;
- assert(bufPtr != NULL);
-
if (GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED)) {
/*
* Further reads cannot do any more.
@@ -10112,20 +10155,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;
@@ -10367,9 +10411,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);
}
- inStatePtr->csPtrR = NULL;
- outStatePtr->csPtrW = NULL;
+}
+
+static void
+CopyDecrRefCount(
+ CopyState *csPtr)
+{
+ if (csPtr->refCount-- > 1) {
+ return;
+ }
+
+ TclChannelRelease((Tcl_Channel)csPtr->readPtr);
+ TclChannelRelease((Tcl_Channel)csPtr->writePtr);
+
Tcl_Free(csPtr);
}