Tcl Source Code

Artifact [8e7b978df8]
Login

Artifact 8e7b978df8429ac33b9b5bfbc9bc2d3815fafef5:

Attachment "ioleak.patch" to ticket [1710825fff] added by kennykb 2007-05-02 04:18:02.
? darius
Index: generic/tclIO.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclIO.c,v
retrieving revision 1.119
diff -u -u -r1.119 tclIO.c
--- generic/tclIO.c	30 Apr 2007 21:40:24 -0000	1.119
+++ generic/tclIO.c	1 May 2007 21:17:10 -0000
@@ -248,87 +248,98 @@
 {
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
     Channel *chanPtr;		/* Iterates over open channels. */
-    ChannelState *nextCSPtr;	/* Iterates over open channels. */
     ChannelState *statePtr;	/* State of channel stack */
+    int active = 1;		/* Flag == 1 while there's still work to do */
 
     /*
      * Walk all channel state structures known to this thread and
      * close corresponding channels.
      */
 
-    for (statePtr = tsdPtr->firstCSPtr; statePtr != NULL;
-	 statePtr = nextCSPtr) {
-	chanPtr = statePtr->topChanPtr;
+    while (active) {
 
 	/*
-	 * Set the channel back into blocking mode to ensure that we wait for
-	 * all data to flush out.
-	 */
-
-	(void) Tcl_SetChannelOption(NULL, (Tcl_Channel) chanPtr,
-		"-blocking", "on");
-
-	if ((chanPtr == (Channel *) tsdPtr->stdinChannel) ||
-		(chanPtr == (Channel *) tsdPtr->stdoutChannel) ||
-		(chanPtr == (Channel *) tsdPtr->stderrChannel)) {
-	    /*
-	     * Decrement the refcount which was earlier artificially bumped up
-	     * to keep the channel from being closed.
-	     */
-
-	    statePtr->refCount--;
+	 * Iterate through the open channel list, and find the first
+	 * channel that isn't dead. We start from the head of the list
+	 * each time, because the close action on one channel can close
+	 * others.
+	 */
+
+	active = 0;
+	for (statePtr = tsdPtr->firstCSPtr;
+	     statePtr != NULL;
+	     statePtr = statePtr->nextCSPtr) {
+	    chanPtr = statePtr->topChanPtr;
+	    if (!(statePtr->flags & CHANNEL_DEAD)) {
+		active = 1;
+		break;
+	    }
 	}
 
 	/*
-	 * Preserve statePtr from disappearing until we can get the
-	 * nextCSPtr below.
+	 * We've found a live channel.  Close it.
 	 */
 
-	Tcl_Preserve(statePtr);
-	if (statePtr->refCount <= 0) {
-	    /*
-	     * Close it only if the refcount indicates that the channel is not
-	     * referenced from any interpreter. If it is, that interpreter
-	     * will close the channel when it gets destroyed.
-	     */
+	if (active) {
 
-	    (void) Tcl_Close(NULL, (Tcl_Channel) chanPtr);
-	} else {
 	    /*
-	     * The refcount is greater than zero, so flush the channel.
+	     * Set the channel back into blocking mode to ensure that we 
+	     * wait for all data to flush out.
 	     */
-
-	    Tcl_Flush((Tcl_Channel) chanPtr);
-
-	    /*
-	     * Call the device driver to actually close the underlying device
-	     * for this channel.
-	     */
-
-	    if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) {
-		(chanPtr->typePtr->closeProc)(chanPtr->instanceData, NULL);
+	    
+	    (void) Tcl_SetChannelOption(NULL, (Tcl_Channel) chanPtr,
+					"-blocking", "on");
+	    
+	    if ((chanPtr == (Channel *) tsdPtr->stdinChannel) ||
+		(chanPtr == (Channel *) tsdPtr->stdoutChannel) ||
+		(chanPtr == (Channel *) tsdPtr->stderrChannel)) {
+		/*
+		 * Decrement the refcount which was earlier artificially 
+		 * bumped up to keep the channel from being closed.
+		 */
+		
+		statePtr->refCount--;
+	    }
+	    
+	    if (statePtr->refCount <= 0) {
+		/*
+		 * Close it only if the refcount indicates that the channel 
+		 * is not referenced from any interpreter. If it is, that
+		 * interpreter will close the channel when it gets destroyed.
+		 */
+		
+		(void) Tcl_Close(NULL, (Tcl_Channel) chanPtr);
 	    } else {
-		(chanPtr->typePtr->close2Proc)(chanPtr->instanceData, NULL, 0);
+		/*
+		 * The refcount is greater than zero, so flush the channel.
+		 */
+		
+		Tcl_Flush((Tcl_Channel) chanPtr);
+		
+		/*
+		 * Call the device driver to actually close the underlying 
+		 * device for this channel.
+		 */
+		
+		if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) {
+		    (chanPtr->typePtr->closeProc)(chanPtr->instanceData, NULL);
+		} else {
+		    (chanPtr->typePtr->close2Proc)(chanPtr->instanceData,
+						   NULL, 0);
+		}
+		
+		/*
+		 * Finally, we clean up the fields in the channel data 
+		 * structure since all of them have been deleted already. 
+		 * We mark the channel with CHANNEL_DEAD to prevent any 
+		 * further IO operations
+		 * on it.
+		 */
+		
+		chanPtr->instanceData = NULL;
+		statePtr->flags |= CHANNEL_DEAD;
 	    }
-
-	    /*
-	     * Finally, we clean up the fields in the channel data structure
-	     * since all of them have been deleted already. We mark the
-	     * channel with CHANNEL_DEAD to prevent any further IO operations
-	     * on it.
-	     */
-
-	    chanPtr->instanceData = NULL;
-	    statePtr->flags |= CHANNEL_DEAD;
 	}
-
-	/*
-	 * We look for the next pointer now in case we had one closed on up
-	 * during the current channel's closeproc (eg: rechan extension)
-	 */
-
-	nextCSPtr = statePtr->nextCSPtr;
-	Tcl_Release(statePtr);
     }
 
     TclpFinalizeSockets();
@@ -665,13 +676,14 @@
 	 * the behavior of Tcl_UnregisterChannel directly here.
 	 */
 
-	Tcl_DeleteHashEntry(hPtr);
 	statePtr->refCount--;
 	if (statePtr->refCount <= 0) {
 	    if (!(statePtr->flags & BG_FLUSH_SCHEDULED)) {
 		(void) Tcl_Close(interp, (Tcl_Channel) chanPtr);
 	    }
 	}
+	Tcl_DeleteHashEntry(hPtr);
+
     }
     Tcl_DeleteHashTable(hTblPtr);
     ckfree((char *) hTblPtr);
Index: generic/tclThread.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclThread.c,v
retrieving revision 1.15
diff -u -u -r1.15 tclThread.c
--- generic/tclThread.c	7 Nov 2006 14:26:26 -0000	1.15
+++ generic/tclThread.c	1 May 2007 21:17:10 -0000
@@ -357,32 +357,34 @@
 void
 TclFinalizeSynchronization(void)
 {
-#ifdef TCL_THREADS
+    int i;
     void *blockPtr;
     Tcl_ThreadDataKey *keyPtr;
+#ifdef TCL_THREADS
     Tcl_Mutex *mutexPtr;
     Tcl_Condition *condPtr;
-    int i;
 
     TclpMasterLock();
+#endif
 
     /*
      * If we're running unthreaded, the TSD blocks are simply stored inside
      * their thread data keys. Free them here.
      */
 
-    for (i=0 ; i<keyRecord.num ; i++) {
-	keyPtr = (Tcl_ThreadDataKey *) keyRecord.list[i];
-	blockPtr = (void *) *keyPtr;
-	ckfree(blockPtr);
-    }
     if (keyRecord.list != NULL) {
+	for (i=0 ; i<keyRecord.num ; i++) {
+	    keyPtr = (Tcl_ThreadDataKey *) keyRecord.list[i];
+	    blockPtr = (void *) *keyPtr;
+	    ckfree(blockPtr);
+	}
 	ckfree((char *) keyRecord.list);
 	keyRecord.list = NULL;
     }
     keyRecord.max = 0;
     keyRecord.num = 0;
-
+    
+#ifdef TCL_THREADS
     /*
      * Call thread storage master cleanup.
      */
@@ -416,13 +418,6 @@
     condRecord.num = 0;
 
     TclpMasterUnlock();
-#else /* TCL_THREADS */
-    if (keyRecord.list != NULL) {
-	ckfree((char *) keyRecord.list);
-	keyRecord.list = NULL;
-    }
-    keyRecord.max = 0;
-    keyRecord.num = 0;
 #endif /* TCL_THREADS */
 }