From d2f1b0eb4956b4923f111c7c740ba7ab25f3312d Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 6 Mar 2024 08:21:33 +0900 Subject: [PATCH] gh-112087: Update list_get_item_ref to optimistically avoid locking (gh-116353) Co-authored-by: Sam Gross --- Objects/listobject.c | 71 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/Objects/listobject.c b/Objects/listobject.c index 79d61878c38..e013383a712 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -233,6 +233,63 @@ valid_index(Py_ssize_t i, Py_ssize_t limit) return (size_t) i < (size_t) limit; } +#ifdef Py_GIL_DISABLED + +static PyObject * +list_item_impl(PyListObject *self, Py_ssize_t idx) +{ + PyObject *item = NULL; + Py_BEGIN_CRITICAL_SECTION(self); + if (!_PyObject_GC_IS_SHARED(self)) { + _PyObject_GC_SET_SHARED(self); + } + Py_ssize_t size = Py_SIZE(self); + if (!valid_index(idx, size)) { + goto exit; + } + item = Py_NewRef(self->ob_item[idx]); +exit: + Py_END_CRITICAL_SECTION(); + return item; +} + +static inline PyObject* +list_get_item_ref(PyListObject *op, Py_ssize_t i) +{ + if (!_Py_IsOwnedByCurrentThread((PyObject *)op) && !_PyObject_GC_IS_SHARED(op)) { + return list_item_impl(op, i); + } + // Need atomic operation for the getting size. + Py_ssize_t size = PyList_GET_SIZE(op); + if (!valid_index(i, size)) { + return NULL; + } + PyObject **ob_item = _Py_atomic_load_ptr(&op->ob_item); + if (ob_item == NULL) { + return NULL; + } + Py_ssize_t cap = _Py_atomic_load_ssize_relaxed(&op->allocated); + assert(cap != -1 && cap >= size); + if (!valid_index(i, cap)) { + return NULL; + } + PyObject *item = _Py_TryXGetRef(&ob_item[i]); + if (item == NULL) { + return list_item_impl(op, i); + } + return item; +} +#else +static inline PyObject* +list_get_item_ref(PyListObject *op, Py_ssize_t i) +{ + if (!valid_index(i, Py_SIZE(op))) { + return NULL; + } + return Py_NewRef(PyList_GET_ITEM(op, i)); +} +#endif + PyObject * PyList_GetItem(PyObject *op, Py_ssize_t i) { @@ -255,21 +312,13 @@ PyList_GetItemRef(PyObject *op, Py_ssize_t i) PyErr_SetString(PyExc_TypeError, "expected a list"); return NULL; } - if (!valid_index(i, Py_SIZE(op))) { + PyObject *item = list_get_item_ref((PyListObject *)op, i); + if (item == NULL) { _Py_DECLARE_STR(list_err, "list index out of range"); PyErr_SetObject(PyExc_IndexError, &_Py_STR(list_err)); return NULL; } - return Py_NewRef(PyList_GET_ITEM(op, i)); -} - -static inline PyObject* -list_get_item_ref(PyListObject *op, Py_ssize_t i) -{ - if (!valid_index(i, Py_SIZE(op))) { - return NULL; - } - return Py_NewRef(PyList_GET_ITEM(op, i)); + return item; } int