Skip to content

Commit eda29bc

Browse files
committed
gh-112087: Update list_get_item_ref to optimistically avoid locking
1 parent cbf3d38 commit eda29bc

File tree

1 file changed

+60
-0
lines changed

1 file changed

+60
-0
lines changed

Objects/listobject.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,65 @@ PyList_GetItemRef(PyObject *op, Py_ssize_t i)
263263
return Py_NewRef(PyList_GET_ITEM(op, i));
264264
}
265265

266+
#ifdef Py_GIL_DISABLED
267+
268+
static PyObject *
269+
list_item_impl(PyListObject *self, Py_ssize_t idx, PyObject *dead)
270+
{
271+
PyObject *item = NULL;
272+
Py_BEGIN_CRITICAL_SECTION(self);
273+
if (!_PyObject_GC_IS_SHARED(self)) {
274+
_PyObject_GC_SET_SHARED(self);
275+
}
276+
Py_ssize_t size = Py_SIZE(self);
277+
if (!valid_index(idx, size)) {
278+
goto exit;
279+
}
280+
item = Py_NewRef(self->ob_item[idx]);
281+
exit:
282+
Py_END_CRITICAL_SECTION();
283+
return item;
284+
}
285+
286+
static inline PyObject*
287+
list_get_item_ref(PyListObject *op, Py_ssize_t i)
288+
{
289+
if (!_Py_IsOwnedByCurrentThread((PyObject *)op) && !_PyObject_GC_IS_SHARED(op)) {
290+
return list_item_impl(op, i, NULL);
291+
}
292+
// Need atomic operation for the getting size.
293+
Py_ssize_t size = _Py_atomic_load_ssize_relaxed(&_PyVarObject_CAST(op)->ob_size);
294+
if (!valid_index(i, size)) {
295+
return NULL;
296+
}
297+
PyObject **ob_item = _Py_atomic_load_ptr(&op->ob_item);
298+
if (ob_item == NULL) {
299+
return NULL;
300+
}
301+
Py_ssize_t cap = _Py_atomic_load_ssize_relaxed(&op->allocated);
302+
if (!valid_index(i, cap)) {
303+
return NULL;
304+
}
305+
PyObject *item = _Py_atomic_load_ptr(&ob_item[i]);
306+
if (mi_unlikely(!item)) {
307+
return list_item_impl(op, i, NULL);
308+
}
309+
if (mi_likely(_Py_TryIncrefFast(item))) {
310+
goto compare_ob_item;
311+
}
312+
if (!_Py_TryIncRefShared(item)) {
313+
return list_item_impl(op, i, item);
314+
}
315+
if (mi_unlikely(item != _Py_atomic_load_ptr(&ob_item[i]))) {
316+
return list_item_impl(op, i, item);
317+
}
318+
compare_ob_item:
319+
if (mi_unlikely(ob_item != _Py_atomic_load_ptr(&op->ob_item))) {
320+
return list_item_impl(op, i, item);
321+
}
322+
return item;
323+
}
324+
#else
266325
static inline PyObject*
267326
list_get_item_ref(PyListObject *op, Py_ssize_t i)
268327
{
@@ -271,6 +330,7 @@ list_get_item_ref(PyListObject *op, Py_ssize_t i)
271330
}
272331
return Py_NewRef(PyList_GET_ITEM(op, i));
273332
}
333+
#endif
274334

275335
int
276336
PyList_SetItem(PyObject *op, Py_ssize_t i,

0 commit comments

Comments
 (0)