From 393cbefe027440f41f9099da7c4915d0e7e8529f Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 27 Jan 2024 23:17:17 +0900 Subject: [PATCH 1/7] gh-112087: Make PyList_{Append,GetItem,Size,GetSlice} to be thread-safe --- Include/object.h | 4 ++++ Objects/listobject.c | 37 +++++++++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/Include/object.h b/Include/object.h index ef3fb721c2b012..2d82a8b3bd4d17 100644 --- a/Include/object.h +++ b/Include/object.h @@ -428,7 +428,11 @@ static inline void Py_SET_TYPE(PyObject *ob, PyTypeObject *type) { static inline void Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) { assert(ob->ob_base.ob_type != &PyLong_Type); assert(ob->ob_base.ob_type != &PyBool_Type); +#ifdef Py_GIL_DISABLED + _Py_atomic_store_ssize_relaxed(&(_PyVarObject_CAST(ob)->ob_size), size); +#else ob->ob_size = size; +#endif } #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000 # define Py_SET_SIZE(ob, size) Py_SET_SIZE(_PyVarObject_CAST(ob), (size)) diff --git a/Objects/listobject.c b/Objects/listobject.c index 56785e5f37a450..20dc69e3a87e86 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -30,6 +30,14 @@ get_list_state(void) } #endif +#ifdef Py_GIL_DISABLED + #define _Py_SET_ITEMREF(op, i, v) \ + _Py_atomic_store_ptr_relaxed(&((PyListObject *)(op))->ob_item[i], Py_NewRef(v)) +#else + #define _Py_SET_ITEMREF(op, i, v) \ + ((PyListObject *)(op))->ob_item[i] = Py_NewRef(v) +#endif + /* Ensure ob_item has room for at least newsize elements, and set * ob_size to newsize. If newsize > ob_size on entry, the content @@ -221,8 +229,9 @@ PyList_Size(PyObject *op) PyErr_BadInternalCall(); return -1; } - else - return Py_SIZE(op); + else { + return PyList_GET_SIZE(op); + } } static inline int @@ -245,12 +254,16 @@ PyList_GetItem(PyObject *op, Py_ssize_t i) PyErr_BadInternalCall(); return NULL; } - if (!valid_index(i, Py_SIZE(op))) { + PyObject *ret = NULL; + if (!valid_index(i, PyList_GET_SIZE(op))) { _Py_DECLARE_STR(list_err, "list index out of range"); PyErr_SetObject(PyExc_IndexError, &_Py_STR(list_err)); return NULL; } - return ((PyListObject *)op) -> ob_item[i]; + Py_BEGIN_CRITICAL_SECTION(op); + ret = ((PyListObject *)op) -> ob_item[i]; + Py_END_CRITICAL_SECTION(); + return ret; } int @@ -341,11 +354,15 @@ _PyList_AppendTakeRefListResize(PyListObject *self, PyObject *newitem) int PyList_Append(PyObject *op, PyObject *newitem) { + int ret = -1; if (PyList_Check(op) && (newitem != NULL)) { - return _PyList_AppendTakeRef((PyListObject *)op, Py_NewRef(newitem)); + Py_BEGIN_CRITICAL_SECTION(op); + ret = _PyList_AppendTakeRef((PyListObject *)op, Py_NewRef(newitem)); + Py_END_CRITICAL_SECTION(); + return ret; } PyErr_BadInternalCall(); - return -1; + return ret; } /* Methods */ @@ -498,7 +515,7 @@ list_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh) dest = np->ob_item; for (i = 0; i < len; i++) { PyObject *v = src[i]; - dest[i] = Py_NewRef(v); + _Py_SET_ITEMREF(np, i, v); } Py_SET_SIZE(np, len); return (PyObject *)np; @@ -511,6 +528,8 @@ PyList_GetSlice(PyObject *a, Py_ssize_t ilow, Py_ssize_t ihigh) PyErr_BadInternalCall(); return NULL; } + PyObject *ret; + Py_BEGIN_CRITICAL_SECTION(a); if (ilow < 0) { ilow = 0; } @@ -523,7 +542,9 @@ PyList_GetSlice(PyObject *a, Py_ssize_t ilow, Py_ssize_t ihigh) else if (ihigh > Py_SIZE(a)) { ihigh = Py_SIZE(a); } - return list_slice((PyListObject *)a, ilow, ihigh); + ret = list_slice((PyListObject *)a, ilow, ihigh); + Py_END_CRITICAL_SECTION(); + return ret; } static PyObject * From df1661d5cfadfb4c5164b55c0a10a6a5567b2ac7 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 27 Jan 2024 23:24:57 +0900 Subject: [PATCH 2/7] Fix compiler warn --- Objects/listobject.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Objects/listobject.c b/Objects/listobject.c index 20dc69e3a87e86..ba0d97c9aa07f8 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -501,7 +501,7 @@ static PyObject * list_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh) { PyListObject *np; - PyObject **src, **dest; + PyObject **src; Py_ssize_t i, len; len = ihigh - ilow; if (len <= 0) { @@ -512,7 +512,6 @@ list_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh) return NULL; src = a->ob_item + ilow; - dest = np->ob_item; for (i = 0; i < len; i++) { PyObject *v = src[i]; _Py_SET_ITEMREF(np, i, v); From 6f37b6143597a1f95084929619e19432f2d42f87 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 27 Jan 2024 23:30:24 +0900 Subject: [PATCH 3/7] nit --- Objects/listobject.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Objects/listobject.c b/Objects/listobject.c index ba0d97c9aa07f8..2a3b928392d179 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -31,11 +31,9 @@ get_list_state(void) #endif #ifdef Py_GIL_DISABLED - #define _Py_SET_ITEMREF(op, i, v) \ - _Py_atomic_store_ptr_relaxed(&((PyListObject *)(op))->ob_item[i], Py_NewRef(v)) + #define _Py_SET_ITEMREF(op, i, v) _Py_atomic_store_ptr_relaxed(&((PyListObject *)(op))->ob_item[i], Py_NewRef(v)) #else - #define _Py_SET_ITEMREF(op, i, v) \ - ((PyListObject *)(op))->ob_item[i] = Py_NewRef(v) + #define _Py_SET_ITEMREF(op, i, v) ((PyListObject *)(op))->ob_item[i] = Py_NewRef(v) #endif From f67f9368c6862c774a57142a4f0f81e7b2337725 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 27 Jan 2024 23:48:53 +0900 Subject: [PATCH 4/7] fix --- Objects/listobject.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Objects/listobject.c b/Objects/listobject.c index 2a3b928392d179..78efff3f80cb6d 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -258,10 +258,7 @@ PyList_GetItem(PyObject *op, Py_ssize_t i) PyErr_SetObject(PyExc_IndexError, &_Py_STR(list_err)); return NULL; } - Py_BEGIN_CRITICAL_SECTION(op); - ret = ((PyListObject *)op) -> ob_item[i]; - Py_END_CRITICAL_SECTION(); - return ret; + return ((PyListObject *)op) -> ob_item[i]; } int @@ -488,7 +485,7 @@ static PyObject * list_item(PyObject *aa, Py_ssize_t i) { PyListObject *a = (PyListObject *)aa; - if (!valid_index(i, Py_SIZE(a))) { + if (!valid_index(i, PyList_GET_SIZE(a))) { PyErr_SetObject(PyExc_IndexError, &_Py_STR(list_err)); return NULL; } From ba74dc50facf61758d0b2109a68c3f19cda82dc8 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 27 Jan 2024 23:56:54 +0900 Subject: [PATCH 5/7] Improve _PyList_AppendTakeRef --- Include/internal/pycore_list.h | 3 ++- Objects/listobject.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_list.h b/Include/internal/pycore_list.h index 6c29d882335512..4536f90e414493 100644 --- a/Include/internal/pycore_list.h +++ b/Include/internal/pycore_list.h @@ -24,12 +24,13 @@ extern void _PyList_Fini(_PyFreeListState *); extern int _PyList_AppendTakeRefListResize(PyListObject *self, PyObject *newitem); +// In free-threaded build: self should be locked by the caller, if it should be thread-safe. static inline int _PyList_AppendTakeRef(PyListObject *self, PyObject *newitem) { assert(self != NULL && newitem != NULL); assert(PyList_Check(self)); - Py_ssize_t len = PyList_GET_SIZE(self); + Py_ssize_t len = Py_SIZE(self); Py_ssize_t allocated = self->allocated; assert((size_t)len + 1 < PY_SSIZE_T_MAX); if (allocated > len) { diff --git a/Objects/listobject.c b/Objects/listobject.c index 78efff3f80cb6d..76ed2bd51d08c5 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -336,7 +336,7 @@ PyList_Insert(PyObject *op, Py_ssize_t where, PyObject *newitem) int _PyList_AppendTakeRefListResize(PyListObject *self, PyObject *newitem) { - Py_ssize_t len = PyList_GET_SIZE(self); + Py_ssize_t len = Py_SIZE(self); assert(self->allocated == -1 || self->allocated == len); if (list_resize(self, len + 1) < 0) { Py_DECREF(newitem); From 9075e61a34ae8dee0f0914b70b8c86f41891b2ae Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sun, 28 Jan 2024 00:01:27 +0900 Subject: [PATCH 6/7] nit --- Objects/listobject.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Objects/listobject.c b/Objects/listobject.c index 76ed2bd51d08c5..e7775dfc07cbd7 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -252,7 +252,6 @@ PyList_GetItem(PyObject *op, Py_ssize_t i) PyErr_BadInternalCall(); return NULL; } - PyObject *ret = NULL; if (!valid_index(i, PyList_GET_SIZE(op))) { _Py_DECLARE_STR(list_err, "list index out of range"); PyErr_SetObject(PyExc_IndexError, &_Py_STR(list_err)); From 553db97d744ecf8d8f413a5335a6208396dd225f Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Tue, 30 Jan 2024 10:09:52 +0900 Subject: [PATCH 7/7] Address code review --- Include/object.h | 2 +- Objects/listobject.c | 18 ++++++------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/Include/object.h b/Include/object.h index 2d82a8b3bd4d17..568d315d7606c4 100644 --- a/Include/object.h +++ b/Include/object.h @@ -429,7 +429,7 @@ static inline void Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) { assert(ob->ob_base.ob_type != &PyLong_Type); assert(ob->ob_base.ob_type != &PyBool_Type); #ifdef Py_GIL_DISABLED - _Py_atomic_store_ssize_relaxed(&(_PyVarObject_CAST(ob)->ob_size), size); + _Py_atomic_store_ssize_relaxed(&ob->ob_size, size); #else ob->ob_size = size; #endif diff --git a/Objects/listobject.c b/Objects/listobject.c index e7775dfc07cbd7..80a1f1da55b8bc 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -30,13 +30,6 @@ get_list_state(void) } #endif -#ifdef Py_GIL_DISABLED - #define _Py_SET_ITEMREF(op, i, v) _Py_atomic_store_ptr_relaxed(&((PyListObject *)(op))->ob_item[i], Py_NewRef(v)) -#else - #define _Py_SET_ITEMREF(op, i, v) ((PyListObject *)(op))->ob_item[i] = Py_NewRef(v) -#endif - - /* Ensure ob_item has room for at least newsize elements, and set * ob_size to newsize. If newsize > ob_size on entry, the content * of the new slots at exit is undefined heap trash; it's the caller's @@ -252,7 +245,7 @@ PyList_GetItem(PyObject *op, Py_ssize_t i) PyErr_BadInternalCall(); return NULL; } - if (!valid_index(i, PyList_GET_SIZE(op))) { + if (!valid_index(i, Py_SIZE(op))) { _Py_DECLARE_STR(list_err, "list index out of range"); PyErr_SetObject(PyExc_IndexError, &_Py_STR(list_err)); return NULL; @@ -348,15 +341,15 @@ _PyList_AppendTakeRefListResize(PyListObject *self, PyObject *newitem) int PyList_Append(PyObject *op, PyObject *newitem) { - int ret = -1; if (PyList_Check(op) && (newitem != NULL)) { + int ret; Py_BEGIN_CRITICAL_SECTION(op); ret = _PyList_AppendTakeRef((PyListObject *)op, Py_NewRef(newitem)); Py_END_CRITICAL_SECTION(); return ret; } PyErr_BadInternalCall(); - return ret; + return -1; } /* Methods */ @@ -495,7 +488,7 @@ static PyObject * list_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh) { PyListObject *np; - PyObject **src; + PyObject **src, **dest; Py_ssize_t i, len; len = ihigh - ilow; if (len <= 0) { @@ -506,9 +499,10 @@ list_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh) return NULL; src = a->ob_item + ilow; + dest = np->ob_item; for (i = 0; i < len; i++) { PyObject *v = src[i]; - _Py_SET_ITEMREF(np, i, v); + dest[i] = Py_NewRef(v); } Py_SET_SIZE(np, len); return (PyObject *)np;