Skip to content

Commit fe7f1e3

Browse files
committed
Clean up dead weakrefs to sqlite3 Blob objects
1 parent 575fe39 commit fe7f1e3

2 files changed

Lines changed: 43 additions & 0 deletions

File tree

Modules/_sqlite/connection.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1()
3939
#include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing()
4040
#include "pycore_unicodeobject.h" // _PyUnicode_AsUTF8NoNUL
41+
#include "pycore_weakref.h"
4142

4243
#include <stdbool.h>
4344

@@ -148,6 +149,7 @@ class _sqlite3.Connection "pysqlite_Connection *" "clinic_state()->ConnectionTyp
148149
[clinic start generated code]*/
149150
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=67369db2faf80891]*/
150151

152+
static int _pysqlite_drop_unused_blob_references(pysqlite_Connection* self);
151153
static void incref_callback_context(callback_context *ctx);
152154
static void decref_callback_context(callback_context *ctx);
153155
static void set_callback_context(callback_context **ctx_pp,
@@ -305,6 +307,7 @@ pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database,
305307
self->thread_ident = PyThread_get_thread_ident();
306308
self->statement_cache = statement_cache;
307309
self->blobs = blobs;
310+
self->created_blobs = 0;
308311
self->row_factory = Py_NewRef(Py_None);
309312
self->text_factory = Py_NewRef(&PyUnicode_Type);
310313
self->trace_ctx = NULL;
@@ -670,6 +673,10 @@ blobopen_impl(pysqlite_Connection *self, const char *table, const char *col,
670673
goto error;
671674
}
672675

676+
if (_pysqlite_drop_unused_blob_references(self) < 0) {
677+
goto error;
678+
}
679+
673680
return (PyObject *)obj;
674681

675682
error:
@@ -1095,6 +1102,38 @@ final_callback(sqlite3_context *context)
10951102
PyGILState_Release(threadstate);
10961103
}
10971104

1105+
static int
1106+
_pysqlite_drop_unused_blob_references(pysqlite_Connection* self)
1107+
{
1108+
/* we only need to do this once in a while */
1109+
if (self->created_blobs++ < 200) {
1110+
return 0;
1111+
}
1112+
1113+
self->created_blobs = 0;
1114+
1115+
PyObject* new_list = PyList_New(0);
1116+
if (!new_list) {
1117+
return -1;
1118+
}
1119+
1120+
assert(PyList_CheckExact(self->blobs));
1121+
Py_ssize_t imax = PyList_GET_SIZE(self->blobs);
1122+
for (Py_ssize_t i = 0; i < imax; i++) {
1123+
PyObject* weakref = PyList_GET_ITEM(self->blobs, i);
1124+
if (_PyWeakref_IsDead(weakref)) {
1125+
continue;
1126+
}
1127+
if (PyList_Append(new_list, weakref) != 0) {
1128+
Py_DECREF(new_list);
1129+
return -1;
1130+
}
1131+
}
1132+
1133+
Py_SETREF(self->blobs, new_list);
1134+
return 0;
1135+
}
1136+
10981137
/* Allocate a UDF/callback context structure. In order to ensure that the state
10991138
* pointer always outlives the callback context, we make sure it owns a
11001139
* reference to the module itself. create_callback_context() is always called

Modules/_sqlite/connection.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ typedef struct
7373
/* Lists of weak references to blobs used within this connection */
7474
PyObject *blobs;
7575

76+
/* Counter for how many blobs were opened in this connection;
77+
* May be reset to 0 at certain intervals. */
78+
int created_blobs;
79+
7680
PyObject* row_factory;
7781

7882
/* Determines how bytestrings from SQLite are converted to Python objects:

0 commit comments

Comments
 (0)