Commit 4c4454c1 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Add support for Py_TRACE_REFS

And use it to hunt down a tricky ref issue.  We were creating more "static constants"
during shutdown.
parent 12369af8
......@@ -39,3 +39,5 @@ If the assertion `assert(var->reftype != RefType::UNKNOWN)` fails in Rewriter::c
- Now we should be at the point that the RewriterVar got created. You can do a backtrace to see who created the RewriterVar and thus where the annotation needs to get added.
Similarly, you can get this assertion about an llvm::Value*, in which case you should try `watch -l *(void**)v`.
Py_TRACE_REFS.
......@@ -25,6 +25,8 @@
#define Py_REF_DEBUG
#define WITH_PYMALLOC
#define PYMALLOC_DEBUG
// Uncomment this to get some very heavy ref debugging:
// #define Py_TRACE_REFS
#endif
// These include orders come from CPython:
......
......@@ -68,11 +68,26 @@ whose size is determined when the object is allocated.
#define Py_REF_DEBUG
#endif
// Pyston change: hacks to allow C++ features
#ifndef _PYSTON_API
typedef struct _object PyObject;
typedef struct _varobject PyVarObject;
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
#else
namespace pyston {
class Box;
class BoxVar;
}
typedef pyston::Box PyObject;
typedef pyston::BoxVar PyVarObject;
#define Py_TYPE(ob) ((ob)->cls)
#endif
#ifdef Py_TRACE_REFS
/* Define pointers to support a doubly-linked list of all live heap objects. */
#define _PyObject_HEAD_EXTRA \
struct _object *_ob_next; \
struct _object *_ob_prev;
PyObject *_ob_next; \
PyObject *_ob_prev;
#define _PyObject_EXTRA_INIT 0, 0,
......@@ -118,21 +133,6 @@ struct _varobject {
PyObject_VAR_HEAD
};
// Pyston change: hacks to allow C++ features
#ifndef _PYSTON_API
typedef struct _object PyObject;
typedef struct _varobject PyVarObject;
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
#else
namespace pyston {
class Box;
class BoxVar;
}
typedef pyston::Box PyObject;
typedef pyston::BoxVar PyVarObject;
#define Py_TYPE(ob) ((ob)->cls)
#endif
// Pyston change: moved Py_TYPE to above
#define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt)
#define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size)
......@@ -812,6 +812,9 @@ PyAPI_FUNC(void) _Py_PrintReferences(FILE *) PYSTON_NOEXCEPT;
PyAPI_FUNC(void) _Py_PrintReferenceAddresses(FILE *) PYSTON_NOEXCEPT;
PyAPI_FUNC(void) _Py_AddToAllObjects(PyObject *, int force) PYSTON_NOEXCEPT;
// Pyston addition:
PyAPI_FUNC(void) _Py_PrintReferenceAddressesCapped(FILE *, int) PYSTON_NOEXCEPT;
#else
/* Without Py_TRACE_REFS, there's little enough to do that we expand code
* inline.
......
......@@ -560,6 +560,9 @@ void Rewriter::_decref(RewriterVar* var) {
assembler->decq(assembler::Indirect(reg, offsetof(Box, ob_refcnt)));
{
assembler::ForwardJump jnz(*assembler, assembler::COND_NOT_ZERO);
#ifdef Py_TRACE_REFS
RELEASE_ASSERT(0, "need to support trace_refs here (call _Py_Dealloc instead of tp_dealloc");
#endif
assembler->movq(assembler::Indirect(reg, offsetof(Box, cls)), assembler::RAX);
assembler->callq(assembler::Indirect(assembler::RAX, offsetof(BoxedClass, tp_dealloc)));
//assembler->mov(assembler::Indirect(assembler::RAX, offsetof(BoxedClass, tp_dealloc)), assembler::R11);
......
......@@ -1153,4 +1153,143 @@ extern "C" void _PyTrash_thread_deposit_object(PyObject* op) noexcept {
extern "C" void _PyTrash_thread_destroy_chain() noexcept {
Py_FatalError("unimplemented");
}
#ifdef Py_TRACE_REFS
/* Head of circular doubly-linked list of all objects. These are linked
* together via the _ob_prev and _ob_next members of a PyObject, which
* exist only in a Py_TRACE_REFS build.
*/
extern "C" {
// static PyObject refchain = { &refchain, &refchain };
static PyObject refchain(Box::createRefchain());
}
/* Insert op at the front of the list of all objects. If force is true,
* op is added even if _ob_prev and _ob_next are non-NULL already. If
* force is false amd _ob_prev or _ob_next are non-NULL, do nothing.
* force should be true if and only if op points to freshly allocated,
* uninitialized memory, or you've unlinked op from the list and are
* relinking it into the front.
* Note that objects are normally added to the list via _Py_NewReference,
* which is called by PyObject_Init. Not all objects are initialized that
* way, though; exceptions include statically allocated type objects, and
* statically allocated singletons (like Py_True and Py_None).
*/
extern "C" void _Py_AddToAllObjects(PyObject* op, int force) noexcept {
#ifdef Py_DEBUG
if (!force) {
/* If it's initialized memory, op must be in or out of
* the list unambiguously.
*/
assert((op->_ob_prev == NULL) == (op->_ob_next == NULL));
}
#endif
if (force || op->_ob_prev == NULL) {
op->_ob_next = refchain._ob_next;
op->_ob_prev = &refchain;
refchain._ob_next->_ob_prev = op;
refchain._ob_next = op;
}
}
#endif /* Py_TRACE_REFS */
#ifdef Py_TRACE_REFS
extern "C" void _Py_NewReference(PyObject* op) noexcept {
_Py_INC_REFTOTAL;
op->ob_refcnt = 1;
_Py_AddToAllObjects(op, 1);
_Py_INC_TPALLOCS(op);
}
extern "C" void _Py_ForgetReference(register PyObject* op) noexcept {
#ifdef SLOW_UNREF_CHECK
register PyObject* p;
#endif
if (op->ob_refcnt < 0)
Py_FatalError("UNREF negative refcnt");
if (op == &refchain || op->_ob_prev->_ob_next != op || op->_ob_next->_ob_prev != op)
Py_FatalError("UNREF invalid object");
#ifdef SLOW_UNREF_CHECK
for (p = refchain._ob_next; p != &refchain; p = p->_ob_next) {
if (p == op)
break;
}
if (p == &refchain) /* Not found */
Py_FatalError("UNREF unknown object");
#endif
op->_ob_next->_ob_prev = op->_ob_prev;
op->_ob_prev->_ob_next = op->_ob_next;
op->_ob_next = op->_ob_prev = NULL;
_Py_INC_TPFREES(op);
}
extern "C" void _Py_Dealloc(PyObject* op) noexcept {
destructor dealloc = Py_TYPE(op)->tp_dealloc;
_Py_ForgetReference(op);
(*dealloc)(op);
}
/* Print all live objects. Because PyObject_Print is called, the
* interpreter must be in a healthy state.
*/
extern "C" void _Py_PrintReferences(FILE* fp) noexcept {
PyObject* op;
fprintf(fp, "Remaining objects:\n");
for (op = refchain._ob_next; op != &refchain; op = op->_ob_next) {
fprintf(fp, "%p [%" PY_FORMAT_SIZE_T "d] ", op, op->ob_refcnt);
if (PyObject_Print(op, fp, 0) != 0)
PyErr_Clear();
putc('\n', fp);
}
}
/* Print the addresses of all live objects. Unlike _Py_PrintReferences, this
* doesn't make any calls to the Python C API, so is always safe to call.
*/
extern "C" void _Py_PrintReferenceAddresses(FILE* fp) noexcept {
_Py_PrintReferenceAddressesCapped(fp, INT_MAX);
}
extern "C" void _Py_PrintReferenceAddressesCapped(FILE* fp, int max_to_print) noexcept {
PyObject* op;
fprintf(fp, "Remaining object addresses:\n");
int found = 0;
for (op = refchain._ob_next; op != &refchain; op = op->_ob_next) {
found++;
if (found <= max_to_print)
fprintf(fp, "%p [%" PY_FORMAT_SIZE_T "d] %s\n", op, op->ob_refcnt, Py_TYPE(op)->tp_name);
}
if (found > max_to_print) {
fprintf(fp, "%d more found (but not printed)\n", found - max_to_print);
}
}
extern "C" PyObject* _Py_GetObjects(PyObject* self, PyObject* args) noexcept {
int i, n;
PyObject* t = NULL;
PyObject* res, *op;
if (!PyArg_ParseTuple(args, "i|O", &n, &t))
return NULL;
op = refchain._ob_next;
res = PyList_New(0);
if (res == NULL)
return NULL;
for (i = 0; (n == 0 || i < n) && op != &refchain; i++) {
while (op == self || op == args || op == res || op == t || (t != NULL && Py_TYPE(op) != (PyTypeObject*)t)) {
op = op->_ob_next;
if (op == &refchain)
return res;
}
if (PyList_Append(res, op) < 0) {
Py_DECREF(res);
return NULL;
}
op = op->_ob_next;
}
return res;
}
#endif
}
......@@ -3403,6 +3403,15 @@ extern "C" int PyType_Ready(PyTypeObject* cls) noexcept {
return 0;
}
#ifdef Py_TRACE_REFS
/* PyType_Ready is the closest thing we have to a choke point
* for type objects, so is the best place I can think of to try
* to get type objects into the doubly-linked list of all objects.
* Still, not all type objects go thru PyType_Ready.
*/
_Py_AddToAllObjects((PyObject*)cls, 0);
#endif
ASSERT(!cls->is_pyston_class, "should not call this on Pyston classes");
_Py_INC_REFTOTAL;
......
......@@ -147,6 +147,12 @@ llvm::Instruction* findInsertionPoint(llvm::BasicBlock* BB, llvm::BasicBlock* fr
}
}
#ifdef Py_TRACE_REFS
#define REFCOUNT_IDX 2
#else
#define REFCOUNT_IDX 0
#endif
void addIncrefs(llvm::Value* v, bool nullable, int num_refs, llvm::Instruction* incref_pt) {
if (num_refs > 1) {
// Not bad but I don't think this should happen:
......@@ -190,8 +196,7 @@ void addIncrefs(llvm::Value* v, bool nullable, int num_refs, llvm::Instruction*
builder.CreateStore(new_reftotal, reftotal_gv);
#endif
llvm::ArrayRef<llvm::Value*> idxs({ getConstantInt(0, g.i32), getConstantInt(0, g.i32) });
auto refcount_ptr = builder.CreateConstInBoundsGEP2_32(v, 0, 0);
auto refcount_ptr = builder.CreateConstInBoundsGEP2_32(v, 0, REFCOUNT_IDX);
auto refcount = builder.CreateLoad(refcount_ptr);
auto new_refcount = builder.CreateAdd(refcount, getConstantInt(num_refs, g.i64));
builder.CreateStore(new_refcount, refcount_ptr);
......@@ -225,7 +230,7 @@ void addDecrefs(llvm::Value* v, bool nullable, int num_refs, llvm::Instruction*
new llvm::StoreInst(new_reftotal, reftotal_gv, decref_pt);
#endif
llvm::ArrayRef<llvm::Value*> idxs({ getConstantInt(0, g.i32), getConstantInt(0, g.i32) });
llvm::ArrayRef<llvm::Value*> idxs({ getConstantInt(0, g.i32), getConstantInt(REFCOUNT_IDX, g.i32) });
auto refcount_ptr = llvm::GetElementPtrInst::CreateInBounds(v, idxs, "", decref_pt);
auto refcount = new llvm::LoadInst(refcount_ptr, "", decref_pt);
auto new_refcount = llvm::BinaryOperator::Create(llvm::BinaryOperator::BinaryOps::Sub, refcount,
......@@ -250,7 +255,14 @@ void addDecrefs(llvm::Value* v, bool nullable, int num_refs, llvm::Instruction*
builder.SetInsertPoint(dealloc_block);
auto cls_ptr = builder.CreateConstInBoundsGEP2_32(v, 0, 1);
#ifdef COUNT_ALLOCS
#error "Don't support COUNT_ALLOCS here yet"
#endif
#ifdef Py_TRACE_REFS
builder.CreateCall(g.funcs._Py_Dealloc, v);
#else
auto cls_ptr = builder.CreateConstInBoundsGEP2_32(v, 0, 1 + REFCOUNT_IDX);
auto cls = builder.CreateLoad(cls_ptr);
auto dtor_ptr = builder.CreateConstInBoundsGEP2_32(cls, 0, 4);
......@@ -261,6 +273,8 @@ void addDecrefs(llvm::Value* v, bool nullable, int num_refs, llvm::Instruction*
#endif
auto dtor = builder.CreateLoad(dtor_ptr);
builder.CreateCall(dtor, v);
#endif
builder.CreateBr(continue_block);
builder.SetInsertPoint(continue_block);
......
......@@ -329,5 +329,9 @@ void initGlobalFuncs(GlobalState& g) {
GET(pow_float_float);
GET(dump);
#ifdef Py_TRACE_REFS
GET(_Py_Dealloc);
#endif
}
}
......@@ -58,6 +58,10 @@ struct GlobalFuncs {
llvm::Value* div_float_float, *floordiv_float_float, *mod_float_float, *pow_float_float;
llvm::Value* dump;
#ifdef Py_TRACE_REFS
llvm::Value* _Py_Dealloc;
#endif
};
}
......
......@@ -603,6 +603,11 @@ inline BoxedString* internStringMortal(llvm::StringRef s) {
}
// TODO this is an immortal intern for now
// Ref usage: this transfers a ref from the passed-in string to the new one.
// Typical usage:
// Py_INCREF(s);
// internStringMortalInplace(s);
// AUTO_DECREF(s);
inline void internStringMortalInplace(BoxedString*& s) noexcept {
PyString_InternInPlace((PyObject**)&s);
}
......@@ -663,6 +668,8 @@ public:
void* operator new(size_t size, BoxedClass* cls) __attribute__((visibility("default")));
void operator delete(void* ptr) __attribute__((visibility("default"))) { abort(); }
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
// Note: cls gets initialized in the new() function.
......@@ -717,6 +724,15 @@ public:
Box* hasnextOrNullIC();
friend class AttrWrapper;
#ifdef Py_TRACE_REFS
static Box createRefchain() {
Box rtn;
rtn._ob_next = &rtn;
rtn._ob_prev = &rtn;
return rtn;
}
#endif
};
static_assert(offsetof(Box, cls) == offsetof(struct _object, ob_type), "");
......
......@@ -73,7 +73,7 @@ static BORROWED(Box*) classLookup(BoxedClassobj* cls, BoxedString* attr, Getattr
return NULL;
}
static Box* classLookup(BoxedClassobj* cls, BoxedString* attr) {
static BORROWED(Box*) classLookup(BoxedClassobj* cls, BoxedString* attr) {
return classLookup<NOT_REWRITABLE>(cls, attr, NULL);
}
......@@ -85,11 +85,14 @@ extern "C" PyObject* _PyInstance_Lookup(PyObject* pinst, PyObject* pname) noexce
BoxedString* name = (BoxedString*)pname;
try {
Py_INCREF(name);
internStringMortalInplace(name);
AUTO_DECREF(name);
Box* v = inst->getattr(name);
if (v == NULL)
v = classLookup(inst->inst_cls, name);
return v;
return xincref(v);
} catch (ExcInfo e) {
setCAPIException(e);
return NULL;
......@@ -189,8 +192,6 @@ Box* classobjCall(Box* _cls, Box* _args, Box* _kwargs) {
Box* init_func = classLookup(cls, init_str);
BoxedInstance* made = new BoxedInstance(cls);
Py_DECREF(made);
Py_RETURN_NONE;
if (init_func) {
Box* init_rtn = runtimeCall(init_func, ArgPassSpec(1, 0, true, true), made, args, kwargs, NULL, NULL);
AUTO_DECREF(init_rtn);
......@@ -213,7 +214,6 @@ extern "C" PyObject* PyInstance_New(PyObject* klass, PyObject* arg, PyObject* kw
}
static Box* classobjGetattribute(Box* _cls, Box* _attr) {
assert(0 && "check refcounting");
RELEASE_ASSERT(_cls->cls == classobj_cls, "");
BoxedClassobj* cls = static_cast<BoxedClassobj*>(_cls);
......@@ -226,12 +226,12 @@ static Box* classobjGetattribute(Box* _cls, Box* _attr) {
return cls->getAttrWrapper();
if (attr->s() == "__bases__")
return cls->bases;
return incref(cls->bases);
if (attr->s() == "__name__") {
if (cls->name)
return cls->name;
return None;
return incref(cls->name);
return incref(None);
}
}
......@@ -365,7 +365,7 @@ static Box* instanceGetattributeSimple(BoxedInstance* inst, BoxedString* attr_st
if (r) {
if (rewrite_args)
rewrite_args->assertReturnConvention(ReturnConvention::HAS_RETURN);
return r;
return incref(r);
}
RewriterVar* r_inst = NULL;
......@@ -388,12 +388,12 @@ static Box* instanceGetattributeSimple(BoxedInstance* inst, BoxedString* attr_st
rewrite_args = NULL;
if (r) {
assert(0 && "erf this was supposed to return a borrowed ref");
Box* rtn = processDescriptor(r, inst, inst->inst_cls);
if (rewrite_args) {
RewriterVar* r_rtn = rewrite_args->rewriter->call(
true, (void*)processDescriptor, grewriter_inst_args.getReturn(ReturnConvention::HAS_RETURN), r_inst,
r_inst_cls);
RewriterVar* r_rtn
= rewrite_args->rewriter->call(true, (void*)processDescriptor,
grewriter_inst_args.getReturn(ReturnConvention::HAS_RETURN), r_inst,
r_inst_cls)->setType(RefType::OWNED);
rewrite_args->setReturn(r_rtn, ReturnConvention::HAS_RETURN);
}
return rtn;
......@@ -439,6 +439,7 @@ static Box* instanceGetattributeWithFallback(BoxedInstance* inst, BoxedString* a
if (getattr) {
getattr = processDescriptor(getattr, inst, inst->inst_cls);
AUTO_DECREF(getattr);
return runtimeCallInternal<CXX, NOT_REWRITABLE>(getattr, NULL, ArgPassSpec(1), attr_str, NULL, NULL, NULL,
NULL);
}
......@@ -449,7 +450,6 @@ static Box* instanceGetattributeWithFallback(BoxedInstance* inst, BoxedString* a
template <Rewritable rewritable>
static Box* _instanceGetattribute(Box* _inst, BoxedString* attr_str, bool raise_on_missing,
GetattrRewriteArgs* rewrite_args) {
assert(0 && "check refcounting");
if (rewritable == NOT_REWRITABLE) {
assert(!rewrite_args);
rewrite_args = NULL;
......@@ -1225,6 +1225,7 @@ static Box* _instanceBinary(Box* _inst, Box* other, BoxedString* attr) {
Box* func = _instanceGetattribute(inst, attr, false);
if (!func)
return incref(NotImplemented);
AUTO_DECREF(func);
return runtimeCall(func, ArgPassSpec(1), other, NULL, NULL, NULL, NULL);
}
......@@ -1470,6 +1471,7 @@ Box* instanceNeg(Box* _inst) {
static BoxedString* neg_str = getStaticString("__neg__");
Box* neg_func = _instanceGetattribute(inst, neg_str, true);
AUTO_DECREF(neg_func);
return runtimeCall(neg_func, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
}
......@@ -1479,6 +1481,7 @@ Box* instancePos(Box* _inst) {
static BoxedString* pos_str = getStaticString("__pos__");
Box* pos_func = _instanceGetattribute(inst, pos_str, true);
AUTO_DECREF(pos_func);
return runtimeCall(pos_func, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
}
......@@ -1488,6 +1491,7 @@ Box* instanceAbs(Box* _inst) {
static BoxedString* abs_str = getStaticString("__abs__");
Box* abs_func = _instanceGetattribute(inst, abs_str, true);
AUTO_DECREF(abs_func);
return runtimeCall(abs_func, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
}
......@@ -1497,6 +1501,7 @@ Box* instanceInvert(Box* _inst) {
static BoxedString* invert_str = getStaticString("__invert__");
Box* invert_func = _instanceGetattribute(inst, invert_str, true);
AUTO_DECREF(invert_func);
return runtimeCall(invert_func, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
}
......@@ -1506,6 +1511,7 @@ Box* instanceTrunc(BoxedInstance* _inst) {
static BoxedString* trunc_str = getStaticString("__trunc__");
Box* trunc_func = _instanceGetattribute(inst, trunc_str, true);
AUTO_DECREF(trunc_func);
return runtimeCall(trunc_func, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
}
......@@ -1517,10 +1523,12 @@ Box* instanceInt(Box* _inst) {
static BoxedString* int_str = getStaticString("__int__");
if (PyObject_HasAttr((PyObject*)inst, int_str)) {
Box* int_func = _instanceGetattribute(inst, int_str, true);
AUTO_DECREF(int_func);
return runtimeCall(int_func, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
}
Box* truncated = instanceTrunc(inst);
AUTO_DECREF(truncated);
/* __trunc__ is specified to return an Integral type, but
int() needs to return an int. */
Box* res = _PyNumber_ConvertIntegralToInt(truncated, "__trunc__ returned non-Integral (type %.200s)");
......@@ -1536,6 +1544,7 @@ Box* instanceLong(Box* _inst) {
static BoxedString* long_str = getStaticString("__long__");
if (PyObject_HasAttr((PyObject*)inst, long_str)) {
Box* long_func = _instanceGetattribute(inst, long_str, true);
AUTO_DECREF(long_func);
return runtimeCall(autoDecref(long_func), ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
}
......@@ -1549,6 +1558,7 @@ Box* instanceFloat(Box* _inst) {
static BoxedString* float_str = getStaticString("__float__");
Box* float_func = _instanceGetattribute(inst, float_str, true);
AUTO_DECREF(float_func);
return runtimeCall(float_func, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
}
......@@ -1558,6 +1568,7 @@ Box* instanceOct(Box* _inst) {
static BoxedString* oct_str = getStaticString("__oct__");
Box* oct_func = _instanceGetattribute(inst, oct_str, true);
AUTO_DECREF(oct_func);
return runtimeCall(oct_func, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
}
......@@ -1567,6 +1578,7 @@ Box* instanceHex(Box* _inst) {
static BoxedString* hex_str = getStaticString("__hex__");
Box* hex_func = _instanceGetattribute(inst, hex_str, true);
AUTO_DECREF(hex_func);
return runtimeCall(hex_func, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
}
......@@ -1581,6 +1593,7 @@ Box* instanceIndex(Box* _inst) {
static BoxedString* index_str = getStaticString("__index__");
Box* index_func = _instanceGetattribute(inst, index_str, true);
AUTO_DECREF(index_func);
return runtimeCall(index_func, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
}
......@@ -1593,6 +1606,7 @@ Box* instanceCall(Box* _inst, Box* _args, Box* _kwargs) {
if (!call_func)
raiseExcHelper(AttributeError, "%s instance has no __call__ method", inst->inst_cls->name->data());
AUTO_DECREF(call_func);
return runtimeCall(call_func, ArgPassSpec(0, 0, true, true), _args, _kwargs, NULL, NULL, NULL);
}
......@@ -1617,7 +1631,7 @@ extern "C" PyObject* PyClass_New(PyObject* bases, PyObject* dict, PyObject* name
extern "C" PyObject* PyClass_Name(PyObject* _classobj) noexcept {
RELEASE_ASSERT(PyClass_Check(_classobj), "");
BoxedClassobj* classobj = (BoxedClassobj*)_classobj;
return classobj->name;
return incref(classobj->name);
}
extern "C" PyObject* PyMethod_New(PyObject* func, PyObject* self, PyObject* klass) noexcept {
......
......@@ -4457,15 +4457,32 @@ extern "C" void Py_Finalize() noexcept {
PyGC_Collect(); // To make sure it creates any static objects
IN_SHUTDOWN = true;
PyOS_FiniInterrupts();
// TODO: we might have to do this in a loop:
PyType_ClearCache();
_PyUnicode_Fini();
PyThreadState_Clear(NULL);
for (auto b : constants) {
Py_DECREF(b);
}
constants.clear();
// May need to run multiple collections to collect everything:
while (PyGC_Collect())
;
while (true) {
int freed = 0;
freed += PyGC_Collect();
// Doing a gc collection can create more constants (and others -- may have to do this whole thing in a loop)
freed += constants.size();
for (auto b : constants) {
Py_DECREF(b);
}
constants.clear();
if (!freed)
break;
}
_Py_ReleaseInternedStrings();
for (auto b : classes) {
if (!PyObject_IS_GC(b)) {
......@@ -4538,6 +4555,11 @@ extern "C" void Py_Finalize() noexcept {
if (VERBOSITY())
PRINT_TOTAL_REFS();
#ifdef Py_REF_DEBUG
#ifdef Py_TRACE_REFS
if (_Py_RefTotal > 0)
_Py_PrintReferenceAddressesCapped(stderr, 10);
#endif
RELEASE_ASSERT(_Py_RefTotal == 0, "%ld refs remaining!", _Py_RefTotal);
#endif
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment