summaryrefslogtreecommitdiffstats
path: root/generic/tclObj.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclObj.c')
-rw-r--r--generic/tclObj.c162
1 files changed, 130 insertions, 32 deletions
diff --git a/generic/tclObj.c b/generic/tclObj.c
index 70a1ae8..28c2e53 100644
--- a/generic/tclObj.c
+++ b/generic/tclObj.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclObj.c,v 1.74 2005/04/01 15:17:25 msofer Exp $
+ * RCS: @(#) $Id: tclObj.c,v 1.75 2005/04/05 16:19:10 msofer Exp $
*/
#include "tclInt.h"
@@ -61,29 +61,80 @@ typedef struct ThreadSpecificData {
static Tcl_ThreadDataKey dataKey;
#endif /* TCL_MEM_DEBUG && TCL_THREADS */
-/*
- * Nested Tcl_Obj deletion management support. Note that the code
- * that implements all this is written as macros in tclInt.h
- */
-
-#ifdef TCL_THREADS
/*
- * Lookup key for the thread-local data used in the implementation in
- * tclInt.h.
- */
-Tcl_ThreadDataKey tclPendingObjDataKey;
-
+ * Nested Tcl_Obj deletion management support
+ *
+ * All context references used in the object freeing code are pointers
+ * to this structure; every thread will have its own structure
+ * instance. The purpose of this structure is to allow deeply nested
+ * collections of Tcl_Objs to be freed without taking a vast depth of
+ * C stack (which could cause all sorts of breakage.)
+ */
+
+typedef struct PendingObjData {
+ int deletionCount; /* Count of the number of invokations of
+ * TclFreeObj() are on the stack (at least
+ * conceptually; many are actually expanded
+ * macros). */
+ Tcl_Obj *deletionStack; /* Stack of objects that have had TclFreeObj()
+ * invoked upon them but which can't be deleted
+ * yet because they are in a nested invokation
+ * of TclFreeObj(). By postponing this way, we
+ * limit the maximum overall C stack depth when
+ * deleting a complex object. The down-side is
+ * that we alter the overall behaviour by
+ * altering the order in which objects are
+ * deleted, and we change the order in which
+ * the string rep and the internal rep of an
+ * object are deleted. Note that code which
+ * assumes the previous behaviour in either of
+ * these respects is unsafe anyway; it was
+ * never documented as to exactly what would
+ * happen in these cases, and the overall
+ * contract of a user-level Tcl_DecrRefCount()
+ * is still preserved (assuming that a
+ * particular T_DRC would delete an object is
+ * not very safe). */
+} PendingObjData;
+
+/*
+ * These are separated out so that some semantic content is attached
+ * to them.
+ */
+#define ObjDeletionLock(contextPtr) (contextPtr)->deletionCount++
+#define ObjDeletionUnlock(contextPtr) (contextPtr)->deletionCount--
+#define ObjDeletePending(contextPtr) (contextPtr)->deletionCount > 0
+#define ObjOnStack(contextPtr) (contextPtr)->deletionStack != NULL
+#define PushObjToDelete(contextPtr,objPtr) \
+ /* Invalidate the string rep first so we can use the bytes value \
+ * for our pointer chain. */ \
+ if (((objPtr)->bytes != NULL) \
+ && ((objPtr)->bytes != tclEmptyStringRep)) { \
+ ckfree((char *) (objPtr)->bytes); \
+ } \
+ /* Now push onto the head of the stack. */ \
+ (objPtr)->bytes = (char *) ((contextPtr)->deletionStack); \
+ (contextPtr)->deletionStack = (objPtr)
+#define PopObjToDelete(contextPtr,objPtrVar) \
+ (objPtrVar) = (contextPtr)->deletionStack; \
+ (contextPtr)->deletionStack = (Tcl_Obj *) (objPtrVar)->bytes
+
+/*
+ * Macro to set up the local reference to the deletion context.
+ */
+#ifndef TCL_THREADS
+PendingObjData pendingObjData;
+#define ObjInitDeletionContext(contextPtr) \
+ PendingObjData *CONST contextPtr = &pendingObjData
#else
-
-/*
- * Declaration of the singleton structure referenced in the
- * implementation in tclInt.h.
- */
-PendingObjData tclPendingObjData = { 0, NULL };
-
+Tcl_ThreadDataKey pendingObjDataKey;
+#define ObjInitDeletionContext(contextPtr) \
+ PendingObjData *CONST contextPtr = (PendingObjData *) \
+ Tcl_GetThreadData(&pendingObjDataKey, sizeof(PendingObjData))
#endif
+
/*
* Prototypes for procedures defined later in this file:
*/
@@ -775,19 +826,19 @@ TclFreeObj(objPtr)
/*
* This macro declares a variable, so must come here...
*/
- TclObjInitDeletionContext(context);
+ ObjInitDeletionContext(context);
if (objPtr->refCount < -1) {
Tcl_Panic("Reference count for %lx was negative", objPtr);
}
- if (TclObjDeletePending(context)) {
- TclPushObjToDelete(context, objPtr);
+ if (ObjDeletePending(context)) {
+ PushObjToDelete(context, objPtr);
} else {
if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
- TclObjDeletionLock(context);
+ ObjDeletionLock(context);
typePtr->freeIntRepProc(objPtr);
- TclObjDeletionUnlock(context);
+ ObjDeletionUnlock(context);
}
Tcl_InvalidateStringRep(objPtr);
@@ -797,11 +848,11 @@ TclFreeObj(objPtr)
#ifdef TCL_COMPILE_STATS
tclObjsFreed++;
#endif /* TCL_COMPILE_STATS */
- TclObjDeletionLock(context);
- while (TclObjOnStack(context)) {
+ ObjDeletionLock(context);
+ while (ObjOnStack(context)) {
Tcl_Obj *objToFree;
- TclPopObjToDelete(context,objToFree);
+ PopObjToDelete(context,objToFree);
TclFreeIntRep(objToFree);
Tcl_MutexLock(&tclObjMutex);
@@ -811,7 +862,7 @@ TclFreeObj(objPtr)
tclObjsFreed++;
#endif /* TCL_COMPILE_STATS */
}
- TclObjDeletionUnlock(context);
+ ObjDeletionUnlock(context);
}
}
#else /* TCL_MEM_DEBUG */
@@ -820,11 +871,58 @@ void
TclFreeObj(objPtr)
register Tcl_Obj *objPtr; /* The object to be freed. */
{
- TclObjInitDeletionContext(context);
- if (TclObjDeletePending(context)) {
- TclPushObjToDelete(context, objPtr);
+ if (!objPtr->typePtr || !objPtr->typePtr->freeIntRepProc) {
+ /*
+ * objPtr can be freed safely, as it will not attempt to free any
+ * other objects: it will not cause recursive calls to this function.
+ */
+
+ if (objPtr->bytes && (objPtr->bytes != tclEmptyStringRep)) {
+ ckfree((char *) objPtr->bytes);
+ }
+ TclFreeObjStorage(objPtr);
+ TclIncrObjsFreed();
} else {
- TclFreeObjMacro(context, objPtr);
+ /*
+ * This macro declares a variable, so must come here...
+ */
+ ObjInitDeletionContext(context);
+
+ if (ObjDeletePending(context)) {
+ PushObjToDelete(context, objPtr);
+ } else {
+ /*
+ * Note that the contents of the while loop assume that the string
+ * rep has already been freed and we don't want to do anything
+ * fancy with adding to the queue inside ourselves. Must take care
+ * to unstack the object first since freeing the internal rep can
+ * add further objects to the stack. The code assumes that it is
+ * the first thing in a block; all current usages in the core
+ * satisfy this.
+ */
+
+ ObjDeletionLock(context);
+ objPtr->typePtr->freeIntRepProc(objPtr);
+ ObjDeletionUnlock(context);
+
+ if (objPtr->bytes && (objPtr->bytes != tclEmptyStringRep)) {
+ ckfree((char *) objPtr->bytes);
+ }
+ TclFreeObjStorage(objPtr);
+ TclIncrObjsFreed();
+ ObjDeletionLock(context);
+ while (ObjOnStack(context)) {
+ Tcl_Obj *objToFree;
+ PopObjToDelete(context,objToFree);
+ if ((objToFree->typePtr != NULL)
+ && (objToFree->typePtr->freeIntRepProc != NULL)) {
+ objToFree->typePtr->freeIntRepProc(objToFree);
+ }
+ TclFreeObjStorage(objToFree);
+ TclIncrObjsFreed();
+ }
+ ObjDeletionUnlock(context);
+ }
}
}
#endif