--- tcl86.orig/ChangeLog	2010-03-30 11:12:22.000000000 -0800
+++ tcl86/ChangeLog	2010-03-30 11:35:35.000000000 -0800
@@ -1,3 +1,12 @@
+2010-03-30  Andreas Kupries  <andreask@activestate.com>
+
+	* generic/tclIORChan.c (ReflectClose, ReflectInput, ReflectOutput,
+	ReflectSeekWide, ReflectWatch, ReflectBlock, ReflectSetOption,
+	ReflectGetOption, ForwardProc): [Bug 2978773]: Preserve
+	ReflectedChannel* structures across handler invokations, to avoid
+	crashes when the handler implementation induces nested callbacks
+	and destruction of the channel deep inside such a nesting.
+
 2010-03-30  Don Porter  <dgp@users.sourceforge.net>
 
 	* generic/tclObj.c (Tcl_GetCommandFromObj):     [Bug 2979402]: Reorder
--- tcl86.orig/generic/tclIORChan.c	2010-03-30 11:08:53.000000000 -0800
+++ tcl86/generic/tclIORChan.c	2010-03-30 11:53:23.000000000 -0800
@@ -1086,7 +1086,7 @@
 	}
 #endif
 
-	FreeReflectedChannel(rcPtr);
+        Tcl_EventuallyFree (rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
 	return EOK;
     }
 
@@ -1098,7 +1098,7 @@
      */
 
     if (rcPtr->methods == 0) {
-	FreeReflectedChannel(rcPtr);
+        Tcl_EventuallyFree (rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
 	return EOK;
     }
 
@@ -1161,7 +1161,7 @@
 	}
 #endif
 
-	FreeReflectedChannel(rcPtr);
+        Tcl_EventuallyFree (rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
 #ifdef TCL_THREADS
     }
 #endif
@@ -1242,29 +1242,26 @@
     /* ASSERT: rcPtr->method & FLAG(METH_READ) */
     /* ASSERT: rcPtr->mode & TCL_READABLE */
 
+    Tcl_Preserve(rcPtr);
+
     toReadObj = Tcl_NewIntObj(toRead);
     if (InvokeTclMethod(rcPtr, "read", toReadObj, NULL, &resObj)!=TCL_OK) {
 	int code = ErrnoReturn(rcPtr, resObj);
 
 	if (code < 0) {
-	    Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
 	    *errorCodePtr = -code;
-	    return -1;
+            goto error;
 	}
 
 	Tcl_SetChannelError(rcPtr->chan, resObj);
-	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
-	*errorCodePtr = EINVAL;
-	return -1;
+        goto invalid;
     }
 
     bytev = Tcl_GetByteArrayFromObj(resObj, &bytec);
 
     if (toRead < bytec) {
-	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
 	SetChannelErrorStr(rcPtr->chan, msg_read_toomuch);
-	*errorCodePtr = EINVAL;
-	return -1;
+        goto invalid;
     }
 
     *errorCodePtr = EOK;
@@ -1273,8 +1270,15 @@
 	memcpy(buf, bytev, (size_t)bytec);
     }
 
+ stop:
     Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */
+    Tcl_Release(rcPtr);
     return bytec;
+ invalid:
+    *errorCodePtr = EINVAL;
+ error:
+    bytec = -1;
+    goto stop;
 }
 
 /*
@@ -1350,31 +1354,26 @@
     /* ASSERT: rcPtr->method & FLAG(METH_WRITE) */
     /* ASSERT: rcPtr->mode & TCL_WRITABLE */
 
+    Tcl_Preserve(rcPtr);
+
     bufObj = Tcl_NewByteArrayObj((unsigned char *) buf, toWrite);
     if (InvokeTclMethod(rcPtr, "write", bufObj, NULL, &resObj) != TCL_OK) {
 	int code = ErrnoReturn(rcPtr, resObj);
 
 	if (code < 0) {
-	    Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
 	    *errorCodePtr = -code;
-	    return -1;
+            goto error;
 	}
 
 	Tcl_SetChannelError(rcPtr->chan, resObj);
-	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
-	*errorCodePtr = EINVAL;
-	return -1;
+        goto invalid;
     }
 
     if (Tcl_GetIntFromObj(rcPtr->interp, resObj, &written) != TCL_OK) {
-	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
 	Tcl_SetChannelError(rcPtr->chan, MarshallError(rcPtr->interp));
-	*errorCodePtr = EINVAL;
-	return -1;
+        goto invalid;
     }
 
-    Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */
-
     if ((written == 0) && (toWrite > 0)) {
 	/*
 	 * The handler claims to have written nothing of what it was
@@ -1382,8 +1381,7 @@
 	 */
 
 	SetChannelErrorStr(rcPtr->chan, msg_write_nothing);
-	*errorCodePtr = EINVAL;
-	return -1;
+        goto invalid;
     }
     if (toWrite < written) {
 	/*
@@ -1393,12 +1391,19 @@
 	 */
 
 	SetChannelErrorStr(rcPtr->chan, msg_write_toomuch);
-	*errorCodePtr = EINVAL;
-	return -1;
+        goto invalid;
     }
 
     *errorCodePtr = EOK;
+ stop:
+    Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */
+    Tcl_Release(rcPtr);
     return written;
+ invalid:
+    *errorCodePtr = EINVAL;
+ error:
+    written = -1;
+    goto stop;
 }
 
 /*
@@ -1456,33 +1461,35 @@
 
     /* ASSERT: rcPtr->method & FLAG(METH_SEEK) */
 
+    Tcl_Preserve(rcPtr);
+
     offObj = Tcl_NewWideIntObj(offset);
     baseObj = Tcl_NewStringObj((seekMode == SEEK_SET) ? "start" :
 	    ((seekMode == SEEK_CUR) ? "current" : "end"), -1);
     if (InvokeTclMethod(rcPtr, "seek", offObj, baseObj, &resObj)!=TCL_OK) {
 	Tcl_SetChannelError(rcPtr->chan, resObj);
-	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
-	*errorCodePtr = EINVAL;
-	return -1;
+        goto invalid;
     }
 
     if (Tcl_GetWideIntFromObj(rcPtr->interp, resObj, &newLoc) != TCL_OK) {
-	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
 	Tcl_SetChannelError(rcPtr->chan, MarshallError(rcPtr->interp));
-	*errorCodePtr = EINVAL;
-	return -1;
+        goto invalid;
     }
 
-    Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */
-
     if (newLoc < Tcl_LongAsWide(0)) {
 	SetChannelErrorStr(rcPtr->chan, msg_seek_beforestart);
-	*errorCodePtr = EINVAL;
-	return -1;
+        goto invalid;
     }
 
     *errorCodePtr = EOK;
+ stop:
+    Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */
+    Tcl_Release(rcPtr);
     return newLoc;
+ invalid:
+    *errorCodePtr = EINVAL;
+    newLoc = -1;
+    goto stop;
 }
 
 static int
@@ -1568,9 +1575,13 @@
     }
 #endif
 
+    Tcl_Preserve(rcPtr);
+
     maskObj = DecodeEventMask(mask);
     (void) InvokeTclMethod(rcPtr, "watch", maskObj, NULL, NULL);
     Tcl_DecrRefCount(maskObj);
+
+    Tcl_Release(rcPtr);
 }
 
 /*
@@ -1623,6 +1634,8 @@
 
     blockObj = Tcl_NewBooleanObj(!nonblocking);
 
+    Tcl_Preserve(rcPtr);
+
     if (InvokeTclMethod(rcPtr, "blocking", blockObj, NULL, &resObj)!=TCL_OK) {
 	Tcl_SetChannelError(rcPtr->chan, resObj);
 	errorNum = EINVAL;
@@ -1631,6 +1644,8 @@
     }
 
     Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */
+
+    Tcl_Release(rcPtr);
     return errorNum;
 }
 
@@ -1686,6 +1701,7 @@
 	return p.base.code;
     }
 #endif
+    Tcl_Preserve(rcPtr);
 
     optionObj = Tcl_NewStringObj(optionName, -1);
     valueObj = Tcl_NewStringObj(newValue, -1);
@@ -1695,6 +1711,7 @@
     }
 
     Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */
+    Tcl_Release(rcPtr);
     return result;
 }
 
@@ -1781,10 +1798,11 @@
 	optionObj = Tcl_NewStringObj(optionName, -1);
     }
 
+    Tcl_Preserve(rcPtr);
+
     if (InvokeTclMethod(rcPtr, method, optionObj, NULL, &resObj)!=TCL_OK) {
 	UnmarshallErrorResult(interp, resObj);
-	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
-	return TCL_ERROR;
+        goto error;
     }
 
     /*
@@ -1794,8 +1812,7 @@
 
     if (optionObj != NULL) {
 	Tcl_DStringAppend(dsPtr, TclGetString(resObj), -1);
-	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
-	return TCL_OK;
+        goto ok;
     }
 
     /*
@@ -1810,8 +1827,7 @@
      */
 
     if (Tcl_ListObjGetElements(interp, resObj, &listc, &listv) != TCL_OK) {
-	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
-	return TCL_ERROR;
+        goto error;
     }
 
     if ((listc % 2) == 1) {
@@ -1824,8 +1840,7 @@
 		"Expected list with even number of "
 		"elements, got %d element%s instead", listc,
 		(listc == 1 ? "" : "s")));
-	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
-	return TCL_ERROR;
+        goto error;
     } else {
 	int len;
 	const char *str = Tcl_GetStringFromObj(resObj, &len);
@@ -1834,9 +1849,17 @@
 	    Tcl_DStringAppend(dsPtr, " ", 1);
 	    Tcl_DStringAppend(dsPtr, str, len);
 	}
+        goto ok;
+    }
+
+ ok:
 	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
+    Tcl_Release(rcPtr);
 	return TCL_OK;
-    }
+ error:
+    Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
+    Tcl_Release(rcPtr);
+    return TCL_ERROR;
 }
 
 /*
@@ -2827,12 +2850,13 @@
 		Tcl_GetChannelName(rcPtr->chan));
 	Tcl_DeleteHashEntry(hPtr);
 
-	FreeReflectedChannel(rcPtr);
+        Tcl_EventuallyFree (rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
 	break;
 
     case ForwardedInput: {
 	Tcl_Obj *toReadObj = Tcl_NewIntObj(paramPtr->input.toRead);
 
+        Tcl_Preserve(rcPtr);
 	if (InvokeTclMethod(rcPtr, "read", toReadObj, NULL, &resObj)!=TCL_OK){
 	    int code = ErrnoReturn(rcPtr, resObj);
 
@@ -2862,6 +2886,7 @@
 		paramPtr->input.toRead = bytec;
 	    }
 	}
+        Tcl_Release(rcPtr);
 	break;
     }
 
@@ -2869,6 +2894,7 @@
 	Tcl_Obj *bufObj = Tcl_NewByteArrayObj((unsigned char *)
 		paramPtr->output.buf, paramPtr->output.toWrite);
 
+        Tcl_Preserve(rcPtr);
 	if (InvokeTclMethod(rcPtr, "write", bufObj, NULL, &resObj) != TCL_OK) {
 	    int code = ErrnoReturn(rcPtr, resObj);
 
@@ -2895,6 +2921,7 @@
 		paramPtr->output.toWrite = written;
 	    }
 	}
+        Tcl_Release(rcPtr);
 	break;
     }
 
@@ -2904,6 +2931,7 @@
 		(paramPtr->seek.seekMode==SEEK_SET) ? "start" :
 		(paramPtr->seek.seekMode==SEEK_CUR) ? "current" : "end", -1);
 
+        Tcl_Preserve(rcPtr);
 	if (InvokeTclMethod(rcPtr, "seek", offObj, baseObj, &resObj)!=TCL_OK){
 	    ForwardSetObjError(paramPtr, resObj);
 	    paramPtr->seek.offset = -1;
@@ -2927,24 +2955,29 @@
 		paramPtr->seek.offset = -1;
 	    }
 	}
+        Tcl_Release(rcPtr);
 	break;
     }
 
     case ForwardedWatch: {
 	Tcl_Obj *maskObj = DecodeEventMask(paramPtr->watch.mask);
 
+        Tcl_Preserve(rcPtr);
 	(void) InvokeTclMethod(rcPtr, "watch", maskObj, NULL, NULL);
 	Tcl_DecrRefCount(maskObj);
+        Tcl_Release(rcPtr);
 	break;
     }
 
     case ForwardedBlock: {
 	Tcl_Obj *blockObj = Tcl_NewBooleanObj(!paramPtr->block.nonblocking);
 
+        Tcl_Preserve(rcPtr);
 	if (InvokeTclMethod(rcPtr, "blocking", blockObj, NULL,
 		&resObj) != TCL_OK) {
 	    ForwardSetObjError(paramPtr, resObj);
 	}
+        Tcl_Release(rcPtr);
 	break;
     }
 
@@ -2952,10 +2985,12 @@
 	Tcl_Obj *optionObj = Tcl_NewStringObj(paramPtr->setOpt.name, -1);
 	Tcl_Obj *valueObj = Tcl_NewStringObj(paramPtr->setOpt.value, -1);
 
+        Tcl_Preserve(rcPtr);
 	if (InvokeTclMethod(rcPtr, "configure", optionObj, valueObj,
 		&resObj) != TCL_OK) {
 	    ForwardSetObjError(paramPtr, resObj);
 	}
+        Tcl_Release(rcPtr);
 	break;
     }
 
@@ -2966,12 +3001,14 @@
 
 	Tcl_Obj *optionObj = Tcl_NewStringObj(paramPtr->getOpt.name, -1);
 
+        Tcl_Preserve(rcPtr);
 	if (InvokeTclMethod(rcPtr, "cget", optionObj, NULL, &resObj)!=TCL_OK){
 	    ForwardSetObjError(paramPtr, resObj);
 	} else {
 	    Tcl_DStringAppend(paramPtr->getOpt.value,
 		    TclGetString(resObj), -1);
 	}
+        Tcl_Release(rcPtr);
 	break;
     }
 
@@ -2980,6 +3017,7 @@
 	 * Retrieve all options.
 	 */
 
+        Tcl_Preserve(rcPtr);
 	if (InvokeTclMethod(rcPtr, "cgetall", NULL, NULL, &resObj) != TCL_OK){
 	    ForwardSetObjError(paramPtr, resObj);
 	} else {
@@ -3015,6 +3053,7 @@
 		}
 	    }
 	}
+        Tcl_Release(rcPtr);
 	break;
 
     default:
