diff --git a/Modules/_sqlite/cache.c b/Modules/_sqlite/cache.c index 8cedd07b4596df..fffe6c08a13346 100644 --- a/Modules/_sqlite/cache.c +++ b/Modules/_sqlite/cache.c @@ -84,8 +84,18 @@ int pysqlite_cache_init(pysqlite_Cache* self, PyObject* args, PyObject* kwargs) Py_INCREF(factory); self->factory = factory; - self->decref_factory = 1; + return 0; +} +static int cache_traverse(pysqlite_Cache *self, visitproc visit, void *arg) +{ + Py_VISIT(self->factory); + return 0; +} + +static int cache_clear(pysqlite_Cache *self) +{ + Py_CLEAR(self->factory); return 0; } @@ -95,6 +105,8 @@ void pysqlite_cache_dealloc(pysqlite_Cache* self) pysqlite_Node* node; pysqlite_Node* delete_node; + PyObject_GC_UnTrack(self); + if (!self->factory) { /* constructor failed, just get out of here */ return; @@ -108,11 +120,9 @@ void pysqlite_cache_dealloc(pysqlite_Cache* self) Py_DECREF(delete_node); } - if (self->decref_factory) { - Py_DECREF(self->factory); - } Py_DECREF(self->mapping); + tp->tp_clear((PyObject *)self); tp->tp_free(self); Py_DECREF(tp); } @@ -283,15 +293,16 @@ static PyMethodDef cache_methods[] = { static PyType_Slot cache_slots[] = { {Py_tp_dealloc, pysqlite_cache_dealloc}, {Py_tp_methods, cache_methods}, - {Py_tp_new, PyType_GenericNew}, {Py_tp_init, pysqlite_cache_init}, + {Py_tp_traverse, cache_traverse}, + {Py_tp_clear, cache_clear}, {0, NULL}, }; static PyType_Spec cache_spec = { .name = MODULE_NAME ".Cache", .basicsize = sizeof(pysqlite_Cache), - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, .slots = cache_slots, }; PyTypeObject *pysqlite_CacheType = NULL; diff --git a/Modules/_sqlite/cache.h b/Modules/_sqlite/cache.h index 0afdf7f09b65c7..6bcc7463dc3686 100644 --- a/Modules/_sqlite/cache.h +++ b/Modules/_sqlite/cache.h @@ -53,10 +53,6 @@ typedef struct pysqlite_Node* first; pysqlite_Node* last; - - /* if set, decrement the factory function when the Cache is deallocated. - * this is almost always desirable, but not in the pysqlite context */ - int decref_factory; } pysqlite_Cache; extern PyTypeObject *pysqlite_NodeType; diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 75aec74e0aaa92..36853fbd4d2e82 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -155,14 +155,6 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject return -1; } - /* By default, the Cache class INCREFs the factory in its initializer, and - * decrefs it in its deallocator method. Since this would create a circular - * reference here, we're breaking it by decrementing self, and telling the - * cache class to not decref the factory (self) in its deallocator. - */ - self->statement_cache->decref_factory = 0; - Py_DECREF(self); - self->detect_types = detect_types; self->timeout = timeout; (void)sqlite3_busy_timeout(self->db, (int)(timeout*1000)); @@ -225,11 +217,23 @@ void pysqlite_do_all_statements(pysqlite_Connection* self, int action, int reset } } +static int connection_traverse(pysqlite_Connection *self, visitproc visit, void *arg) +{ + Py_VISIT(self->statement_cache); + return 0; +} + +static int connection_clear(pysqlite_Connection *self) +{ + Py_CLEAR(self->statement_cache); + return 0; +} + void pysqlite_connection_dealloc(pysqlite_Connection* self) { PyTypeObject *tp = Py_TYPE(self); - Py_XDECREF(self->statement_cache); + PyObject_GC_UnTrack(self); /* Clean up if user has not called .close() explicitly. */ if (self->db) { @@ -246,6 +250,7 @@ void pysqlite_connection_dealloc(pysqlite_Connection* self) Py_XDECREF(self->statements); Py_XDECREF(self->cursors); + tp->tp_clear((PyObject *)self); tp->tp_free(self); Py_DECREF(tp); } @@ -1920,16 +1925,17 @@ static PyType_Slot connection_slots[] = { {Py_tp_methods, connection_methods}, {Py_tp_members, connection_members}, {Py_tp_getset, connection_getset}, - {Py_tp_new, PyType_GenericNew}, {Py_tp_init, pysqlite_connection_init}, {Py_tp_call, pysqlite_connection_call}, + {Py_tp_traverse, connection_traverse}, + {Py_tp_clear, connection_clear}, {0, NULL}, }; static PyType_Spec connection_spec = { .name = MODULE_NAME ".Connection", .basicsize = sizeof(pysqlite_Connection), - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, .slots = connection_slots, };