From 83a05d5ee71eaca8feb2782ef989faec0d7d66f6 Mon Sep 17 00:00:00 2001 From: Duane Griffin Date: Thu, 17 Apr 2025 13:31:44 +1200 Subject: [PATCH 1/2] gh-127081: add critical sections to dbm objects The dbm_* functions are not thread-safe, naturally. Add critical sections to protect their use. --- ...-04-21-01-05-14.gh-issue-127081.Egrpq7.rst | 2 + Modules/_dbmmodule.c | 102 +++++++++++++----- Modules/_gdbmmodule.c | 99 ++++++++++++----- Modules/clinic/_dbmmodule.c.h | 37 +++++-- Modules/clinic/_gdbmmodule.c.h | 70 +++++++++--- 5 files changed, 234 insertions(+), 76 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-04-21-01-05-14.gh-issue-127081.Egrpq7.rst diff --git a/Misc/NEWS.d/next/Library/2025-04-21-01-05-14.gh-issue-127081.Egrpq7.rst b/Misc/NEWS.d/next/Library/2025-04-21-01-05-14.gh-issue-127081.Egrpq7.rst new file mode 100644 index 00000000000000..30643673bf9a3f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-04-21-01-05-14.gh-issue-127081.Egrpq7.rst @@ -0,0 +1,2 @@ +Fix libc thread safety issues with :mod:`dbm` by performing stateful +operations in critical sections. diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index cc65cbd98d71dc..b293b9ee0b9f6a 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -68,6 +68,7 @@ typedef struct { #include "clinic/_dbmmodule.c.h" +/* NOTE: Must be used within a critical section! */ #define check_dbmobject_open(v, err) \ if ((v)->di_dbm == NULL) { \ PyErr_SetString(err, "DBM object has already been closed"); \ @@ -118,12 +119,15 @@ dbm_dealloc(PyObject *self) static Py_ssize_t dbm_length(PyObject *self) { + Py_ssize_t result = -1; dbmobject *dp = dbmobject_CAST(self); _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); assert(state != NULL); + + Py_BEGIN_CRITICAL_SECTION(self); if (dp->di_dbm == NULL) { PyErr_SetString(state->dbm_error, "DBM object has already been closed"); - return -1; + goto done; } if ( dp->di_size < 0 ) { datum key; @@ -135,39 +139,51 @@ dbm_length(PyObject *self) size++; dp->di_size = size; } - return dp->di_size; + result = dp->di_size; +done:; + Py_END_CRITICAL_SECTION(); + return result; } static int dbm_bool(PyObject *self) { + int result; dbmobject *dp = dbmobject_CAST(self); _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); assert(state != NULL); + Py_BEGIN_CRITICAL_SECTION(self); if (dp->di_dbm == NULL) { PyErr_SetString(state->dbm_error, "DBM object has already been closed"); - return -1; + result = -1; + goto done; } if (dp->di_size > 0) { /* Known non-zero size. */ - return 1; + result = 1; + goto done; } if (dp->di_size == 0) { /* Known zero size. */ - return 0; + result = 0; + goto done; } /* Unknown size. Ensure DBM object has an entry. */ datum key = dbm_firstkey(dp->di_dbm); if (key.dptr == NULL) { /* Empty. Cache this fact. */ - dp->di_size = 0; - return 0; + result = dp->di_size = 0; + goto done; } + /* Non-empty. Don't cache the length since we don't know. */ - return 1; + result = 1; +done:; + Py_END_CRITICAL_SECTION(); + return result; } static PyObject * @@ -175,26 +191,35 @@ dbm_subscript(PyObject *self, PyObject *key) { datum drec, krec; Py_ssize_t tmp_size; + PyObject *result = NULL; dbmobject *dp = dbmobject_CAST(self); _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); assert(state != NULL); if (!PyArg_Parse(key, "s#", &krec.dptr, &tmp_size)) { return NULL; } - krec.dsize = tmp_size; - check_dbmobject_open(dp, state->dbm_error); + + Py_BEGIN_CRITICAL_SECTION(self); + /* Can't use the macro here as it returns. */ + if (dp->di_dbm == NULL) { + PyErr_SetString(state->dbm_error, "DBM object has already been closed"); + goto done; + } drec = dbm_fetch(dp->di_dbm, krec); if ( drec.dptr == 0 ) { PyErr_SetObject(PyExc_KeyError, key); - return NULL; + goto done; } if ( dbm_error(dp->di_dbm) ) { dbm_clearerr(dp->di_dbm); PyErr_SetString(state->dbm_error, ""); - return NULL; + goto done; } - return PyBytes_FromStringAndSize(drec.dptr, drec.dsize); + result = PyBytes_FromStringAndSize(drec.dptr, drec.dsize); +done:; + Py_END_CRITICAL_SECTION(); + return result; } static int @@ -202,6 +227,7 @@ dbm_ass_sub(PyObject *self, PyObject *v, PyObject *w) { datum krec, drec; Py_ssize_t tmp_size; + int result = -1; dbmobject *dp = dbmobject_CAST(self); if ( !PyArg_Parse(v, "s#", &krec.dptr, &tmp_size) ) { @@ -212,10 +238,13 @@ dbm_ass_sub(PyObject *self, PyObject *v, PyObject *w) _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); assert(state != NULL); krec.dsize = tmp_size; + + Py_BEGIN_CRITICAL_SECTION(self); if (dp->di_dbm == NULL) { - PyErr_SetString(state->dbm_error, "DBM object has already been closed"); - return -1; + PyErr_SetString(state->dbm_error, "DBM object has already been closed"); + goto done; } + dp->di_size = -1; if (w == NULL) { if ( dbm_delete(dp->di_dbm, krec) < 0 ) { @@ -228,31 +257,36 @@ dbm_ass_sub(PyObject *self, PyObject *v, PyObject *w) else { PyErr_SetString(state->dbm_error, "cannot delete item from database"); } - return -1; + goto done; } } else { if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) { PyErr_SetString(PyExc_TypeError, "dbm mappings have bytes or string elements only"); - return -1; + goto done; } drec.dsize = tmp_size; if ( dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) { dbm_clearerr(dp->di_dbm); PyErr_SetString(state->dbm_error, "cannot add item to database"); - return -1; + goto done; } } if ( dbm_error(dp->di_dbm) ) { dbm_clearerr(dp->di_dbm); PyErr_SetString(state->dbm_error, ""); - return -1; + goto done; } - return 0; + + result = 0; +done:; + Py_END_CRITICAL_SECTION(); + return result; } /*[clinic input] +@critical_section _dbm.dbm.close Close the database. @@ -260,7 +294,7 @@ Close the database. static PyObject * _dbm_dbm_close_impl(dbmobject *self) -/*[clinic end generated code: output=c8dc5b6709600b86 input=046db72377d51be8]*/ +/*[clinic end generated code: output=c8dc5b6709600b86 input=4a94f79facbc28ca]*/ { if (self->di_dbm) { dbm_close(self->di_dbm); @@ -270,6 +304,7 @@ _dbm_dbm_close_impl(dbmobject *self) } /*[clinic input] +@critical_section _dbm.dbm.keys cls: defining_class @@ -279,7 +314,7 @@ Return a list of all keys in the database. static PyObject * _dbm_dbm_keys_impl(dbmobject *self, PyTypeObject *cls) -/*[clinic end generated code: output=f2a593b3038e5996 input=d3706a28fc051097]*/ +/*[clinic end generated code: output=f2a593b3038e5996 input=6ddefeadf2a80156]*/ { PyObject *v, *item; datum key; @@ -315,35 +350,42 @@ dbm_contains(PyObject *self, PyObject *arg) dbmobject *dp = dbmobject_CAST(self); datum key, val; Py_ssize_t size; + int result = -1; _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); assert(state != NULL); + + Py_BEGIN_CRITICAL_SECTION(self); if ((dp)->di_dbm == NULL) { PyErr_SetString(state->dbm_error, "DBM object has already been closed"); - return -1; + goto done; } if (PyUnicode_Check(arg)) { key.dptr = (char *)PyUnicode_AsUTF8AndSize(arg, &size); key.dsize = size; if (key.dptr == NULL) - return -1; + goto done; } else if (!PyBytes_Check(arg)) { PyErr_Format(PyExc_TypeError, "dbm key must be bytes or string, not %.100s", Py_TYPE(arg)->tp_name); - return -1; + goto done; } else { key.dptr = PyBytes_AS_STRING(arg); key.dsize = PyBytes_GET_SIZE(arg); } val = dbm_fetch(dp->di_dbm, key); - return val.dptr != NULL; + result = val.dptr != NULL; +done:; + Py_END_CRITICAL_SECTION(); + return result; } /*[clinic input] +@critical_section _dbm.dbm.get cls: defining_class key: str(accept={str, robuffer}, zeroes=True) @@ -356,7 +398,7 @@ Return the value for key if present, otherwise default. static PyObject * _dbm_dbm_get_impl(dbmobject *self, PyTypeObject *cls, const char *key, Py_ssize_t key_length, PyObject *default_value) -/*[clinic end generated code: output=b4e55f8b6d482bc4 input=66b993b8349fa8c1]*/ +/*[clinic end generated code: output=b4e55f8b6d482bc4 input=1d88a22bb5e55202]*/ { datum dbm_key, val; _dbm_state *state = PyType_GetModuleState(cls); @@ -373,6 +415,7 @@ _dbm_dbm_get_impl(dbmobject *self, PyTypeObject *cls, const char *key, } /*[clinic input] +@critical_section _dbm.dbm.setdefault cls: defining_class key: str(accept={str, robuffer}, zeroes=True) @@ -387,7 +430,7 @@ If key is not in the database, it is inserted with default as the value. static PyObject * _dbm_dbm_setdefault_impl(dbmobject *self, PyTypeObject *cls, const char *key, Py_ssize_t key_length, PyObject *default_value) -/*[clinic end generated code: output=9c2f6ea6d0fb576c input=126a3ff15c5f8232]*/ +/*[clinic end generated code: output=9c2f6ea6d0fb576c input=c01510ef7571e13b]*/ { datum dbm_key, val; Py_ssize_t tmp_size; @@ -427,6 +470,7 @@ _dbm_dbm_setdefault_impl(dbmobject *self, PyTypeObject *cls, const char *key, } /*[clinic input] +@critical_section _dbm.dbm.clear cls: defining_class / @@ -436,7 +480,7 @@ Remove all items from the database. static PyObject * _dbm_dbm_clear_impl(dbmobject *self, PyTypeObject *cls) -/*[clinic end generated code: output=8d126b9e1d01a434 input=43aa6ca1acb7f5f5]*/ +/*[clinic end generated code: output=8d126b9e1d01a434 input=a1aa5d99adfb9656]*/ { _dbm_state *state = PyType_GetModuleState(cls); assert(state != NULL); diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index ab2ebdba9249bf..7f9afabf652b88 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -80,6 +80,7 @@ typedef struct { #include "clinic/_gdbmmodule.c.h" +/* NOTE: Must be used within a critical section! */ #define check_gdbmobject_open(v, err) \ if ((v)->di_dbm == NULL) { \ PyErr_SetString(err, "GDBM object has already been closed"); \ @@ -144,8 +145,10 @@ gdbm_dealloc(PyObject *op) static Py_ssize_t gdbm_length(PyObject *op) { + Py_ssize_t result = -1; gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); + Py_BEGIN_CRITICAL_SECTION(op); if (dp->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); return -1; @@ -161,11 +164,11 @@ gdbm_length(PyObject *op) else { set_gdbm_error(state, "gdbm_count() error"); } - return -1; + goto done; } if (count > PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "count exceeds PY_SSIZE_T_MAX"); - return -1; + goto done; } dp->di_size = count; #else @@ -185,37 +188,50 @@ gdbm_length(PyObject *op) dp->di_size = size; #endif } - return dp->di_size; + result = dp->di_size; +done:; + Py_END_CRITICAL_SECTION(); + return result; } static int gdbm_bool(PyObject *op) { + int result; gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); + + Py_BEGIN_CRITICAL_SECTION(op); if (dp->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); - return -1; + result = -1; + goto done; } if (dp->di_size > 0) { /* Known non-zero size. */ - return 1; + result = 1; + goto done; } if (dp->di_size == 0) { /* Known zero size. */ - return 0; + result = 0; + goto done; } /* Unknown size. Ensure DBM object has an entry. */ datum key = gdbm_firstkey(dp->di_dbm); if (key.dptr == NULL) { /* Empty. Cache this fact. */ - dp->di_size = 0; - return 0; + result = dp->di_size = 0; + goto done; } /* Non-empty. Don't cache the length since we don't know. */ free(key.dptr); - return 1; + result = 1; + +done:; + Py_END_CRITICAL_SECTION(); + return result; } // Wrapper function for PyArg_Parse(o, "s#", &d.dptr, &d.size). @@ -242,7 +258,7 @@ parse_datum(PyObject *o, datum *d, const char *failmsg) static PyObject * gdbm_subscript(PyObject *op, PyObject *key) { - PyObject *v; + PyObject *v = NULL; datum drec, krec; gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); @@ -250,18 +266,22 @@ gdbm_subscript(PyObject *op, PyObject *key) if (!parse_datum(key, &krec, NULL)) { return NULL; } + + Py_BEGIN_CRITICAL_SECTION(op); if (dp->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); - return NULL; + goto done; } drec = gdbm_fetch(dp->di_dbm, krec); if (drec.dptr == 0) { PyErr_SetObject(PyExc_KeyError, key); - return NULL; + goto done; } v = PyBytes_FromStringAndSize(drec.dptr, drec.dsize); free(drec.dptr); +done:; + Py_END_CRITICAL_SECTION(); return v; } @@ -294,16 +314,19 @@ gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w) { datum krec, drec; const char *failmsg = "gdbm mappings have bytes or string indices only"; + int result = -1; gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); if (!parse_datum(v, &krec, failmsg)) { return -1; } + + Py_BEGIN_CRITICAL_SECTION(op); if (dp->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); - return -1; + goto done; } dp->di_size = -1; if (w == NULL) { @@ -314,12 +337,12 @@ gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w) else { set_gdbm_error(state, "gdbm_delete() error"); } - return -1; + goto done; } } else { if (!parse_datum(w, &drec, failmsg)) { - return -1; + goto done; } errno = 0; if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0) { @@ -329,13 +352,18 @@ gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w) else { set_gdbm_error(state, "gdbm_store() error"); } - return -1; + goto done; } } - return 0; + result = 0; + +done:; + Py_END_CRITICAL_SECTION(); + return result; } /*[clinic input] +@critical_section _gdbm.gdbm.setdefault key: object @@ -348,7 +376,7 @@ Get value for key, or set it to default and return default if not present. static PyObject * _gdbm_gdbm_setdefault_impl(gdbmobject *self, PyObject *key, PyObject *default_value) -/*[clinic end generated code: output=f3246e880509f142 input=0db46b69e9680171]*/ +/*[clinic end generated code: output=f3246e880509f142 input=854374cd81ab51b6]*/ { PyObject *res; @@ -363,6 +391,7 @@ _gdbm_gdbm_setdefault_impl(gdbmobject *self, PyObject *key, } /*[clinic input] +@critical_section _gdbm.gdbm.close Close the database. @@ -370,7 +399,7 @@ Close the database. static PyObject * _gdbm_gdbm_close_impl(gdbmobject *self) -/*[clinic end generated code: output=f5abb4d6bb9e52d5 input=0a203447379b45fd]*/ +/*[clinic end generated code: output=f5abb4d6bb9e52d5 input=56b604f4e77f533d]*/ { if (self->di_dbm) { gdbm_close(self->di_dbm); @@ -381,6 +410,7 @@ _gdbm_gdbm_close_impl(gdbmobject *self) /* XXX Should return a set or a set view */ /*[clinic input] +@critical_section _gdbm.gdbm.keys cls: defining_class @@ -390,7 +420,7 @@ Get a list of all keys in the database. static PyObject * _gdbm_gdbm_keys_impl(gdbmobject *self, PyTypeObject *cls) -/*[clinic end generated code: output=c24b824e81404755 input=1428b7c79703d7d5]*/ +/*[clinic end generated code: output=c24b824e81404755 input=785988b1ea8f77e0]*/ { PyObject *v, *item; datum key, nextkey; @@ -437,33 +467,40 @@ gdbm_contains(PyObject *self, PyObject *arg) gdbmobject *dp = (gdbmobject *)self; datum key; Py_ssize_t size; + int result = -1; _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); + Py_BEGIN_CRITICAL_SECTION(self); if ((dp)->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); - return -1; + goto done; } if (PyUnicode_Check(arg)) { key.dptr = (char *)PyUnicode_AsUTF8AndSize(arg, &size); key.dsize = size; if (key.dptr == NULL) - return -1; + goto done; } else if (!PyBytes_Check(arg)) { PyErr_Format(PyExc_TypeError, "gdbm key must be bytes or string, not %.100s", Py_TYPE(arg)->tp_name); - return -1; + goto done; } else { key.dptr = PyBytes_AS_STRING(arg); key.dsize = PyBytes_GET_SIZE(arg); } - return gdbm_exists(dp->di_dbm, key); + result = gdbm_exists(dp->di_dbm, key); + +done:; + Py_END_CRITICAL_SECTION(); + return result; } /*[clinic input] +@critical_section _gdbm.gdbm.firstkey cls: defining_class @@ -477,7 +514,7 @@ hash values, and won't be sorted by the key values. static PyObject * _gdbm_gdbm_firstkey_impl(gdbmobject *self, PyTypeObject *cls) -/*[clinic end generated code: output=139275e9c8b60827 input=ed8782a029a5d299]*/ +/*[clinic end generated code: output=139275e9c8b60827 input=aad5a7c886c542f5]*/ { PyObject *v; datum key; @@ -497,6 +534,7 @@ _gdbm_gdbm_firstkey_impl(gdbmobject *self, PyTypeObject *cls) } /*[clinic input] +@critical_section _gdbm.gdbm.nextkey cls: defining_class @@ -517,7 +555,7 @@ to create a list in memory that contains them all: static PyObject * _gdbm_gdbm_nextkey_impl(gdbmobject *self, PyTypeObject *cls, const char *key, Py_ssize_t key_length) -/*[clinic end generated code: output=c81a69300ef41766 input=365e297bc0b3db48]*/ +/*[clinic end generated code: output=c81a69300ef41766 input=181f1130d5bfeb1e]*/ { PyObject *v; datum dbm_key, nextkey; @@ -539,6 +577,7 @@ _gdbm_gdbm_nextkey_impl(gdbmobject *self, PyTypeObject *cls, const char *key, } /*[clinic input] +@critical_section _gdbm.gdbm.reorganize cls: defining_class @@ -554,7 +593,7 @@ kept and reused as new (key,value) pairs are added. static PyObject * _gdbm_gdbm_reorganize_impl(gdbmobject *self, PyTypeObject *cls) -/*[clinic end generated code: output=d77c69e8e3dd644a input=e1359faeef844e46]*/ +/*[clinic end generated code: output=d77c69e8e3dd644a input=3e3ca0d2ea787861]*/ { _gdbm_state *state = PyType_GetModuleState(cls); assert(state != NULL); @@ -573,6 +612,7 @@ _gdbm_gdbm_reorganize_impl(gdbmobject *self, PyTypeObject *cls) } /*[clinic input] +@critical_section _gdbm.gdbm.sync cls: defining_class @@ -585,7 +625,7 @@ any unwritten data to be written to the disk. static PyObject * _gdbm_gdbm_sync_impl(gdbmobject *self, PyTypeObject *cls) -/*[clinic end generated code: output=bb680a2035c3f592 input=3d749235f79b6f2a]*/ +/*[clinic end generated code: output=bb680a2035c3f592 input=6054385b071d238a]*/ { _gdbm_state *state = PyType_GetModuleState(cls); assert(state != NULL); @@ -595,6 +635,7 @@ _gdbm_gdbm_sync_impl(gdbmobject *self, PyTypeObject *cls) } /*[clinic input] +@critical_section _gdbm.gdbm.clear cls: defining_class / @@ -604,7 +645,7 @@ Remove all items from the database. static PyObject * _gdbm_gdbm_clear_impl(gdbmobject *self, PyTypeObject *cls) -/*[clinic end generated code: output=673577c573318661 input=34136d52fcdd4210]*/ +/*[clinic end generated code: output=673577c573318661 input=b17467adfe62f23d]*/ { _gdbm_state *state = PyType_GetModuleState(cls); assert(state != NULL); diff --git a/Modules/clinic/_dbmmodule.c.h b/Modules/clinic/_dbmmodule.c.h index 5e503194408776..091ce9edc43d4b 100644 --- a/Modules/clinic/_dbmmodule.c.h +++ b/Modules/clinic/_dbmmodule.c.h @@ -5,6 +5,7 @@ preserve #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) # include "pycore_runtime.h" // _Py_SINGLETON() #endif +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(_dbm_dbm_close__doc__, @@ -22,7 +23,13 @@ _dbm_dbm_close_impl(dbmobject *self); static PyObject * _dbm_dbm_close(PyObject *self, PyObject *Py_UNUSED(ignored)) { - return _dbm_dbm_close_impl((dbmobject *)self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _dbm_dbm_close_impl((dbmobject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_dbm_dbm_keys__doc__, @@ -40,11 +47,18 @@ _dbm_dbm_keys_impl(dbmobject *self, PyTypeObject *cls); static PyObject * _dbm_dbm_keys(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { + PyObject *return_value = NULL; + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { PyErr_SetString(PyExc_TypeError, "keys() takes no arguments"); - return NULL; + goto exit; } - return _dbm_dbm_keys_impl((dbmobject *)self, cls); + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _dbm_dbm_keys_impl((dbmobject *)self, cls); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; } PyDoc_STRVAR(_dbm_dbm_get__doc__, @@ -85,7 +99,9 @@ _dbm_dbm_get(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_ &key, &key_length, &default_value)) { goto exit; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = _dbm_dbm_get_impl((dbmobject *)self, cls, key, key_length, default_value); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -131,7 +147,9 @@ _dbm_dbm_setdefault(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py &key, &key_length, &default_value)) { goto exit; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = _dbm_dbm_setdefault_impl((dbmobject *)self, cls, key, key_length, default_value); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -152,11 +170,18 @@ _dbm_dbm_clear_impl(dbmobject *self, PyTypeObject *cls); static PyObject * _dbm_dbm_clear(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { + PyObject *return_value = NULL; + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { PyErr_SetString(PyExc_TypeError, "clear() takes no arguments"); - return NULL; + goto exit; } - return _dbm_dbm_clear_impl((dbmobject *)self, cls); + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _dbm_dbm_clear_impl((dbmobject *)self, cls); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; } PyDoc_STRVAR(dbmopen__doc__, @@ -221,4 +246,4 @@ dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=3b456118f231b160 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=279511ea7cda38dd input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_gdbmmodule.c.h b/Modules/clinic/_gdbmmodule.c.h index 00950f18e53541..6fd6aa3da50335 100644 --- a/Modules/clinic/_gdbmmodule.c.h +++ b/Modules/clinic/_gdbmmodule.c.h @@ -5,6 +5,7 @@ preserve #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) # include "pycore_runtime.h" // _Py_SINGLETON() #endif +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_CheckPositional() PyDoc_STRVAR(_gdbm_gdbm_get__doc__, @@ -70,7 +71,9 @@ _gdbm_gdbm_setdefault(PyObject *self, PyObject *const *args, Py_ssize_t nargs) } default_value = args[1]; skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _gdbm_gdbm_setdefault_impl((gdbmobject *)self, key, default_value); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -91,7 +94,13 @@ _gdbm_gdbm_close_impl(gdbmobject *self); static PyObject * _gdbm_gdbm_close(PyObject *self, PyObject *Py_UNUSED(ignored)) { - return _gdbm_gdbm_close_impl((gdbmobject *)self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _gdbm_gdbm_close_impl((gdbmobject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_gdbm_gdbm_keys__doc__, @@ -109,11 +118,18 @@ _gdbm_gdbm_keys_impl(gdbmobject *self, PyTypeObject *cls); static PyObject * _gdbm_gdbm_keys(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { + PyObject *return_value = NULL; + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { PyErr_SetString(PyExc_TypeError, "keys() takes no arguments"); - return NULL; + goto exit; } - return _gdbm_gdbm_keys_impl((gdbmobject *)self, cls); + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _gdbm_gdbm_keys_impl((gdbmobject *)self, cls); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; } PyDoc_STRVAR(_gdbm_gdbm_firstkey__doc__, @@ -135,11 +151,18 @@ _gdbm_gdbm_firstkey_impl(gdbmobject *self, PyTypeObject *cls); static PyObject * _gdbm_gdbm_firstkey(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { + PyObject *return_value = NULL; + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { PyErr_SetString(PyExc_TypeError, "firstkey() takes no arguments"); - return NULL; + goto exit; } - return _gdbm_gdbm_firstkey_impl((gdbmobject *)self, cls); + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _gdbm_gdbm_firstkey_impl((gdbmobject *)self, cls); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; } PyDoc_STRVAR(_gdbm_gdbm_nextkey__doc__, @@ -187,7 +210,9 @@ _gdbm_gdbm_nextkey(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ &key, &key_length)) { goto exit; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = _gdbm_gdbm_nextkey_impl((gdbmobject *)self, cls, key, key_length); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -214,11 +239,18 @@ _gdbm_gdbm_reorganize_impl(gdbmobject *self, PyTypeObject *cls); static PyObject * _gdbm_gdbm_reorganize(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { + PyObject *return_value = NULL; + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { PyErr_SetString(PyExc_TypeError, "reorganize() takes no arguments"); - return NULL; + goto exit; } - return _gdbm_gdbm_reorganize_impl((gdbmobject *)self, cls); + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _gdbm_gdbm_reorganize_impl((gdbmobject *)self, cls); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; } PyDoc_STRVAR(_gdbm_gdbm_sync__doc__, @@ -239,11 +271,18 @@ _gdbm_gdbm_sync_impl(gdbmobject *self, PyTypeObject *cls); static PyObject * _gdbm_gdbm_sync(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { + PyObject *return_value = NULL; + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { PyErr_SetString(PyExc_TypeError, "sync() takes no arguments"); - return NULL; + goto exit; } - return _gdbm_gdbm_sync_impl((gdbmobject *)self, cls); + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _gdbm_gdbm_sync_impl((gdbmobject *)self, cls); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; } PyDoc_STRVAR(_gdbm_gdbm_clear__doc__, @@ -261,11 +300,18 @@ _gdbm_gdbm_clear_impl(gdbmobject *self, PyTypeObject *cls); static PyObject * _gdbm_gdbm_clear(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { + PyObject *return_value = NULL; + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { PyErr_SetString(PyExc_TypeError, "clear() takes no arguments"); - return NULL; + goto exit; } - return _gdbm_gdbm_clear_impl((gdbmobject *)self, cls); + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _gdbm_gdbm_clear_impl((gdbmobject *)self, cls); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; } PyDoc_STRVAR(dbmopen__doc__, @@ -343,4 +389,4 @@ dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=d974cb39e4ee5d67 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8bca34ce9d4493dd input=a9049054013a1b77]*/ From 07748700577a15944b4addb33665efb241c02daa Mon Sep 17 00:00:00 2001 From: Duane Griffin Date: Mon, 21 Apr 2025 11:55:44 +1200 Subject: [PATCH 2/2] Switch to calling a *_lock_held variant while holding the critical section instead of using gotos everywhere. --- Modules/_dbmmodule.c | 126 +++++++++++++++++++++++------------------- Modules/_gdbmmodule.c | 114 ++++++++++++++++++++++---------------- 2 files changed, 135 insertions(+), 105 deletions(-) diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index b293b9ee0b9f6a..0cd0f043de453d 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -68,8 +68,8 @@ typedef struct { #include "clinic/_dbmmodule.c.h" -/* NOTE: Must be used within a critical section! */ #define check_dbmobject_open(v, err) \ + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED((v)) \ if ((v)->di_dbm == NULL) { \ PyErr_SetString(err, "DBM object has already been closed"); \ return NULL; \ @@ -117,17 +117,14 @@ dbm_dealloc(PyObject *self) } static Py_ssize_t -dbm_length(PyObject *self) +dbm_length_lock_held(PyObject *self) { - Py_ssize_t result = -1; dbmobject *dp = dbmobject_CAST(self); _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); assert(state != NULL); - - Py_BEGIN_CRITICAL_SECTION(self); if (dp->di_dbm == NULL) { PyErr_SetString(state->dbm_error, "DBM object has already been closed"); - goto done; + return -1; } if ( dp->di_size < 0 ) { datum key; @@ -139,95 +136,103 @@ dbm_length(PyObject *self) size++; dp->di_size = size; } - result = dp->di_size; -done:; + return dp->di_size; +} + +static Py_ssize_t +dbm_length(PyObject *self) +{ + Py_ssize_t result; + Py_BEGIN_CRITICAL_SECTION(self); + result = dbm_length_lock_held(self); Py_END_CRITICAL_SECTION(); return result; } static int -dbm_bool(PyObject *self) +dbm_bool_lock_held(PyObject *self) { - int result; dbmobject *dp = dbmobject_CAST(self); _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); assert(state != NULL); - Py_BEGIN_CRITICAL_SECTION(self); if (dp->di_dbm == NULL) { PyErr_SetString(state->dbm_error, "DBM object has already been closed"); - result = -1; - goto done; + return -1; } if (dp->di_size > 0) { /* Known non-zero size. */ - result = 1; - goto done; + return 1; } if (dp->di_size == 0) { /* Known zero size. */ - result = 0; - goto done; + return 0; } /* Unknown size. Ensure DBM object has an entry. */ datum key = dbm_firstkey(dp->di_dbm); if (key.dptr == NULL) { /* Empty. Cache this fact. */ - result = dp->di_size = 0; - goto done; + dp->di_size = 0; + return 0; } - /* Non-empty. Don't cache the length since we don't know. */ - result = 1; -done:; + return 1; +} + +static int +dbm_bool(PyObject *self) +{ + int result; + Py_BEGIN_CRITICAL_SECTION(self); + result = dbm_bool_lock_held(self); Py_END_CRITICAL_SECTION(); return result; } static PyObject * -dbm_subscript(PyObject *self, PyObject *key) +dbm_subscript_lock_held(PyObject *self, PyObject *key) { datum drec, krec; Py_ssize_t tmp_size; - PyObject *result = NULL; dbmobject *dp = dbmobject_CAST(self); _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); assert(state != NULL); if (!PyArg_Parse(key, "s#", &krec.dptr, &tmp_size)) { return NULL; } - krec.dsize = tmp_size; - Py_BEGIN_CRITICAL_SECTION(self); - /* Can't use the macro here as it returns. */ - if (dp->di_dbm == NULL) { - PyErr_SetString(state->dbm_error, "DBM object has already been closed"); - goto done; - } + krec.dsize = tmp_size; + check_dbmobject_open(dp, state->dbm_error); drec = dbm_fetch(dp->di_dbm, krec); if ( drec.dptr == 0 ) { PyErr_SetObject(PyExc_KeyError, key); - goto done; + return NULL; } if ( dbm_error(dp->di_dbm) ) { dbm_clearerr(dp->di_dbm); PyErr_SetString(state->dbm_error, ""); - goto done; + return NULL; } - result = PyBytes_FromStringAndSize(drec.dptr, drec.dsize); -done:; + return PyBytes_FromStringAndSize(drec.dptr, drec.dsize); +} + +static PyObject * +dbm_subscript(PyObject *self, PyObject *key) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(self); + result = dbm_subscript_lock_held(self, key); Py_END_CRITICAL_SECTION(); return result; } static int -dbm_ass_sub(PyObject *self, PyObject *v, PyObject *w) +dbm_ass_sub_lock_held(PyObject *self, PyObject *v, PyObject *w) { datum krec, drec; Py_ssize_t tmp_size; - int result = -1; dbmobject *dp = dbmobject_CAST(self); if ( !PyArg_Parse(v, "s#", &krec.dptr, &tmp_size) ) { @@ -238,13 +243,10 @@ dbm_ass_sub(PyObject *self, PyObject *v, PyObject *w) _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); assert(state != NULL); krec.dsize = tmp_size; - - Py_BEGIN_CRITICAL_SECTION(self); if (dp->di_dbm == NULL) { - PyErr_SetString(state->dbm_error, "DBM object has already been closed"); - goto done; + PyErr_SetString(state->dbm_error, "DBM object has already been closed"); + return -1; } - dp->di_size = -1; if (w == NULL) { if ( dbm_delete(dp->di_dbm, krec) < 0 ) { @@ -257,30 +259,36 @@ dbm_ass_sub(PyObject *self, PyObject *v, PyObject *w) else { PyErr_SetString(state->dbm_error, "cannot delete item from database"); } - goto done; + return -1; } } else { if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) { PyErr_SetString(PyExc_TypeError, "dbm mappings have bytes or string elements only"); - goto done; + return -1; } drec.dsize = tmp_size; if ( dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) { dbm_clearerr(dp->di_dbm); PyErr_SetString(state->dbm_error, "cannot add item to database"); - goto done; + return -1; } } if ( dbm_error(dp->di_dbm) ) { dbm_clearerr(dp->di_dbm); PyErr_SetString(state->dbm_error, ""); - goto done; + return -1; } + return 0; +} - result = 0; -done:; +static int +dbm_ass_sub(PyObject *self, PyObject *v, PyObject *w) +{ + int result; + Py_BEGIN_CRITICAL_SECTION(self); + result = dbm_ass_sub_lock_held(self, v, w); Py_END_CRITICAL_SECTION(); return result; } @@ -345,41 +353,45 @@ _dbm_dbm_keys_impl(dbmobject *self, PyTypeObject *cls) } static int -dbm_contains(PyObject *self, PyObject *arg) +dbm_contains_lock_held(PyObject *self, PyObject *arg) { dbmobject *dp = dbmobject_CAST(self); datum key, val; Py_ssize_t size; - int result = -1; _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); assert(state != NULL); - - Py_BEGIN_CRITICAL_SECTION(self); if ((dp)->di_dbm == NULL) { PyErr_SetString(state->dbm_error, "DBM object has already been closed"); - goto done; + return -1; } if (PyUnicode_Check(arg)) { key.dptr = (char *)PyUnicode_AsUTF8AndSize(arg, &size); key.dsize = size; if (key.dptr == NULL) - goto done; + return -1; } else if (!PyBytes_Check(arg)) { PyErr_Format(PyExc_TypeError, "dbm key must be bytes or string, not %.100s", Py_TYPE(arg)->tp_name); - goto done; + return -1; } else { key.dptr = PyBytes_AS_STRING(arg); key.dsize = PyBytes_GET_SIZE(arg); } val = dbm_fetch(dp->di_dbm, key); - result = val.dptr != NULL; -done:; + return val.dptr != NULL; +} + +static int +dbm_contains(PyObject *self, PyObject *arg) +{ + int result; + Py_BEGIN_CRITICAL_SECTION(self); + result = dbm_contains_lock_held(self, arg); Py_END_CRITICAL_SECTION(); return result; } diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index 7f9afabf652b88..9c402e20e513b9 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -80,8 +80,8 @@ typedef struct { #include "clinic/_gdbmmodule.c.h" -/* NOTE: Must be used within a critical section! */ #define check_gdbmobject_open(v, err) \ + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED((v)) \ if ((v)->di_dbm == NULL) { \ PyErr_SetString(err, "GDBM object has already been closed"); \ return NULL; \ @@ -143,12 +143,10 @@ gdbm_dealloc(PyObject *op) } static Py_ssize_t -gdbm_length(PyObject *op) +gdbm_length_lock_held(PyObject *op) { - Py_ssize_t result = -1; gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); - Py_BEGIN_CRITICAL_SECTION(op); if (dp->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); return -1; @@ -164,11 +162,11 @@ gdbm_length(PyObject *op) else { set_gdbm_error(state, "gdbm_count() error"); } - goto done; + return -1; } if (count > PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "count exceeds PY_SSIZE_T_MAX"); - goto done; + return -1; } dp->di_size = count; #else @@ -188,48 +186,55 @@ gdbm_length(PyObject *op) dp->di_size = size; #endif } - result = dp->di_size; -done:; + return dp->di_size; +} + +static Py_ssize_t +gdbm_length(PyObject *op) +{ + Py_ssize_t result; + Py_BEGIN_CRITICAL_SECTION(op); + result = gdbm_length_lock_held(op); Py_END_CRITICAL_SECTION(); return result; } static int -gdbm_bool(PyObject *op) +gdbm_bool_lock_held(PyObject *op) { - int result; gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); - - Py_BEGIN_CRITICAL_SECTION(op); if (dp->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); - result = -1; - goto done; + return -1; } if (dp->di_size > 0) { /* Known non-zero size. */ - result = 1; - goto done; + return 1; } if (dp->di_size == 0) { /* Known zero size. */ - result = 0; - goto done; + return 0; } /* Unknown size. Ensure DBM object has an entry. */ datum key = gdbm_firstkey(dp->di_dbm); if (key.dptr == NULL) { /* Empty. Cache this fact. */ - result = dp->di_size = 0; - goto done; + dp->di_size = 0; + return 0; } /* Non-empty. Don't cache the length since we don't know. */ free(key.dptr); - result = 1; + return 1; +} -done:; +static int +gdbm_bool(PyObject *op) +{ + int result; + Py_BEGIN_CRITICAL_SECTION(op); + result = gdbm_bool_lock_held(op); Py_END_CRITICAL_SECTION(); return result; } @@ -256,9 +261,9 @@ parse_datum(PyObject *o, datum *d, const char *failmsg) } static PyObject * -gdbm_subscript(PyObject *op, PyObject *key) +gdbm_subscript_lock_held(PyObject *op, PyObject *key) { - PyObject *v = NULL; + PyObject *v; datum drec, krec; gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); @@ -266,25 +271,31 @@ gdbm_subscript(PyObject *op, PyObject *key) if (!parse_datum(key, &krec, NULL)) { return NULL; } - - Py_BEGIN_CRITICAL_SECTION(op); if (dp->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); - goto done; + return NULL; } drec = gdbm_fetch(dp->di_dbm, krec); if (drec.dptr == 0) { PyErr_SetObject(PyExc_KeyError, key); - goto done; + return NULL; } v = PyBytes_FromStringAndSize(drec.dptr, drec.dsize); free(drec.dptr); -done:; - Py_END_CRITICAL_SECTION(); return v; } +static PyObject * +gdbm_subscript(PyObject *op, PyObject *key) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = gdbm_subscript_lock_held(op, key); + Py_END_CRITICAL_SECTION(); + return result; +} + /*[clinic input] _gdbm.gdbm.get @@ -310,23 +321,20 @@ _gdbm_gdbm_get_impl(gdbmobject *self, PyObject *key, PyObject *default_value) } static int -gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w) +gdbm_ass_sub_lock_held(PyObject *op, PyObject *v, PyObject *w) { datum krec, drec; const char *failmsg = "gdbm mappings have bytes or string indices only"; - int result = -1; gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); if (!parse_datum(v, &krec, failmsg)) { return -1; } - - Py_BEGIN_CRITICAL_SECTION(op); if (dp->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); - goto done; + return -1; } dp->di_size = -1; if (w == NULL) { @@ -337,12 +345,12 @@ gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w) else { set_gdbm_error(state, "gdbm_delete() error"); } - goto done; + return -1; } } else { if (!parse_datum(w, &drec, failmsg)) { - goto done; + return -1; } errno = 0; if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0) { @@ -352,12 +360,18 @@ gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w) else { set_gdbm_error(state, "gdbm_store() error"); } - goto done; + return -1; } } - result = 0; + return 0; +} -done:; +static int +gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w) +{ + int result; + Py_BEGIN_CRITICAL_SECTION(op); + result = gdbm_ass_sub_lock_held(op, v, w); Py_END_CRITICAL_SECTION(); return result; } @@ -462,39 +476,43 @@ _gdbm_gdbm_keys_impl(gdbmobject *self, PyTypeObject *cls) } static int -gdbm_contains(PyObject *self, PyObject *arg) +gdbm_contains_lock_held(PyObject *self, PyObject *arg) { gdbmobject *dp = (gdbmobject *)self; datum key; Py_ssize_t size; - int result = -1; _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); - Py_BEGIN_CRITICAL_SECTION(self); if ((dp)->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); - goto done; + return -1; } if (PyUnicode_Check(arg)) { key.dptr = (char *)PyUnicode_AsUTF8AndSize(arg, &size); key.dsize = size; if (key.dptr == NULL) - goto done; + return -1; } else if (!PyBytes_Check(arg)) { PyErr_Format(PyExc_TypeError, "gdbm key must be bytes or string, not %.100s", Py_TYPE(arg)->tp_name); - goto done; + return -1; } else { key.dptr = PyBytes_AS_STRING(arg); key.dsize = PyBytes_GET_SIZE(arg); } - result = gdbm_exists(dp->di_dbm, key); + return gdbm_exists(dp->di_dbm, key); +} -done:; +static int +gdbm_contains(PyObject *self, PyObject *arg) +{ + int result; + Py_BEGIN_CRITICAL_SECTION(self); + result = gdbm_contains_lock_held(self, arg); Py_END_CRITICAL_SECTION(); return result; } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy