diff Objects/classobject.c @ 24184:c4dcb80f48ea legacy-trunk

object.h special-build macro minefield: renamed all the new lexical helper macros to something saner, and used them appropriately in other files too, to reduce #ifdef blocks. classobject.c, instance_dealloc(): One of my worst Python Memories is trying to fix this routine a few years ago when COUNT_ALLOCS was defined but Py_TRACE_REFS wasn't. The special-build code here is way too complicated. Now it's much simpler. Difference: in a Py_TRACE_REFS build, the instance is no longer in the doubly-linked list of live objects while its __del__ method is executing, and that may be visible via sys.getobjects() called from a __del__ method. Tough -- the object is presumed dead while its __del__ is executing anyway, and not calling _Py_NewReference() at the start allows enormous code simplification. typeobject.c, call_finalizer(): The special-build instance_dealloc() pain apparently spread to here too via cut-'n-paste, and this is much simpler now too. In addition, I didn't understand why this routine was calling _PyObject_GC_TRACK() after a resurrection, since there's no plausible way _PyObject_GC_UNTRACK() could have been called on the object by this point. I suspect it was left over from pasting the instance_delloc() code. Instead asserted that the object is still tracked. Caution: I suspect we don't have a test that actually exercises the subtype_dealloc() __del__-resurrected-me code.
author Tim Peters <tim.peters@gmail.com>
date Thu, 11 Jul 2002 06:23:50 +0000
parents bf3421a28744
children bc660f73795c
line wrap: on
line diff
--- a/Objects/classobject.c	Thu Jul 11 04:33:41 2002 +0000
+++ b/Objects/classobject.c	Thu Jul 11 06:23:50 2002 +0000
@@ -615,31 +615,15 @@
 	PyObject *error_type, *error_value, *error_traceback;
 	PyObject *del;
 	static PyObject *delstr;
-#ifdef Py_REF_DEBUG
-	extern long _Py_RefTotal;
-#endif
+
 	_PyObject_GC_UNTRACK(inst);
 	if (inst->in_weakreflist != NULL)
 		PyObject_ClearWeakRefs((PyObject *) inst);
 
 	/* Temporarily resurrect the object. */
-#ifdef Py_TRACE_REFS
-#ifndef Py_REF_DEBUG
-#   error "Py_TRACE_REFS defined but Py_REF_DEBUG not."
-#endif
-	/* much too complicated if Py_TRACE_REFS defined */
-	inst->ob_type = &PyInstance_Type;
-	_Py_NewReference((PyObject *)inst);
-#ifdef COUNT_ALLOCS
-	/* compensate for boost in _Py_NewReference; note that
-	 * _Py_RefTotal was also boosted; we'll knock that down later.
-	 */
-	inst->ob_type->tp_allocs--;
-#endif
-#else /* !Py_TRACE_REFS */
-	/* Py_INCREF boosts _Py_RefTotal if Py_REF_DEBUG is defined */
-	Py_INCREF(inst);
-#endif /* !Py_TRACE_REFS */
+	assert(inst->ob_type == &PyInstance_Type);
+	assert(inst->ob_refcnt == 0);
+	inst->ob_refcnt = 1;
 
 	/* Save the current exception, if any. */
 	PyErr_Fetch(&error_type, &error_value, &error_traceback);
@@ -656,32 +640,37 @@
 	}
 	/* Restore the saved exception. */
 	PyErr_Restore(error_type, error_value, error_traceback);
+
 	/* Undo the temporary resurrection; can't use DECREF here, it would
 	 * cause a recursive call.
 	 */
-#ifdef Py_REF_DEBUG
-	/* _Py_RefTotal was boosted either by _Py_NewReference or
-	 * Py_INCREF above.
-	 */
-	_Py_RefTotal--;
-#endif
-	if (--inst->ob_refcnt > 0) {
-#ifdef COUNT_ALLOCS
-		inst->ob_type->tp_frees--;
-#endif
+	assert(inst->ob_refcnt > 0);
+	if (--inst->ob_refcnt == 0) {
+		Py_DECREF(inst->in_class);
+		Py_XDECREF(inst->in_dict);
+		PyObject_GC_Del(inst);
+	}
+	else {
+		int refcnt = inst->ob_refcnt;
+		/* __del__ resurrected it!  Make it look like the original
+		 * Py_DECREF never happened.
+		 */
+		_Py_NewReference((PyObject *)inst);
+		inst->ob_refcnt = refcnt;
 		_PyObject_GC_TRACK(inst);
-		return; /* __del__ added a reference; don't delete now */
-	}
-#ifdef Py_TRACE_REFS
-	_Py_ForgetReference((PyObject *)inst);
+		/* If Py_REF_DEBUG, the original decref dropped _Py_RefTotal,
+		 * but _Py_NewReference bumped it again, so that's a wash.
+		 * If Py_TRACE_REFS, _Py_NewReference re-added self to the
+		 * object chain, so no more to do there either.
+		 * If COUNT_ALLOCS, the original decref bumped tp_frees, and
+		 * _Py_NewReference bumped tp_allocs:  both of those need to
+		 * be undone.
+		 */
 #ifdef COUNT_ALLOCS
-	/* compensate for increment in _Py_ForgetReference */
-	inst->ob_type->tp_frees--;
-#endif
+		--inst->ob_type->tp_frees;
+		--inst->ob_type->tp_allocs;
 #endif
-	Py_DECREF(inst->in_class);
-	Py_XDECREF(inst->in_dict);
-	PyObject_GC_Del(inst);
+	}
 }
 
 static PyObject *
@@ -1097,7 +1086,7 @@
 	start = PyInt_FromLong((long)i);
 	if (!start)
 		return NULL;
-	
+
 	end = PyInt_FromLong((long)j);
 	if (!end) {
 		Py_DECREF(start);
@@ -1131,9 +1120,9 @@
 		if (func == NULL)
 			return NULL;
 		arg = Py_BuildValue("(N)", sliceobj_from_intint(i, j));
-	} else 
+	} else
 		arg = Py_BuildValue("(ii)", i, j);
-		
+
 	if (arg == NULL) {
 		Py_DECREF(func);
 		return NULL;
@@ -1266,7 +1255,7 @@
 		res = PyEval_CallObject(func, arg);
 		Py_DECREF(func);
 		Py_DECREF(arg);
-		if(res == NULL) 
+		if(res == NULL)
 			return -1;
 		ret = PyObject_IsTrue(res);
 		Py_DECREF(res);
@@ -1339,7 +1328,7 @@
 
 /* Try one half of a binary operator involving a class instance. */
 static PyObject *
-half_binop(PyObject *v, PyObject *w, char *opname, binaryfunc thisfunc, 
+half_binop(PyObject *v, PyObject *w, char *opname, binaryfunc thisfunc,
 		int swapped)
 {
 	PyObject *args;
@@ -1347,7 +1336,7 @@
 	PyObject *coerced = NULL;
 	PyObject *v1;
 	PyObject *result;
-	
+
 	if (!PyInstance_Check(v)) {
 		Py_INCREF(Py_NotImplemented);
 		return Py_NotImplemented;
@@ -1708,7 +1697,7 @@
 /* This version is for ternary calls only (z != None) */
 static PyObject *
 instance_pow(PyObject *v, PyObject *w, PyObject *z)
-{	
+{
 	if (z == Py_None) {
 		return do_binop(v, w, "__pow__", "__rpow__", bin_power);
 	}
@@ -1777,7 +1766,7 @@
 #define NAME_OPS 6
 static PyObject **name_op = NULL;
 
-static int 
+static int
 init_name_op(void)
 {
 	int i;
@@ -1818,7 +1807,7 @@
 	   instance_getattr2 directly because it will not set an
 	   exception on failure. */
 	if (((PyInstanceObject *)v)->in_class->cl_getattr == NULL) {
-		method = instance_getattr2((PyInstanceObject *)v, 
+		method = instance_getattr2((PyInstanceObject *)v,
 					   name_op[op]);
 		if (method == NULL) {
 			assert(!PyErr_Occurred());