From 3459251d5a2b9589e03e818effc357e5e60ee4a9 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Thu, 11 Jul 2002 06:23:50 +0000 Subject: 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. --- Include/object.h | 100 ++++++++++++++++++++----------------------------- Objects/classobject.c | 87 +++++++++++++++++++----------------------- Objects/object.c | 8 ++-- Objects/stringobject.c | 4 +- Objects/tupleobject.c | 4 +- Objects/typeobject.c | 66 ++++++++++++++------------------ 6 files changed, 112 insertions(+), 157 deletions(-) diff --git a/Include/object.h b/Include/object.h index 1f9100e..736095a 100644 --- a/Include/object.h +++ b/Include/object.h @@ -51,35 +51,10 @@ A standard interface exists for objects that contain an array of items whose size is determined when the object is allocated. */ -#ifdef Py_DEBUG -/* Turn on aggregate reference counting. This arranges that extern - * _Py_RefTotal hold a count of all references, the sum of ob_refcnt - * across all objects. The value can be gotten programatically via - * sys.gettotalrefcount() (which exists only if Py_REF_DEBUG is enabled). - * In a debug-mode build, this is where the "8288" comes from in - * - * >>> 23 - * 23 - * [8288 refs] - * >>> - * - * Note that if this count increases when you're not storing away new objects, - * there's probably a leak. Remember, though, that in interactive mode the - * special name "_" holds a reference to the last result displayed! - * Py_REF_DEBUG also checks after every decref to verify that the refcount - * hasn't gone negative, and causes an immediate fatal error if it has. - */ -#define Py_REF_DEBUG - -/* Turn on heavy reference debugging. This is major surgery. Every PyObject - * grows two more pointers, to maintain a doubly-linked list of all live - * heap-allocated objects (note that, e.g., most builtin type objects are - * not in this list, as they're statically allocated). This list can be - * materialized into a Python list via sys.getobjects() (which exists only - * if Py_TRACE_REFS is enabled). Py_TRACE_REFS implies Py_REF_DEBUG. - */ +/* Py_DEBUG implies Py_TRACE_REFS. */ +#if defined(Py_DEBUG) && !defined(Py_TRACE_REFS) #define Py_TRACE_REFS -#endif /* Py_DEBUG */ +#endif /* Py_TRACE_REFS implies Py_REF_DEBUG. */ #if defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) @@ -536,40 +511,45 @@ variable first, both of which are slower; and in a multi-threaded environment the global variable trick is not safe.) */ +/* First define a pile of simple helper macros, one set per special + * build symbol. These either expand to the obvious things, or to + * nothing at all when the special mode isn't in effect. The main + * macros can later be defined just once then, yet expand to different + * things depending on which special build options are and aren't in effect. + * Trust me : while painful, this is 20x easier to understand than, + * e.g, defining _Py_NewReference five different times in a maze of nested + * #ifdefs (we used to do that -- it was impenetrable). + */ #ifdef Py_REF_DEBUG extern DL_IMPORT(long) _Py_RefTotal; extern DL_IMPORT(void) _Py_NegativeRefcount(const char *fname, int lineno, PyObject *op); -#define _PyMAYBE_BUMP_REFTOTAL _Py_RefTotal++ -#define _PyMAYBE_DROP_REFTOTAL _Py_RefTotal-- -#define _PyMAYBE_BUMP_REFTOTAL_COMMA _PyMAYBE_BUMP_REFTOTAL , -#define _PyMAYBE_DROP_REFTOTAL_COMMA _PyMAYBE_DROP_REFTOTAL , -#define _PyMAYBE_CHECK_REFCNT(OP) \ +#define _Py_INC_REFTOTAL _Py_RefTotal++ +#define _Py_DEC_REFTOTAL _Py_RefTotal-- +#define _Py_REF_DEBUG_COMMA , +#define _Py_CHECK_REFCNT(OP) \ { if ((OP)->ob_refcnt < 0) \ _Py_NegativeRefcount(__FILE__, __LINE__, \ (PyObject *)(OP)); \ } #else -#define _PyMAYBE_BUMP_REFTOTAL -#define _PyMAYBE_DROP_REFTOTAL -#define _PyMAYBE_BUMP_REFTOTAL_COMMA -#define _PyMAYBE_DROP_REFTOTAL_COMMA -#define _PyMAYBE_CHECK_REFCNT(OP) /* a semicolon */; +#define _Py_INC_REFTOTAL +#define _Py_DEC_REFTOTAL +#define _Py_REF_DEBUG_COMMA +#define _Py_CHECK_REFCNT(OP) /* a semicolon */; #endif /* Py_REF_DEBUG */ #ifdef COUNT_ALLOCS extern DL_IMPORT(void) inc_count(PyTypeObject *); -#define _PyMAYBE_BUMP_COUNT(OP) inc_count((OP)->ob_type) -#define _PyMAYBE_BUMP_FREECOUNT(OP) (OP)->ob_type->tp_frees++ -#define _PyMAYBE_DROP_FREECOUNT(OP) (OP)->ob_type->tp_frees-- -#define _PyMAYBE_BUMP_COUNT_COMMA(OP) _PyMAYBE_BUMP_COUNT(OP) , -#define _PyMAYBE_BUMP_FREECOUNT_COMMA(OP) _PyMAYBE_BUMP_FREECOUNT(OP) , +#define _Py_INC_TPALLOCS(OP) inc_count((OP)->ob_type) +#define _Py_INC_TPFREES(OP) (OP)->ob_type->tp_frees++ +#define _Py_DEC_TPFREES(OP) (OP)->ob_type->tp_frees-- +#define _Py_COUNT_ALLOCS_COMMA , #else -#define _PyMAYBE_BUMP_COUNT(OP) -#define _PyMAYBE_BUMP_FREECOUNT(OP) -#define _PyMAYBE_DROP_FREECOUNT(OP) -#define _PyMAYBE_BUMP_COUNT_COMMA(OP) -#define _PyMAYBE_BUMP_FREECOUNT_COMMA(OP) +#define _Py_INC_TPALLOCS(OP) +#define _Py_INC_TPFREES(OP) +#define _Py_DEC_TPFREES(OP) +#define _Py_COUNT_ALLOCS_COMMA #endif /* COUNT_ALLOCS */ #ifdef Py_TRACE_REFS @@ -584,27 +564,27 @@ extern DL_IMPORT(void) _Py_ResetReferences(void); /* Without Py_TRACE_REFS, there's little enough to do that we expand code * inline. */ -#define _Py_NewReference(op) ( \ - _PyMAYBE_BUMP_COUNT_COMMA(op) \ - _PyMAYBE_BUMP_REFTOTAL_COMMA \ +#define _Py_NewReference(op) ( \ + _Py_INC_TPALLOCS(op) _Py_COUNT_ALLOCS_COMMA \ + _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \ (op)->ob_refcnt = 1) -#define _Py_ForgetReference(op) _PyMAYBE_BUMP_FREECOUNT(op) +#define _Py_ForgetReference(op) _Py_INC_TPFREES(op) #define _Py_Dealloc(op) ( \ - _PyMAYBE_BUMP_FREECOUNT_COMMA(op) \ + _Py_INC_TPFREES(op) _Py_COUNT_ALLOCS_COMMA \ (*(op)->ob_type->tp_dealloc)((PyObject *)(op))) #endif /* !Py_TRACE_REFS */ -#define Py_INCREF(op) ( \ - _PyMAYBE_BUMP_REFTOTAL_COMMA \ +#define Py_INCREF(op) ( \ + _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \ (op)->ob_refcnt++) -#define Py_DECREF(op) \ - if (_PyMAYBE_DROP_REFTOTAL_COMMA \ - --(op)->ob_refcnt != 0) \ - _PyMAYBE_CHECK_REFCNT(op) \ - else \ +#define Py_DECREF(op) \ + if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA \ + --(op)->ob_refcnt != 0) \ + _Py_CHECK_REFCNT(op) \ + else \ _Py_Dealloc((PyObject *)(op)) /* Macros to use in case the object pointer may be NULL: */ diff --git a/Objects/classobject.c b/Objects/classobject.c index 90a0e22..32e8d2a 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -615,31 +615,15 @@ instance_dealloc(register PyInstanceObject *inst) 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 @@ instance_dealloc(register PyInstanceObject *inst) } /* 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 - _PyObject_GC_TRACK(inst); - return; /* __del__ added a reference; don't delete now */ + assert(inst->ob_refcnt > 0); + if (--inst->ob_refcnt == 0) { + Py_DECREF(inst->in_class); + Py_XDECREF(inst->in_dict); + PyObject_GC_Del(inst); } -#ifdef Py_TRACE_REFS - _Py_ForgetReference((PyObject *)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); + /* 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 @@ sliceobj_from_intint(int i, int j) start = PyInt_FromLong((long)i); if (!start) return NULL; - + end = PyInt_FromLong((long)j); if (!end) { Py_DECREF(start); @@ -1131,9 +1120,9 @@ instance_slice(PyInstanceObject *inst, int i, int j) 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 @@ instance_contains(PyInstanceObject *inst, PyObject *member) 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 @@ static PyObject *coerce_obj; /* 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 @@ half_binop(PyObject *v, PyObject *w, char *opname, binaryfunc thisfunc, PyObject *coerced = NULL; PyObject *v1; PyObject *result; - + if (!PyInstance_Check(v)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; @@ -1708,7 +1697,7 @@ bin_power(PyObject *v, PyObject *w) /* 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 @@ instance_ipow(PyObject *v, PyObject *w, PyObject *z) #define NAME_OPS 6 static PyObject **name_op = NULL; -static int +static int init_name_op(void) { int i; @@ -1818,7 +1807,7 @@ half_richcompare(PyObject *v, PyObject *w, int op) 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()); diff --git a/Objects/object.c b/Objects/object.c index 4987ff3..19f9a7a 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -7,7 +7,7 @@ #include "macglue.h" #endif -#if defined( Py_TRACE_REFS ) || defined( Py_REF_DEBUG ) +#ifdef Py_REF_DEBUG DL_IMPORT(long) _Py_RefTotal; #endif @@ -1876,13 +1876,13 @@ _Py_ResetReferences(void) void _Py_NewReference(PyObject *op) { - _Py_RefTotal++; + _Py_INC_REFTOTAL; op->ob_refcnt = 1; op->_ob_next = refchain._ob_next; op->_ob_prev = &refchain; refchain._ob_next->_ob_prev = op; refchain._ob_next = op; - _PyMAYBE_BUMP_COUNT(op); + _Py_INC_TPALLOCS(op); } void @@ -1907,7 +1907,7 @@ _Py_ForgetReference(register PyObject *op) op->_ob_next->_ob_prev = op->_ob_prev; op->_ob_prev->_ob_next = op->_ob_next; op->_ob_next = op->_ob_prev = NULL; - _PyMAYBE_BUMP_FREECOUNT(op); + _Py_INC_TPFREES(op); } void diff --git a/Objects/stringobject.c b/Objects/stringobject.c index 5d6478b..0543f80 100644 --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -3060,9 +3060,7 @@ _PyString_Resize(PyObject **pv, int newsize) return -1; } /* XXX UNREF/NEWREF interface should be more symmetrical */ -#ifdef Py_REF_DEBUG - --_Py_RefTotal; -#endif + _Py_DEC_REFTOTAL; _Py_ForgetReference(v); *pv = (PyObject *) PyObject_REALLOC((char *)v, diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 4c264fd..1a15bb7 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -677,9 +677,7 @@ _PyTuple_Resize(PyObject **pv, int newsize) } /* XXX UNREF/NEWREF interface should be more symmetrical */ -#ifdef Py_REF_DEBUG - --_Py_RefTotal; -#endif + _Py_DEC_REFTOTAL; _PyObject_GC_UNTRACK(v); _Py_ForgetReference((PyObject *) v); /* DECREF items deleted by shrinkage */ diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 2beb3b3..a7afa9b 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -365,22 +365,8 @@ call_finalizer(PyObject *self) PyObject *error_type, *error_value, *error_traceback; /* 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 */ - _Py_NewReference((PyObject *)self); -#ifdef COUNT_ALLOCS - /* compensate for boost in _Py_NewReference; note that - * _Py_RefTotal was also boosted; we'll knock that down later. - */ - self->ob_type->tp_allocs--; -#endif -#else /* !Py_TRACE_REFS */ - /* Py_INCREF boosts _Py_RefTotal if Py_REF_DEBUG is defined */ - Py_INCREF(self); -#endif /* !Py_TRACE_REFS */ + assert(self->ob_refcnt == 0); + self->ob_refcnt = 1; /* Save the current exception, if any. */ PyErr_Fetch(&error_type, &error_value, &error_traceback); @@ -402,28 +388,32 @@ call_finalizer(PyObject *self) /* 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. + assert(self->ob_refcnt > 0); + if (--self->ob_refcnt == 0) + return 0; /* this is the normal path out */ + + /* __del__ resurrected it! Make it look like the original Py_DECREF + * never happened. + */ + { + int refcnt = self->ob_refcnt; + _Py_NewReference(self); + self->ob_refcnt = refcnt; + } + assert(_Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED); + /* 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. */ - _Py_RefTotal--; -#endif - if (--self->ob_refcnt > 0) { -#ifdef COUNT_ALLOCS - self->ob_type->tp_frees--; -#endif - _PyObject_GC_TRACK(self); - return -1; /* __del__ added a reference; don't delete now */ - } -#ifdef Py_TRACE_REFS - _Py_ForgetReference((PyObject *)self); #ifdef COUNT_ALLOCS - /* compensate for increment in _Py_ForgetReference */ - self->ob_type->tp_frees--; + --self->ob_type->tp_frees; + --self->ob_type->tp_allocs; #endif -#endif - - return 0; + return -1; /* __del__ added a reference; don't delete now */ } static void @@ -1387,13 +1377,13 @@ type_getattro(PyTypeObject *type, PyObject *name) /* No readable descriptor found yet */ meta_get = NULL; - + /* Look for the attribute in the metatype */ meta_attribute = _PyType_Lookup(metatype, name); if (meta_attribute != NULL) { meta_get = meta_attribute->ob_type->tp_descr_get; - + if (meta_get != NULL && PyDescr_IsData(meta_attribute)) { /* Data descriptors implement tp_descr_set to intercept * writes. Assume the attribute is not overridden in @@ -1416,7 +1406,7 @@ type_getattro(PyTypeObject *type, PyObject *name) return local_get(attribute, (PyObject *)NULL, (PyObject *)type); } - + Py_INCREF(attribute); return attribute; } -- cgit v0.12