summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tclAssembly.c4
-rw-r--r--generic/tclClock.c86
-rw-r--r--generic/tclCompCmdsGR.c60
-rw-r--r--generic/tclCompile.c13
-rw-r--r--generic/tclCompile.h7
-rw-r--r--generic/tclExecute.c167
-rw-r--r--generic/tclIO.c346
-rw-r--r--generic/tclIO.h2
-rw-r--r--generic/tclIOCmd.c32
-rw-r--r--generic/tclIORChan.c67
-rw-r--r--generic/tclInt.h2
-rw-r--r--generic/tclOO.c7
-rw-r--r--tests/all.tcl3
-rw-r--r--tests/io.test60
-rw-r--r--tests/ioCmd.test7
-rw-r--r--tests/oo.test13
-rw-r--r--unix/tclUnixNotfy.c4
17 files changed, 717 insertions, 163 deletions
diff --git a/generic/tclAssembly.c b/generic/tclAssembly.c
index d1866c8..6d5676b 100644
--- a/generic/tclAssembly.c
+++ b/generic/tclAssembly.c
@@ -410,6 +410,10 @@ static const TalInstDesc TalInstructionTable[] = {
{"lappendArray", ASSEM_LVT, (INST_LAPPEND_ARRAY1<<8
| INST_LAPPEND_ARRAY4),2, 1},
{"lappendArrayStk", ASSEM_1BYTE, INST_LAPPEND_ARRAY_STK, 3, 1},
+ {"lappendList", ASSEM_LVT4, INST_LAPPEND_LIST, 1, 1},
+ {"lappendListArray",ASSEM_LVT4, INST_LAPPEND_LIST_ARRAY,2, 1},
+ {"lappendListArrayStk", ASSEM_1BYTE,INST_LAPPEND_LIST_ARRAY_STK, 3, 1},
+ {"lappendListStk", ASSEM_1BYTE, INST_LAPPEND_LIST_STK, 2, 1},
{"lappendStk", ASSEM_1BYTE, INST_LAPPEND_STK, 2, 1},
{"le", ASSEM_1BYTE, INST_LE, 2, 1},
{"lindexMulti", ASSEM_LINDEX_MULTI,
diff --git a/generic/tclClock.c b/generic/tclClock.c
index 7e917f6..9d4bcd6 100644
--- a/generic/tclClock.c
+++ b/generic/tclClock.c
@@ -522,6 +522,46 @@ ClockGetdatefieldsObjCmd(
*/
static int
+FetchEraField(
+ Tcl_Interp *interp,
+ Tcl_Obj *dict,
+ Tcl_Obj *key,
+ int *storePtr)
+{
+ Tcl_Obj *value = NULL;
+
+ if (Tcl_DictObjGet(interp, dict, key, &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (value == NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "expected key(s) not found in dictionary", -1));
+ return TCL_ERROR;
+ }
+ return Tcl_GetIndexFromObj(interp, value, eras, "era", TCL_EXACT, storePtr);
+}
+
+static int
+FetchIntField(
+ Tcl_Interp *interp,
+ Tcl_Obj *dict,
+ Tcl_Obj *key,
+ int *storePtr)
+{
+ Tcl_Obj *value = NULL;
+
+ if (Tcl_DictObjGet(interp, dict, key, &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (value == NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "expected key(s) not found in dictionary", -1));
+ return TCL_ERROR;
+ }
+ return TclGetIntFromObj(interp, value, storePtr);
+}
+
+static int
ClockGetjuliandayfromerayearmonthdayObjCmd(
ClientData clientData, /* Opaque pointer to literal pool, etc. */
Tcl_Interp *interp, /* Tcl interpreter */
@@ -532,7 +572,6 @@ ClockGetjuliandayfromerayearmonthdayObjCmd(
Tcl_Obj *dict;
ClockClientData *data = clientData;
Tcl_Obj *const *literals = data->literals;
- Tcl_Obj *fieldPtr;
int changeover;
int copied = 0;
int status;
@@ -547,22 +586,14 @@ ClockGetjuliandayfromerayearmonthdayObjCmd(
return TCL_ERROR;
}
dict = objv[1];
- if (Tcl_DictObjGet(interp, dict, literals[LIT_ERA], &fieldPtr) != TCL_OK
- || fieldPtr == NULL
- || Tcl_GetIndexFromObj(interp, fieldPtr, eras, "era", TCL_EXACT,
- &era) != TCL_OK
- || Tcl_DictObjGet(interp, dict, literals[LIT_YEAR], &fieldPtr) != TCL_OK
- || fieldPtr == NULL
- || TclGetIntFromObj(interp, fieldPtr, &fields.year) != TCL_OK
- || Tcl_DictObjGet(interp, dict, literals[LIT_MONTH], &fieldPtr) != TCL_OK
- || fieldPtr == NULL
- || TclGetIntFromObj(interp, fieldPtr, &fields.month) != TCL_OK
- || Tcl_DictObjGet(interp, dict, literals[LIT_DAYOFMONTH], &fieldPtr) != TCL_OK
- || fieldPtr == NULL
- || TclGetIntFromObj(interp, fieldPtr, &fields.dayOfMonth)!=TCL_OK
+ if (FetchEraField(interp, dict, literals[LIT_ERA], &era) != TCL_OK
+ || FetchIntField(interp, dict, literals[LIT_YEAR], &fields.year)
+ != TCL_OK
+ || FetchIntField(interp, dict, literals[LIT_MONTH], &fields.month)
+ != TCL_OK
+ || FetchIntField(interp, dict, literals[LIT_DAYOFMONTH],
+ &fields.dayOfMonth) != TCL_OK
|| TclGetIntFromObj(interp, objv[2], &changeover) != TCL_OK) {
- if (fieldPtr == NULL)
- Tcl_SetObjResult(interp, Tcl_NewStringObj("expected key(s) not found in dictionary", -1));
return TCL_ERROR;
}
fields.era = era;
@@ -625,7 +656,6 @@ ClockGetjuliandayfromerayearweekdayObjCmd(
Tcl_Obj *dict;
ClockClientData *data = clientData;
Tcl_Obj *const *literals = data->literals;
- Tcl_Obj *fieldPtr;
int changeover;
int copied = 0;
int status;
@@ -640,22 +670,14 @@ ClockGetjuliandayfromerayearweekdayObjCmd(
return TCL_ERROR;
}
dict = objv[1];
- if (Tcl_DictObjGet(interp, dict, literals[LIT_ERA], &fieldPtr) != TCL_OK
- || fieldPtr == NULL
- || Tcl_GetIndexFromObj(interp, fieldPtr, eras, "era", TCL_EXACT,
- &era) != TCL_OK
- || Tcl_DictObjGet(interp, dict, literals[LIT_ISO8601YEAR], &fieldPtr) != TCL_OK
- || fieldPtr == NULL
- || TclGetIntFromObj(interp, fieldPtr, &(fields.iso8601Year)) != TCL_OK
- || Tcl_DictObjGet(interp, dict, literals[LIT_ISO8601WEEK], &fieldPtr) != TCL_OK
- || fieldPtr == NULL
- || TclGetIntFromObj(interp, fieldPtr, &(fields.iso8601Week)) != TCL_OK
- || Tcl_DictObjGet(interp, dict, literals[LIT_DAYOFWEEK], &fieldPtr) != TCL_OK
- || fieldPtr == NULL
- || TclGetIntFromObj(interp, fieldPtr, &(fields.dayOfWeek)) != TCL_OK
+ if (FetchEraField(interp, dict, literals[LIT_ERA], &era) != TCL_OK
+ || FetchIntField(interp, dict, literals[LIT_ISO8601YEAR],
+ &fields.iso8601Year) != TCL_OK
+ || FetchIntField(interp, dict, literals[LIT_ISO8601WEEK],
+ &fields.iso8601Week) != TCL_OK
+ || FetchIntField(interp, dict, literals[LIT_DAYOFWEEK],
+ &fields.dayOfWeek) != TCL_OK
|| TclGetIntFromObj(interp, objv[2], &changeover) != TCL_OK) {
- if (fieldPtr == NULL)
- Tcl_SetObjResult(interp, Tcl_NewStringObj("expected key(s) not found in dictionary", -1));
return TCL_ERROR;
}
fields.era = era;
diff --git a/generic/tclCompCmdsGR.c b/generic/tclCompCmdsGR.c
index b3e273f..166fea0 100644
--- a/generic/tclCompCmdsGR.c
+++ b/generic/tclCompCmdsGR.c
@@ -868,28 +868,16 @@ TclCompileLappendCmd(
CompileEnv *envPtr) /* Holds resulting instructions. */
{
Tcl_Token *varTokenPtr, *valueTokenPtr;
- int isScalar, localIndex, numWords, i, fwd, offsetFwd;
+ int isScalar, localIndex, numWords, i;
DefineLineInformation; /* TIP #280 */
- /*
- * If we're not in a procedure, don't compile.
- */
-
- if (envPtr->procPtr == NULL) {
- return TCL_ERROR;
- }
-
/* TODO: Consider support for compiling expanded args. */
numWords = parsePtr->numWords;
if (numWords == 1) {
return TCL_ERROR;
}
- if (numWords != 3) {
- /*
- * LAPPEND instructions currently only handle one value, but we can
- * handle some multi-value cases by stringing them together.
- */
+ if (numWords != 3 || envPtr->procPtr == NULL) {
goto lappendMultiple;
}
@@ -943,42 +931,28 @@ TclCompileLappendCmd(
return TCL_OK;
lappendMultiple:
- /*
- * Can only handle the case where we are appending to a local scalar when
- * there are multiple values to append. Fortunately, this is common.
- */
-
- if (envPtr->procPtr == NULL) {
- return TCL_ERROR;
- }
varTokenPtr = TokenAfter(parsePtr->tokenPtr);
- PushVarNameWord(interp, varTokenPtr, envPtr, TCL_NO_ELEMENT,
+ PushVarNameWord(interp, varTokenPtr, envPtr, 0,
&localIndex, &isScalar, 1);
- if (!isScalar || localIndex < 0) {
- return TCL_ERROR;
- }
-
- /*
- * Definitely appending to a local scalar; generate the words and append
- * them.
- */
-
valueTokenPtr = TokenAfter(varTokenPtr);
for (i = 2 ; i < numWords ; i++) {
CompileWord(envPtr, valueTokenPtr, interp, i);
valueTokenPtr = TokenAfter(valueTokenPtr);
}
- TclEmitInstInt4( INST_LIST, numWords-2, envPtr);
- TclEmitInstInt4( INST_EXIST_SCALAR, localIndex, envPtr);
- offsetFwd = CurrentOffset(envPtr);
- TclEmitInstInt1( INST_JUMP_FALSE1, 0, envPtr);
- Emit14Inst( INST_LOAD_SCALAR, localIndex, envPtr);
- TclEmitInstInt4( INST_REVERSE, 2, envPtr);
- TclEmitOpcode( INST_LIST_CONCAT, envPtr);
- fwd = CurrentOffset(envPtr) - offsetFwd;
- TclStoreInt1AtPtr(fwd, envPtr->codeStart+offsetFwd+1);
- Emit14Inst( INST_STORE_SCALAR, localIndex, envPtr);
-
+ TclEmitInstInt4( INST_LIST, numWords-2, envPtr);
+ if (isScalar) {
+ if (localIndex < 0) {
+ TclEmitOpcode( INST_LAPPEND_LIST_STK, envPtr);
+ } else {
+ TclEmitInstInt4(INST_LAPPEND_LIST, localIndex, envPtr);
+ }
+ } else {
+ if (localIndex < 0) {
+ TclEmitOpcode( INST_LAPPEND_LIST_ARRAY_STK, envPtr);
+ } else {
+ TclEmitInstInt4(INST_LAPPEND_LIST_ARRAY, localIndex,envPtr);
+ }
+ }
return TCL_OK;
}
diff --git a/generic/tclCompile.c b/generic/tclCompile.c
index 347e3f0..838b195 100644
--- a/generic/tclCompile.c
+++ b/generic/tclCompile.c
@@ -650,6 +650,19 @@ InstructionDesc const tclInstructionTable[] = {
* satisfy the class check (standard definition of "all").
* Stack: ... stringValue => ... boolean */
+ {"lappendList", 5, 0, 1, {OPERAND_LVT4}},
+ /* Lappend list to scalar variable at op4 in frame.
+ * Stack: ... list => ... listVarContents */
+ {"lappendListArray", 5, -1, 1, {OPERAND_LVT4}},
+ /* Lappend list to array element; array at op4.
+ * Stack: ... elem list => ... listVarContents */
+ {"lappendListArrayStk", 1, -2, 0, {OPERAND_NONE}},
+ /* Lappend list to array element.
+ * Stack: ... arrayName elem list => ... listVarContents */
+ {"lappendListStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Lappend list to general variable.
+ * Stack: ... varName list => ... listVarContents */
+
{NULL, 0, 0, 0, {OPERAND_NONE}}
};
diff --git a/generic/tclCompile.h b/generic/tclCompile.h
index 5665ca9..fa4a360 100644
--- a/generic/tclCompile.h
+++ b/generic/tclCompile.h
@@ -799,8 +799,13 @@ typedef struct ByteCode {
#define INST_TRY_CVT_TO_BOOLEAN 183
#define INST_STR_CLASS 184
+#define INST_LAPPEND_LIST 185
+#define INST_LAPPEND_LIST_ARRAY 186
+#define INST_LAPPEND_LIST_ARRAY_STK 187
+#define INST_LAPPEND_LIST_STK 188
+
/* The last opcode */
-#define LAST_INST_OPCODE 184
+#define LAST_INST_OPCODE 188
/*
* Table describing the Tcl bytecode instructions: their name (for displaying
diff --git a/generic/tclExecute.c b/generic/tclExecute.c
index 0cd074d..2098e50 100644
--- a/generic/tclExecute.c
+++ b/generic/tclExecute.c
@@ -3347,7 +3347,7 @@ TEBCresume(
*/
{
- int storeFlags;
+ int storeFlags, len;
case INST_STORE_ARRAY4:
opnd = TclGetUInt4AtPtr(pc+1);
@@ -3587,6 +3587,171 @@ TEBCresume(
#endif
TRACE_APPEND(("%.30s\n", O2S(objResultPtr)));
NEXT_INST_V(pcAdjustment, cleanup, 1);
+
+ case INST_LAPPEND_LIST:
+ opnd = TclGetUInt4AtPtr(pc+1);
+ valuePtr = OBJ_AT_TOS;
+ varPtr = LOCAL(opnd);
+ cleanup = 1;
+ pcAdjustment = 5;
+ while (TclIsVarLink(varPtr)) {
+ varPtr = varPtr->value.linkPtr;
+ }
+ TRACE(("%u <- \"%.30s\" => ", opnd, O2S(valuePtr)));
+ if (TclListObjGetElements(interp, valuePtr, &objc, &objv)
+ != TCL_OK) {
+ TRACE_ERROR(interp);
+ goto gotError;
+ }
+ if (TclIsVarDirectReadable(varPtr)
+ && TclIsVarDirectWritable(varPtr)) {
+ goto lappendListDirect;
+ }
+ arrayPtr = NULL;
+ part1Ptr = part2Ptr = NULL;
+ goto lappendListPtr;
+
+ case INST_LAPPEND_LIST_ARRAY:
+ opnd = TclGetUInt4AtPtr(pc+1);
+ valuePtr = OBJ_AT_TOS;
+ part1Ptr = NULL;
+ part2Ptr = OBJ_UNDER_TOS;
+ arrayPtr = LOCAL(opnd);
+ cleanup = 2;
+ pcAdjustment = 5;
+ while (TclIsVarLink(arrayPtr)) {
+ arrayPtr = arrayPtr->value.linkPtr;
+ }
+ TRACE(("%u \"%.30s\" \"%.30s\" => ",
+ opnd, O2S(part2Ptr), O2S(valuePtr)));
+ if (TclListObjGetElements(interp, valuePtr, &objc, &objv)
+ != TCL_OK) {
+ TRACE_ERROR(interp);
+ goto gotError;
+ }
+ if (TclIsVarArray(arrayPtr) && !ReadTraced(arrayPtr)
+ && !WriteTraced(arrayPtr)) {
+ varPtr = VarHashFindVar(arrayPtr->value.tablePtr, part2Ptr);
+ if (varPtr && TclIsVarDirectReadable(varPtr)
+ && TclIsVarDirectWritable(varPtr)) {
+ goto lappendListDirect;
+ }
+ }
+ varPtr = TclLookupArrayElement(interp, part1Ptr, part2Ptr,
+ TCL_LEAVE_ERR_MSG, "set", 1, 1, arrayPtr, opnd);
+ if (varPtr == NULL) {
+ TRACE_ERROR(interp);
+ goto gotError;
+ }
+ goto lappendListPtr;
+
+ case INST_LAPPEND_LIST_ARRAY_STK:
+ pcAdjustment = 1;
+ cleanup = 3;
+ valuePtr = OBJ_AT_TOS;
+ part2Ptr = OBJ_UNDER_TOS; /* element name */
+ part1Ptr = OBJ_AT_DEPTH(2); /* array name */
+ TRACE(("\"%.30s(%.30s)\" \"%.30s\" => ",
+ O2S(part1Ptr), O2S(part2Ptr), O2S(valuePtr)));
+ goto lappendList;
+
+ case INST_LAPPEND_LIST_STK:
+ pcAdjustment = 1;
+ cleanup = 2;
+ valuePtr = OBJ_AT_TOS;
+ part2Ptr = NULL;
+ part1Ptr = OBJ_UNDER_TOS; /* variable name */
+ TRACE(("\"%.30s\" \"%.30s\" => ", O2S(part1Ptr), O2S(valuePtr)));
+ goto lappendList;
+
+ lappendListDirect:
+ objResultPtr = varPtr->value.objPtr;
+ if (TclListObjLength(interp, objResultPtr, &len) != TCL_OK) {
+ TRACE_ERROR(interp);
+ goto gotError;
+ }
+ if (Tcl_IsShared(objResultPtr)) {
+ Tcl_Obj *newValue = Tcl_DuplicateObj(objResultPtr);
+
+ TclDecrRefCount(objResultPtr);
+ varPtr->value.objPtr = objResultPtr = newValue;
+ Tcl_IncrRefCount(newValue);
+ }
+ if (Tcl_ListObjReplace(interp, objResultPtr, len, 0, objc, objv)
+ != TCL_OK) {
+ TRACE_ERROR(interp);
+ goto gotError;
+ }
+ TRACE_APPEND(("%.30s\n", O2S(objResultPtr)));
+ NEXT_INST_V(pcAdjustment, cleanup, 1);
+
+ lappendList:
+ opnd = -1;
+ if (TclListObjGetElements(interp, valuePtr, &objc, &objv)
+ != TCL_OK) {
+ TRACE_ERROR(interp);
+ goto gotError;
+ }
+ DECACHE_STACK_INFO();
+ varPtr = TclObjLookupVarEx(interp, part1Ptr, part2Ptr,
+ TCL_LEAVE_ERR_MSG, "set", 1, 1, &arrayPtr);
+ CACHE_STACK_INFO();
+ if (!varPtr) {
+ TRACE_ERROR(interp);
+ goto gotError;
+ }
+
+ lappendListPtr:
+ if (TclIsVarInHash(varPtr)) {
+ VarHashRefCount(varPtr)++;
+ }
+ if (arrayPtr && TclIsVarInHash(arrayPtr)) {
+ VarHashRefCount(arrayPtr)++;
+ }
+ DECACHE_STACK_INFO();
+ objResultPtr = TclPtrGetVar(interp, varPtr, arrayPtr,
+ part1Ptr, part2Ptr, TCL_LEAVE_ERR_MSG, opnd);
+ CACHE_STACK_INFO();
+ if (TclIsVarInHash(varPtr)) {
+ VarHashRefCount(varPtr)--;
+ }
+ if (arrayPtr && TclIsVarInHash(arrayPtr)) {
+ VarHashRefCount(arrayPtr)--;
+ }
+
+ {
+ int createdNewObj = 0;
+
+ if (!objResultPtr) {
+ objResultPtr = valuePtr;
+ } else if (TclListObjLength(interp, objResultPtr, &len)!=TCL_OK) {
+ TRACE_ERROR(interp);
+ goto gotError;
+ } else {
+ if (Tcl_IsShared(objResultPtr)) {
+ objResultPtr = Tcl_DuplicateObj(objResultPtr);
+ createdNewObj = 1;
+ }
+ if (Tcl_ListObjReplace(interp, objResultPtr, len,0, objc,objv)
+ != TCL_OK) {
+ goto errorInLappendListPtr;
+ }
+ }
+ DECACHE_STACK_INFO();
+ objResultPtr = TclPtrSetVar(interp, varPtr, arrayPtr, part1Ptr,
+ part2Ptr, objResultPtr, TCL_LEAVE_ERR_MSG, opnd);
+ CACHE_STACK_INFO();
+ if (!objResultPtr) {
+ errorInLappendListPtr:
+ if (createdNewObj) {
+ TclDecrRefCount(objResultPtr);
+ }
+ TRACE_ERROR(interp);
+ goto gotError;
+ }
+ }
+ TRACE_APPEND(("%.30s\n", O2S(objResultPtr)));
+ NEXT_INST_V(pcAdjustment, cleanup, 1);
}
/*
diff --git a/generic/tclIO.c b/generic/tclIO.c
index d7ed3ac..7381f4d 100644
--- a/generic/tclIO.c
+++ b/generic/tclIO.c
@@ -182,6 +182,14 @@ static int CloseChannelPart(Tcl_Interp *interp, Channel *chanPtr,
static int CloseWrite(Tcl_Interp *interp, Channel *chanPtr);
static void CommonGetsCleanup(Channel *chanPtr);
static int CopyData(CopyState *csPtr, int mask);
+static int MoveBytes(CopyState *csPtr);
+
+static void MBCallback(CopyState *csPtr, Tcl_Obj *errObj);
+static void MBError(CopyState *csPtr, int mask, int errorCode);
+static int MBRead(CopyState *csPtr);
+static int MBWrite(CopyState *csPtr);
+static void MBEvent(ClientData clientData, int mask);
+
static void CopyEventProc(ClientData clientData, int mask);
static void CreateScriptRecord(Tcl_Interp *interp,
Channel *chanPtr, int mask, Tcl_Obj *scriptPtr);
@@ -1644,6 +1652,7 @@ Tcl_CreateChannel(
chanPtr->upChanPtr = NULL;
chanPtr->inQueueHead = NULL;
chanPtr->inQueueTail = NULL;
+ chanPtr->refCount = 0;
/*
* TIP #219, Tcl Channel Reflection API
@@ -1864,6 +1873,7 @@ Tcl_StackChannel(
chanPtr->upChanPtr = NULL;
chanPtr->inQueueHead = NULL;
chanPtr->inQueueTail = NULL;
+ chanPtr->refCount = 0;
/*
* Place new block at the head of a possibly existing list of previously
@@ -1888,6 +1898,31 @@ Tcl_StackChannel(
return (Tcl_Channel) chanPtr;
}
+
+void
+TclChannelPreserve(
+ Tcl_Channel chan)
+{
+ ((Channel *)chan)->refCount++;
+}
+
+void
+TclChannelRelease(
+ Tcl_Channel chan)
+{
+ Channel *chanPtr = (Channel *) chan;
+
+ if (chanPtr->refCount == 0) {
+ Tcl_Panic("Channel released more than preserved");
+ }
+ if (--chanPtr->refCount) {
+ return;
+ }
+ if (chanPtr->typePtr == NULL) {
+ ckfree(chanPtr);
+ }
+}
+
/*
*----------------------------------------------------------------------
@@ -2036,11 +2071,6 @@ Tcl_UnstackChannel(
result = ChanClose(chanPtr, interp);
chanPtr->typePtr = NULL;
- /*
- * AK: Tcl_NotifyChannel may hold a reference to this block of memory
- */
-
- Tcl_EventuallyFree(chanPtr, TCL_DYNAMIC);
UpdateInterest(statePtr->topChanPtr);
if (result != 0) {
@@ -2634,7 +2664,7 @@ FlushChannel(
* of the queued output to the channel.
*/
- Tcl_Preserve(chanPtr);
+ TclChannelPreserve((Tcl_Channel)chanPtr);
while (statePtr->outQueueHead) {
bufPtr = statePtr->outQueueHead;
@@ -2825,7 +2855,7 @@ FlushChannel(
}
done:
- Tcl_Release(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
return errorCode;
}
@@ -2993,7 +3023,6 @@ CloseChannel(
downChanPtr->upChanPtr = NULL;
chanPtr->typePtr = NULL;
- Tcl_EventuallyFree(chanPtr, TCL_DYNAMIC);
return Tcl_Close(interp, (Tcl_Channel) downChanPtr);
}
@@ -3001,13 +3030,11 @@ CloseChannel(
* There is only the TOP Channel, so we free the remaining pointers we
* have and then ourselves. Since this is the last of the channels in the
* stack, make sure to free the ChannelState structure associated with it.
- * We use Tcl_EventuallyFree to allow for any last references.
*/
chanPtr->typePtr = NULL;
Tcl_EventuallyFree(statePtr, TCL_DYNAMIC);
- Tcl_EventuallyFree(chanPtr, TCL_DYNAMIC);
return errorCode;
}
@@ -4383,7 +4410,7 @@ Tcl_GetsObj(
*/
chanPtr = statePtr->topChanPtr;
- Tcl_Preserve(chanPtr);
+ TclChannelPreserve((Tcl_Channel)chanPtr);
bufPtr = statePtr->inQueueHead;
encoding = statePtr->encoding;
@@ -4619,9 +4646,9 @@ Tcl_GetsObj(
*/
if (chanPtr != statePtr->topChanPtr) {
- Tcl_Release(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
chanPtr = statePtr->topChanPtr;
- Tcl_Preserve(chanPtr);
+ TclChannelPreserve((Tcl_Channel)chanPtr);
}
bufPtr = gs.bufPtr;
@@ -4657,9 +4684,9 @@ Tcl_GetsObj(
* self-modifying reflected transforms.
*/
if (chanPtr != statePtr->topChanPtr) {
- Tcl_Release(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
chanPtr = statePtr->topChanPtr;
- Tcl_Preserve(chanPtr);
+ TclChannelPreserve((Tcl_Channel)chanPtr);
}
bufPtr = statePtr->inQueueHead;
if (bufPtr != NULL) {
@@ -4701,12 +4728,12 @@ Tcl_GetsObj(
* self-modifying reflected transforms.
*/
if (chanPtr != statePtr->topChanPtr) {
- Tcl_Release(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
chanPtr = statePtr->topChanPtr;
- Tcl_Preserve(chanPtr);
+ TclChannelPreserve((Tcl_Channel)chanPtr);
}
UpdateInterest(chanPtr);
- Tcl_Release(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
return copiedTotal;
}
@@ -4752,7 +4779,7 @@ TclGetsObjBinary(
*/
chanPtr = statePtr->topChanPtr;
- Tcl_Preserve(chanPtr);
+ TclChannelPreserve((Tcl_Channel)chanPtr);
bufPtr = statePtr->inQueueHead;
@@ -4957,7 +4984,7 @@ TclGetsObjBinary(
done:
UpdateInterest(chanPtr);
- Tcl_Release(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
return copiedTotal;
}
@@ -5581,7 +5608,7 @@ DoReadChars(
chanPtr = statePtr->topChanPtr;
encoding = statePtr->encoding;
factor = UTF_EXPANSION_FACTOR;
- Tcl_Preserve(chanPtr);
+ TclChannelPreserve((Tcl_Channel)chanPtr);
binaryMode = (encoding == NULL)
&& (statePtr->inputTranslation == TCL_TRANSLATE_LF)
@@ -5642,9 +5669,9 @@ DoReadChars(
}
result = GetInput(chanPtr);
if (chanPtr != statePtr->topChanPtr) {
- Tcl_Release(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
chanPtr = statePtr->topChanPtr;
- Tcl_Preserve(chanPtr);
+ TclChannelPreserve((Tcl_Channel)chanPtr);
}
if (result != 0) {
if (!GotFlag(statePtr, CHANNEL_BLOCKED)) {
@@ -5672,9 +5699,9 @@ DoReadChars(
* self-modifying reflected transforms.
*/
if (chanPtr != statePtr->topChanPtr) {
- Tcl_Release(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
chanPtr = statePtr->topChanPtr;
- Tcl_Preserve(chanPtr);
+ TclChannelPreserve((Tcl_Channel)chanPtr);
}
/*
@@ -5682,7 +5709,7 @@ DoReadChars(
* in the buffers.
*/
UpdateInterest(chanPtr);
- Tcl_Release(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
return copied;
}
@@ -8054,7 +8081,7 @@ Tcl_NotifyChannel(
* Preserve the channel struct in case the script closes it.
*/
- Tcl_Preserve(channel);
+ TclChannelPreserve((Tcl_Channel)channel);
Tcl_Preserve(statePtr);
/*
@@ -8104,7 +8131,7 @@ Tcl_NotifyChannel(
}
Tcl_Release(statePtr);
- Tcl_Release(channel);
+ TclChannelRelease(channel);
tsdPtr->nestedHandlerPtr = nh.nestedHandlerPtr;
}
@@ -8586,7 +8613,7 @@ TclChannelEventScriptInvoker(
*/
Tcl_Preserve(interp);
- Tcl_Preserve(chanPtr);
+ TclChannelPreserve((Tcl_Channel)chanPtr);
result = Tcl_EvalObjEx(interp, esPtr->scriptPtr, TCL_EVAL_GLOBAL);
/*
@@ -8603,7 +8630,7 @@ TclChannelEventScriptInvoker(
}
Tcl_BackgroundException(interp, result);
}
- Tcl_Release(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
Tcl_Release(interp);
}
@@ -8778,6 +8805,7 @@ TclCopyChannel(
int readFlags, writeFlags;
CopyState *csPtr;
int nonBlocking = (cmdPtr) ? CHANNEL_NONBLOCKING : 0;
+ int moveBytes;
inStatePtr = inPtr->state;
outStatePtr = outPtr->state;
@@ -8829,13 +8857,24 @@ TclCopyChannel(
| CHANNEL_UNBUFFERED;
/*
+ * Test for conditions where we know we can just move bytes from input
+ * channel to output channel with no transformation or even examination
+ * of the bytes themselves.
+ */
+
+ moveBytes = inStatePtr->inEofChar == '\0' /* No eofChar to stop input */
+ && inStatePtr->inputTranslation == TCL_TRANSLATE_LF
+ && outStatePtr->outputTranslation == TCL_TRANSLATE_LF
+ && inStatePtr->encoding == outStatePtr->encoding;
+
+ /*
* Allocate a new CopyState to maintain info about the current copy in
* progress. This structure will be deallocated when the copy is
* completed.
*/
- csPtr = ckalloc(sizeof(CopyState) + inStatePtr->bufSize);
- csPtr->bufSize = inStatePtr->bufSize;
+ csPtr = ckalloc(sizeof(CopyState) + !moveBytes * inStatePtr->bufSize);
+ csPtr->bufSize = !moveBytes * inStatePtr->bufSize;
csPtr->readPtr = inPtr;
csPtr->writePtr = outPtr;
csPtr->readFlags = readFlags;
@@ -8851,6 +8890,10 @@ TclCopyChannel(
inStatePtr->csPtrR = csPtr;
outStatePtr->csPtrW = csPtr;
+ if (moveBytes) {
+ return MoveBytes(csPtr);
+ }
+
/*
* Special handling of -size 0 async transfers, so that the -command is
* still called asynchronously.
@@ -8885,6 +8928,225 @@ TclCopyChannel(
*----------------------------------------------------------------------
*/
+static void
+MBCallback(
+ CopyState *csPtr,
+ Tcl_Obj *errObj)
+{
+ Tcl_Obj *cmdPtr = Tcl_DuplicateObj(csPtr->cmdPtr);
+ Tcl_WideInt total = csPtr->total;
+ Tcl_Interp *interp = csPtr->interp;
+ int code;
+
+ Tcl_IncrRefCount(cmdPtr);
+ StopCopy(csPtr);
+
+ /* TODO: What if cmdPtr is not a list?! */
+
+ Tcl_ListObjAppendElement(NULL, cmdPtr, Tcl_NewWideIntObj(total));
+ if (errObj) {
+ Tcl_ListObjAppendElement(NULL, cmdPtr, errObj);
+ }
+
+ Tcl_Preserve(interp);
+ code = Tcl_EvalObjEx(interp, cmdPtr, TCL_EVAL_GLOBAL);
+ if (code != TCL_OK) {
+ Tcl_BackgroundException(interp, code);
+ }
+ Tcl_Release(interp);
+ TclDecrRefCount(cmdPtr);
+}
+
+static void
+MBError(
+ CopyState *csPtr,
+ int mask,
+ int errorCode)
+{
+ Tcl_Channel inChan = (Tcl_Channel) csPtr->readPtr;
+ Tcl_Channel outChan = (Tcl_Channel) csPtr->writePtr;
+ Tcl_Obj *errObj;
+
+ Tcl_SetErrno(errorCode);
+
+ errObj = Tcl_ObjPrintf( "error %sing \"%s\": %s",
+ (mask & TCL_READABLE) ? "read" : "writ",
+ Tcl_GetChannelName((mask & TCL_READABLE) ? inChan : outChan),
+ Tcl_PosixError(csPtr->interp));
+
+ if (csPtr->cmdPtr) {
+ MBCallback(csPtr, errObj);
+ } else {
+ Tcl_SetObjResult(csPtr->interp, errObj);
+ StopCopy(csPtr);
+ }
+}
+
+static void
+MBEvent(
+ ClientData clientData,
+ int mask)
+{
+ CopyState *csPtr = (CopyState *) clientData;
+ Tcl_Channel inChan = (Tcl_Channel) csPtr->readPtr;
+ Tcl_Channel outChan = (Tcl_Channel) csPtr->writePtr;
+ ChannelState *inStatePtr = csPtr->readPtr->state;
+
+ if (mask & TCL_WRITABLE) {
+ Tcl_DeleteChannelHandler(inChan, MBEvent, csPtr);
+ Tcl_DeleteChannelHandler(outChan, MBEvent, csPtr);
+ switch (MBWrite(csPtr)) {
+ case TCL_OK:
+ MBCallback(csPtr, NULL);
+ break;
+ case TCL_CONTINUE:
+ Tcl_CreateChannelHandler(inChan, TCL_READABLE, MBEvent, csPtr);
+ break;
+ }
+ } else if (mask & TCL_READABLE) {
+ if (TCL_OK == MBRead(csPtr)) {
+ /* When at least one full buffer is present, stop reading. */
+ if (IsBufferFull(inStatePtr->inQueueHead)
+ || !Tcl_InputBlocked(inChan)) {
+ Tcl_DeleteChannelHandler(inChan, MBEvent, csPtr);
+ }
+
+ /* Successful read -- set up to write the bytes we read */
+ Tcl_CreateChannelHandler(outChan, TCL_WRITABLE, MBEvent, csPtr);
+ }
+ }
+}
+
+static int
+MBRead(
+ CopyState *csPtr)
+{
+ ChannelState *inStatePtr = csPtr->readPtr->state;
+ ChannelBuffer *bufPtr = inStatePtr->inQueueHead;
+ int code;
+
+ if (bufPtr && BytesLeft(bufPtr) > 0) {
+ return TCL_OK;
+ }
+
+ code = GetInput(inStatePtr->topChanPtr);
+ if (code == 0) {
+ return TCL_OK;
+ } else {
+ MBError(csPtr, TCL_READABLE, code);
+ return TCL_ERROR;
+ }
+}
+
+static int
+MBWrite(
+ CopyState *csPtr)
+{
+ ChannelState *inStatePtr = csPtr->readPtr->state;
+ ChannelState *outStatePtr = csPtr->writePtr->state;
+ ChannelBuffer *bufPtr = inStatePtr->inQueueHead;
+ ChannelBuffer *tail = NULL;
+ int code, inBytes = 0;
+
+ /* Count up number of bytes waiting in the input queue */
+ while (bufPtr) {
+ inBytes += BytesLeft(bufPtr);
+ tail = bufPtr;
+ if (csPtr->toRead != -1 && csPtr->toRead < inBytes) {
+ /* Queue has enough bytes to complete the copy */
+ break;
+ }
+ bufPtr = bufPtr->nextPtr;
+ }
+
+ if (bufPtr) {
+ /* Split the overflowing buffer in two */
+ int extra = inBytes - csPtr->toRead;
+
+ bufPtr = AllocChannelBuffer(extra);
+
+ tail->nextAdded -= extra;
+ memcpy(InsertPoint(bufPtr), InsertPoint(tail), extra);
+ bufPtr->nextAdded += extra;
+ bufPtr->nextPtr = tail->nextPtr;
+ tail->nextPtr = NULL;
+ inBytes = csPtr->toRead;
+ }
+
+ /* Update the byte counts */
+ if (csPtr->toRead != -1) {
+ csPtr->toRead -= inBytes;
+ }
+ csPtr->total += inBytes;
+
+ /* Move buffers from input to output channels */
+ if (outStatePtr->outQueueTail) {
+ outStatePtr->outQueueTail->nextPtr = inStatePtr->inQueueHead;
+ } else {
+ outStatePtr->outQueueHead = inStatePtr->inQueueHead;
+ }
+ outStatePtr->outQueueTail = tail;
+ inStatePtr->inQueueHead = bufPtr;
+ if (bufPtr == NULL) {
+ inStatePtr->inQueueTail = NULL;
+ }
+
+ code = FlushChannel(csPtr->interp, outStatePtr->topChanPtr, 0);
+ if (code) {
+ MBError(csPtr, TCL_WRITABLE, code);
+ return TCL_ERROR;
+ }
+ if (csPtr->toRead == 0 || GotFlag(inStatePtr, CHANNEL_EOF)) {
+ return TCL_OK;
+ }
+ return TCL_CONTINUE;
+}
+
+static int
+MoveBytes(
+ CopyState *csPtr) /* State of copy operation. */
+{
+ ChannelState *outStatePtr = csPtr->writePtr->state;
+ ChannelBuffer *bufPtr = outStatePtr->curOutPtr;
+ int errorCode;
+
+ if (bufPtr && BytesLeft(bufPtr)) {
+ /* If we start with unflushed bytes in the destination
+ * channel, flush them out of the way first. */
+
+ errorCode = FlushChannel(csPtr->interp, outStatePtr->topChanPtr, 0);
+ if (errorCode != 0) {
+ MBError(csPtr, TCL_WRITABLE, errorCode);
+ return TCL_ERROR;
+ }
+ }
+
+ if (csPtr->cmdPtr) {
+ Tcl_Channel inChan = (Tcl_Channel) csPtr->readPtr;
+ Tcl_CreateChannelHandler(inChan, TCL_READABLE, MBEvent, csPtr);
+ return TCL_OK;
+ }
+
+ while (1) {
+ int code;
+
+ if (TCL_ERROR == MBRead(csPtr)) {
+ return TCL_ERROR;
+ }
+ code = MBWrite(csPtr);
+ if (code == TCL_OK) {
+ Tcl_SetObjResult(csPtr->interp, Tcl_NewWideIntObj(csPtr->total));
+ StopCopy(csPtr);
+ return TCL_OK;
+ }
+ if (code == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ /* code == TCL_CONTINUE --> continue the loop */
+ }
+ return TCL_OK; /* Silence compiler warnings */
+}
+
static int
CopyData(
CopyState *csPtr, /* State of copy operation. */
@@ -9221,7 +9483,7 @@ DoRead(
ChannelState *statePtr = chanPtr->state;
char *p = dst;
- Tcl_Preserve(chanPtr);
+ TclChannelPreserve((Tcl_Channel)chanPtr);
while (bytesToRead) {
/*
* Each pass through the loop is intended to process up to
@@ -9260,7 +9522,7 @@ DoRead(
if (code) {
/* Read error */
UpdateInterest(chanPtr);
- Tcl_Release(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
return -1;
}
@@ -9358,7 +9620,7 @@ DoRead(
ResetFlag(statePtr, CHANNEL_BLOCKED);
}
- Tcl_Release(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
return (int)(p - dst);
}
@@ -9410,12 +9672,16 @@ StopCopy(
CopyState *csPtr) /* State for bg copy to stop . */
{
ChannelState *inStatePtr, *outStatePtr;
+ Tcl_Channel inChan, outChan;
+
int nonBlocking;
if (!csPtr) {
return;
}
+ inChan = (Tcl_Channel) csPtr->readPtr;
+ outChan = (Tcl_Channel) csPtr->writePtr;
inStatePtr = csPtr->readPtr->state;
outStatePtr = csPtr->writePtr->state;
@@ -9440,12 +9706,12 @@ StopCopy(
csPtr->writeFlags & (CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
if (csPtr->cmdPtr) {
- Tcl_DeleteChannelHandler((Tcl_Channel) csPtr->readPtr, CopyEventProc,
- csPtr);
- if (csPtr->readPtr != csPtr->writePtr) {
- Tcl_DeleteChannelHandler((Tcl_Channel) csPtr->writePtr,
- CopyEventProc, csPtr);
+ Tcl_DeleteChannelHandler(inChan, CopyEventProc, csPtr);
+ if (inChan != outChan) {
+ Tcl_DeleteChannelHandler(outChan, CopyEventProc, csPtr);
}
+ Tcl_DeleteChannelHandler(inChan, MBEvent, csPtr);
+ Tcl_DeleteChannelHandler(outChan, MBEvent, csPtr);
TclDecrRefCount(csPtr->cmdPtr);
}
inStatePtr->csPtrR = NULL;
diff --git a/generic/tclIO.h b/generic/tclIO.h
index 097cd61..ca74c3e 100644
--- a/generic/tclIO.h
+++ b/generic/tclIO.h
@@ -112,6 +112,8 @@ typedef struct Channel {
ChannelBuffer *inQueueHead; /* Points at first buffer in input queue. */
ChannelBuffer *inQueueTail; /* Points at last buffer in input queue. */
+
+ int refCount;
} Channel;
/*
diff --git a/generic/tclIOCmd.c b/generic/tclIOCmd.c
index 3368a76..834f225 100644
--- a/generic/tclIOCmd.c
+++ b/generic/tclIOCmd.c
@@ -181,7 +181,7 @@ Tcl_PutsObjCmd(
return TCL_ERROR;
}
- Tcl_Preserve(chan);
+ TclChannelPreserve(chan);
result = Tcl_WriteObj(chan, string);
if (result < 0) {
goto error;
@@ -192,7 +192,7 @@ Tcl_PutsObjCmd(
goto error;
}
}
- Tcl_Release(chan);
+ TclChannelRelease(chan);
return TCL_OK;
/*
@@ -207,7 +207,7 @@ Tcl_PutsObjCmd(
Tcl_SetObjResult(interp, Tcl_ObjPrintf("error writing \"%s\": %s",
TclGetString(chanObjPtr), Tcl_PosixError(interp)));
}
- Tcl_Release(chan);
+ TclChannelRelease(chan);
return TCL_ERROR;
}
@@ -255,7 +255,7 @@ Tcl_FlushObjCmd(
return TCL_ERROR;
}
- Tcl_Preserve(chan);
+ TclChannelPreserve(chan);
if (Tcl_Flush(chan) != TCL_OK) {
/*
* TIP #219.
@@ -269,10 +269,10 @@ Tcl_FlushObjCmd(
"error flushing \"%s\": %s",
TclGetString(chanObjPtr), Tcl_PosixError(interp)));
}
- Tcl_Release(chan);
+ TclChannelRelease(chan);
return TCL_ERROR;
}
- Tcl_Release(chan);
+ TclChannelRelease(chan);
return TCL_OK;
}
@@ -322,7 +322,7 @@ Tcl_GetsObjCmd(
return TCL_ERROR;
}
- Tcl_Preserve(chan);
+ TclChannelPreserve(chan);
linePtr = Tcl_NewObj();
lineLen = Tcl_GetsObj(chan, linePtr);
if (lineLen < 0) {
@@ -357,7 +357,7 @@ Tcl_GetsObjCmd(
Tcl_SetObjResult(interp, linePtr);
}
done:
- Tcl_Release(chan);
+ TclChannelRelease(chan);
return code;
}
@@ -465,7 +465,7 @@ Tcl_ReadObjCmd(
resultPtr = Tcl_NewObj();
Tcl_IncrRefCount(resultPtr);
- Tcl_Preserve(chan);
+ TclChannelPreserve(chan);
charactersRead = Tcl_ReadChars(chan, resultPtr, toRead, 0);
if (charactersRead < 0) {
/*
@@ -480,7 +480,7 @@ Tcl_ReadObjCmd(
"error reading \"%s\": %s",
TclGetString(chanObjPtr), Tcl_PosixError(interp)));
}
- Tcl_Release(chan);
+ TclChannelRelease(chan);
Tcl_DecrRefCount(resultPtr);
return TCL_ERROR;
}
@@ -499,7 +499,7 @@ Tcl_ReadObjCmd(
}
}
Tcl_SetObjResult(interp, resultPtr);
- Tcl_Release(chan);
+ TclChannelRelease(chan);
Tcl_DecrRefCount(resultPtr);
return TCL_OK;
}
@@ -559,7 +559,7 @@ Tcl_SeekObjCmd(
mode = modeArray[optionIndex];
}
- Tcl_Preserve(chan);
+ TclChannelPreserve(chan);
result = Tcl_Seek(chan, offset, mode);
if (result == Tcl_LongAsWide(-1)) {
/*
@@ -574,10 +574,10 @@ Tcl_SeekObjCmd(
"error during seek on \"%s\": %s",
TclGetString(objv[1]), Tcl_PosixError(interp)));
}
- Tcl_Release(chan);
+ TclChannelRelease(chan);
return TCL_ERROR;
}
- Tcl_Release(chan);
+ TclChannelRelease(chan);
return TCL_OK;
}
@@ -624,7 +624,7 @@ Tcl_TellObjCmd(
return TCL_ERROR;
}
- Tcl_Preserve(chan);
+ TclChannelPreserve(chan);
newLoc = Tcl_Tell(chan);
/*
@@ -635,7 +635,7 @@ Tcl_TellObjCmd(
code = TclChanCaughtErrorBypass(interp, chan);
- Tcl_Release(chan);
+ TclChannelRelease(chan);
if (code) {
return TCL_ERROR;
}
diff --git a/generic/tclIORChan.c b/generic/tclIORChan.c
index ff602c6..21c766e 100644
--- a/generic/tclIORChan.c
+++ b/generic/tclIORChan.c
@@ -436,6 +436,7 @@ static ReflectedChannelMap * GetReflectedChannelMap(Tcl_Interp *interp);
static void DeleteReflectedChannelMap(ClientData clientData,
Tcl_Interp *interp);
static int ErrnoReturn(ReflectedChannel *rcPtr, Tcl_Obj *resObj);
+static void MarkDead(ReflectedChannel *rcPtr);
/*
* Global constant strings (messages). ==================
@@ -657,7 +658,7 @@ TclChanCreateObjCmd(
chan = Tcl_CreateChannel(&tclRChannelType, TclGetString(rcId), rcPtr,
mode);
rcPtr->chan = chan;
- Tcl_Preserve(chan);
+ TclChannelPreserve(chan);
chanPtr = (Channel *) chan;
if ((methods & NULLABLE_METHODS) != NULLABLE_METHODS) {
@@ -1146,7 +1147,6 @@ ReflectClose(
if (result != TCL_OK) {
FreeReceivedError(&p);
}
- return EOK;
}
#endif
@@ -1217,17 +1217,14 @@ ReflectClose(
if (hPtr) {
Tcl_DeleteHashEntry(hPtr);
}
+ }
#endif
-
- tctPtr = ((Channel *)rcPtr->chan)->typePtr;
- if (tctPtr && tctPtr != &tclRChannelType) {
+ tctPtr = ((Channel *)rcPtr->chan)->typePtr;
+ if (tctPtr && tctPtr != &tclRChannelType) {
ckfree((char *)tctPtr);
((Channel *)rcPtr->chan)->typePtr = NULL;
- }
- Tcl_EventuallyFree(rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
-#ifdef TCL_THREADS
}
-#endif
+ Tcl_EventuallyFree(rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
return (result == TCL_OK) ? EOK : EINVAL;
}
@@ -2191,10 +2188,16 @@ FreeReflectedChannel(
{
Channel *chanPtr = (Channel *) rcPtr->chan;
- Tcl_Release(chanPtr);
- Tcl_DecrRefCount(rcPtr->name);
- Tcl_DecrRefCount(rcPtr->methods);
- Tcl_DecrRefCount(rcPtr->cmd);
+ TclChannelRelease((Tcl_Channel)chanPtr);
+ if (rcPtr->name) {
+ Tcl_DecrRefCount(rcPtr->name);
+ }
+ if (rcPtr->methods) {
+ Tcl_DecrRefCount(rcPtr->methods);
+ }
+ if (rcPtr->cmd) {
+ Tcl_DecrRefCount(rcPtr->cmd);
+ }
ckfree(rcPtr);
}
@@ -2460,6 +2463,28 @@ GetReflectedChannelMap(
*/
static void
+MarkDead(
+ ReflectedChannel *rcPtr)
+{
+ if (rcPtr->dead) {
+ return;
+ }
+ if (rcPtr->name) {
+ Tcl_DecrRefCount(rcPtr->name);
+ rcPtr->name = NULL;
+ }
+ if (rcPtr->methods) {
+ Tcl_DecrRefCount(rcPtr->methods);
+ rcPtr->methods = NULL;
+ }
+ if (rcPtr->cmd) {
+ Tcl_DecrRefCount(rcPtr->cmd);
+ rcPtr->cmd = NULL;
+ }
+ rcPtr->dead = 1;
+}
+
+static void
DeleteReflectedChannelMap(
ClientData clientData, /* The per-interpreter data structure. */
Tcl_Interp *interp) /* The interpreter being deleted. */
@@ -2494,7 +2519,7 @@ DeleteReflectedChannelMap(
chan = Tcl_GetHashValue(hPtr);
rcPtr = Tcl_GetChannelInstanceData(chan);
- rcPtr->dead = 1;
+ MarkDead(rcPtr);
Tcl_DeleteHashEntry(hPtr);
}
Tcl_DeleteHashTable(&rcmPtr->map);
@@ -2577,7 +2602,7 @@ DeleteReflectedChannelMap(
continue;
}
- rcPtr->dead = 1;
+ MarkDead(rcPtr);
Tcl_DeleteHashEntry(hPtr);
}
#endif
@@ -2724,7 +2749,7 @@ DeleteThreadReflectedChannelMap(
Tcl_Channel chan = Tcl_GetHashValue(hPtr);
ReflectedChannel *rcPtr = Tcl_GetChannelInstanceData(chan);
- rcPtr->dead = 1;
+ MarkDead(rcPtr);
Tcl_DeleteHashEntry(hPtr);
}
ckfree(rcmPtr);
@@ -2907,8 +2932,6 @@ ForwardProc(
* No parameters/results.
*/
- const Tcl_ChannelType *tctPtr;
-
if (InvokeTclMethod(rcPtr, METH_FINAL, NULL, NULL, &resObj)!=TCL_OK) {
ForwardSetObjError(paramPtr, resObj);
}
@@ -2932,13 +2955,7 @@ ForwardProc(
hPtr = Tcl_FindHashEntry(&rcmPtr->map,
Tcl_GetChannelName(rcPtr->chan));
Tcl_DeleteHashEntry(hPtr);
-
- tctPtr = ((Channel *)rcPtr->chan)->typePtr;
- if (tctPtr && tctPtr != &tclRChannelType) {
- ckfree((char *)tctPtr);
- ((Channel *)rcPtr->chan)->typePtr = NULL;
- }
- Tcl_EventuallyFree(rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
+ MarkDead(rcPtr);
break;
}
diff --git a/generic/tclInt.h b/generic/tclInt.h
index 9a2e8dd..1bb2103 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -2864,6 +2864,8 @@ MODULE_SCOPE int TclByteArrayMatch(const unsigned char *string,
int strLen, const unsigned char *pattern,
int ptnLen, int flags);
MODULE_SCOPE double TclCeil(const mp_int *a);
+MODULE_SCOPE void TclChannelPreserve(Tcl_Channel chan);
+MODULE_SCOPE void TclChannelRelease(Tcl_Channel chan);
MODULE_SCOPE int TclCheckBadOctal(Tcl_Interp *interp,
const char *value);
MODULE_SCOPE int TclChanCaughtErrorBypass(Tcl_Interp *interp,
diff --git a/generic/tclOO.c b/generic/tclOO.c
index de00733..02e00c9 100644
--- a/generic/tclOO.c
+++ b/generic/tclOO.c
@@ -1008,6 +1008,12 @@ ReleaseClassContents(
}
if (!Deleted(instancePtr)) {
Tcl_DeleteCommandFromToken(interp, instancePtr->command);
+ /*
+ * Tcl_DeleteCommandFromToken() may have done to whole
+ * job for us. Roll back and check again.
+ */
+ i--;
+ continue;
}
DelRef(instancePtr);
}
@@ -1280,6 +1286,7 @@ TclOORemoveFromInstances(
removeInstance:
if (Deleted(clsPtr->thisPtr)) {
+ DelRef(clsPtr->instances.list[i]);
clsPtr->instances.list[i] = NULL;
} else {
clsPtr->instances.num--;
diff --git a/tests/all.tcl b/tests/all.tcl
index 05d3024..0a6f57f 100644
--- a/tests/all.tcl
+++ b/tests/all.tcl
@@ -15,5 +15,8 @@ package require Tcl 8.5
package require tcltest 2.2
namespace import tcltest::*
configure {*}$argv -testdir [file dir [info script]]
+if {[singleProcess]} {
+ interp debug {} -frame 1
+}
runAllTests
proc exit args {}
diff --git a/tests/io.test b/tests/io.test
index bf5adb0..cef3e81 100644
--- a/tests/io.test
+++ b/tests/io.test
@@ -7612,6 +7612,66 @@ test io-53.12 {CopyData: foreground short reads, aka bug 3096275} {stdio unix op
close $f1
list $::done $ch
} {ok A}
+test io-53.13 {TclCopyChannel: read error reporting} -setup {
+ proc driver {cmd args} {
+ variable buffer
+ variable index
+ set chan [lindex $args 0]
+ switch -- $cmd {
+ initialize {
+ return {initialize finalize watch read}
+ }
+ finalize {
+ return
+ }
+ watch {}
+ read {
+ error FAIL
+ }
+ }
+ }
+ set outFile [makeFile {} out]
+} -body {
+ set in [chan create read [namespace which driver]]
+ chan configure $in -translation binary
+ set out [open $outFile wb]
+ chan copy $in $out
+} -cleanup {
+ catch {close $in}
+ catch {close $out}
+ removeFile out
+ rename driver {}
+} -result {error reading "*": *} -returnCodes error -match glob
+test io-53.14 {TclCopyChannel: write error reporting} -setup {
+ proc driver {cmd args} {
+ variable buffer
+ variable index
+ set chan [lindex $args 0]
+ switch -- $cmd {
+ initialize {
+ return {initialize finalize watch write}
+ }
+ finalize {
+ return
+ }
+ watch {}
+ write {
+ error FAIL
+ }
+ }
+ }
+ set inFile [makeFile {aaa} in]
+} -body {
+ set in [open $inFile rb]
+ set out [chan create write [namespace which driver]]
+ chan configure $out -translation binary
+ chan copy $in $out
+} -cleanup {
+ catch {close $in}
+ catch {close $out}
+ removeFile in
+ rename driver {}
+} -result {error writing "*": *} -returnCodes error -match glob
test io-54.1 {Recursive channel events} {socket fileevent} {
# This test checks to see if file events are delivered during recursive
diff --git a/tests/ioCmd.test b/tests/ioCmd.test
index 8d35ec7..57f8d47 100644
--- a/tests/ioCmd.test
+++ b/tests/ioCmd.test
@@ -2748,10 +2748,9 @@ test iocmd.tf-24.17.bug3522560 {postevent for transfered channel} \
init* {set ret {initialize finalize watch read}}
watch {
set l [lindex $args 0]
+ catch {after cancel $::timer}
if {[llength $l]} {
set ::timer [after $::drive [list POST $ch]]
- } else {
- after cancel $::timer
}
}
finalize {
@@ -2814,7 +2813,9 @@ test iocmd.tf-24.17.bug3522560 {postevent for transfered channel} \
update
}
LOG THREAD-LOOP-DONE
- thread::exit
+ #thread::exit
+ # Thread exits cause leaks; Use clean thread shutdown
+ set forever yourGirl
}
LOG MAIN_WAITING
diff --git a/tests/oo.test b/tests/oo.test
index d63e931..fcd9818 100644
--- a/tests/oo.test
+++ b/tests/oo.test
@@ -258,6 +258,19 @@ test oo-1.18 {OO: create object in NS with same name as global cmd} -setup {
rename test-oo-1.18 {}
A destroy
} -result ::C
+test oo-1.18.1 {Bug 75b8433707: memory leak in oo-1.18} -setup {
+ proc test-oo-1.18 {} return
+} -constraints memory -body {
+ leaktest {
+ oo::class create A
+ oo::class create B {superclass A}
+ oo::define B constructor {} {A create test-oo-1.18}
+ B create C
+ A destroy
+ }
+} -cleanup {
+ rename test-oo-1.18 {}
+} -result 0
test oo-1.19 {basic test of OO functionality: teardown order} -body {
oo::object create o
namespace delete [info object namespace o]
diff --git a/unix/tclUnixNotfy.c b/unix/tclUnixNotfy.c
index b234667..b2bea45 100644
--- a/unix/tclUnixNotfy.c
+++ b/unix/tclUnixNotfy.c
@@ -311,6 +311,7 @@ Tcl_InitNotifier(void)
* pipe to the original notifier thread
*/
if (notifierCount > 0 && processIDInitialized != getpid()) {
+ Tcl_ConditionFinalize(&notifierCV);
notifierCount = 0;
processIDInitialized = 0;
close(triggerPipe);
@@ -1375,8 +1376,7 @@ AtForkParent(void)
static void
AtForkChild(void)
{
- notifierMutex = NULL;
- notifierCV = NULL;
+ Tcl_MutexFinalize(&notifierMutex);
Tcl_InitNotifier();
}
#endif /* HAVE_PTHREAD_ATFORK */