--- Modules/gcmodule.c~ 2004-10-30 09:59:28.239441892 -0400 +++ Modules/gcmodule.c 2004-10-30 10:50:20.574449583 -0400 @@ -527,19 +527,22 @@ } /* Now that all weakrefs to trash have been cleared, it's safe to - * decref the callbacks we decided to ignore. + * decref the callbacks we decided to ignore. We cannot invoke + * them because they may be able to reference objects in `unreachable`. */ while (! gc_list_is_empty(&wrcb_to_kill)) { gc = wrcb_to_kill.gc.gc_next; op = FROM_GC(gc); assert(IS_REACHABLE(op)); assert(PyWeakref_Check(op)); - assert(((PyWeakReference *)op)->wr_callback != NULL); + wr = (PyWeakReference *)op; + assert(wr->wr_callback != NULL); - /* Give up the reference we created in the first pass. This - * may cause the weakref to be deallocated, in which case - * its tp_dealloc will decref the callback too. + /* Clear the callback and give up the reference we created in + * the first pass. This may cause the callback or weakref to + * be deallocated. */ + Py_CLEAR(wr->wr_callback); Py_DECREF(op); if (wrcb_to_kill.gc.gc_next == gc) { /* object is still alive -- move it */ @@ -550,7 +553,9 @@ ++num_freed; } - /* Finally, invoke the callbacks we decided to honor. */ + /* Finally, invoke the callbacks we decided to honor. It's safe to + * invoke them because they cannot reference objects in `unreachable`. + */ while (! gc_list_is_empty(&wrcb_to_call)) { PyObject *temp; PyObject *callback; @@ -561,16 +566,20 @@ assert(PyWeakref_Check(op)); wr = (PyWeakReference *)op; callback = wr->wr_callback; - assert(callback != NULL); - /* copy-paste of weakrefobject.c's handle_callback() */ - temp = PyObject_CallFunction(callback, "O", wr); - if (temp == NULL) - PyErr_WriteUnraisable(callback); - else - Py_DECREF(temp); + /* We need to check if callback is NULL here because another + * callback might have caused it to be cleared. */ + if (callback != NULL) { + /* copy-paste of weakrefobject.c's handle_callback() */ + temp = PyObject_CallFunction(callback, "O", wr); + if (temp == NULL) + PyErr_WriteUnraisable(callback); + else + Py_DECREF(temp); - Py_DECREF(op); + Py_CLEAR(wr->wr_callback); + } + Py_DECREF(wr); if (wrcb_to_call.gc.gc_next == gc) { /* object is still alive -- move it */ gc_list_remove(gc);