Skip to content

Commit 5bba97e

Browse files
committed
Clean up dead weakrefs to sqlite3 Blob objects
1 parent 4e15b8d commit 5bba97e

File tree

2 files changed

+43
-0
lines changed

2 files changed

+43
-0
lines changed

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

@@ -143,6 +144,7 @@ class _sqlite3.Connection "pysqlite_Connection *" "clinic_state()->ConnectionTyp
143144
[clinic start generated code]*/
144145
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=67369db2faf80891]*/
145146

147+
static int _pysqlite_drop_unused_blob_references(pysqlite_Connection* self);
146148
static void incref_callback_context(callback_context *ctx);
147149
static void decref_callback_context(callback_context *ctx);
148150
static void set_callback_context(callback_context **ctx_pp,
@@ -300,6 +302,7 @@ pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database,
300302
self->thread_ident = PyThread_get_thread_ident();
301303
self->statement_cache = statement_cache;
302304
self->blobs = blobs;
305+
self->created_blobs = 0;
303306
self->row_factory = Py_NewRef(Py_None);
304307
self->text_factory = Py_NewRef(&PyUnicode_Type);
305308
self->trace_ctx = NULL;
@@ -624,6 +627,10 @@ blobopen_impl(pysqlite_Connection *self, const char *table, const char *col,
624627
goto error;
625628
}
626629

630+
if (_pysqlite_drop_unused_blob_references(self) < 0) {
631+
goto error;
632+
}
633+
627634
return (PyObject *)obj;
628635

629636
error:
@@ -1049,6 +1056,38 @@ final_callback(sqlite3_context *context)
10491056
PyGILState_Release(threadstate);
10501057
}
10511058

1059+
static int
1060+
_pysqlite_drop_unused_blob_references(pysqlite_Connection* self)
1061+
{
1062+
/* we only need to do this once in a while */
1063+
if (self->created_blobs++ < 200) {
1064+
return 0;
1065+
}
1066+
1067+
self->created_blobs = 0;
1068+
1069+
PyObject* new_list = PyList_New(0);
1070+
if (!new_list) {
1071+
return -1;
1072+
}
1073+
1074+
assert(PyList_CheckExact(self->blobs));
1075+
Py_ssize_t imax = PyList_GET_SIZE(self->blobs);
1076+
for (Py_ssize_t i = 0; i < imax; i++) {
1077+
PyObject* weakref = PyList_GET_ITEM(self->blobs, i);
1078+
if (_PyWeakref_IsDead(weakref)) {
1079+
continue;
1080+
}
1081+
if (PyList_Append(new_list, weakref) != 0) {
1082+
Py_DECREF(new_list);
1083+
return -1;
1084+
}
1085+
}
1086+
1087+
Py_SETREF(self->blobs, new_list);
1088+
return 0;
1089+
}
1090+
10521091
/* Allocate a UDF/callback context structure. In order to ensure that the state
10531092
* pointer always outlives the callback context, we make sure it owns a
10541093
* 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)