]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-112069: Add _PySet_NextEntryRef to be thread-safe. (gh-117990)
authorDonghee Na <donghee.na@python.org>
Thu, 18 Apr 2024 15:18:22 +0000 (00:18 +0900)
committerGitHub <noreply@github.com>
Thu, 18 Apr 2024 15:18:22 +0000 (00:18 +0900)
Include/internal/pycore_setobject.h
Modules/_abc.c
Modules/_pickle.c
Modules/_testinternalcapi/set.c
Objects/dictobject.c
Objects/listobject.c
Objects/setobject.c
Python/marshal.c
Python/pylifecycle.c

index c4ec3ceb17eba6b3f65b3c0bf4c17652e04d5cb4..41b351ead25dd180daed2527d6c072d8184e4a25 100644 (file)
@@ -8,13 +8,20 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
-// Export for '_pickle' shared extension
+// Export for '_abc' shared extension
 PyAPI_FUNC(int) _PySet_NextEntry(
     PyObject *set,
     Py_ssize_t *pos,
     PyObject **key,
     Py_hash_t *hash);
 
+// Export for '_pickle' shared extension
+PyAPI_FUNC(int) _PySet_NextEntryRef(
+    PyObject *set,
+    Py_ssize_t *pos,
+    PyObject **key,
+    Py_hash_t *hash);
+
 // Export for '_pickle' shared extension
 PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable);
 
index ad28035843fd321f7140389f4d11c8d99f3465e3..f2a523e6f2fc273d0a76984fa0324a67eef6017b 100644 (file)
@@ -862,7 +862,7 @@ subclasscheck_check_registry(_abc_data *impl, PyObject *subclass,
 
     // Make a local copy of the registry to protect against concurrent
     // modifications of _abc_registry.
-    PyObject *registry = PySet_New(registry_shared);
+    PyObject *registry = PyFrozenSet_New(registry_shared);
     if (registry == NULL) {
         return -1;
     }
index 0d83261168185d98010d9a58be5b7dd95a4da5c7..d7ffb04c28c2ac3caedeea29dcdb45dc9de8867c 100644 (file)
@@ -9,15 +9,16 @@
 #endif
 
 #include "Python.h"
-#include "pycore_bytesobject.h"   // _PyBytesWriter
-#include "pycore_ceval.h"         // _Py_EnterRecursiveCall()
-#include "pycore_long.h"          // _PyLong_AsByteArray()
-#include "pycore_moduleobject.h"  // _PyModule_GetState()
-#include "pycore_object.h"        // _PyNone_Type
-#include "pycore_pystate.h"       // _PyThreadState_GET()
-#include "pycore_runtime.h"       // _Py_ID()
-#include "pycore_setobject.h"     // _PySet_NextEntry()
-#include "pycore_sysmodule.h"     // _PySys_GetAttr()
+#include "pycore_bytesobject.h"       // _PyBytesWriter
+#include "pycore_ceval.h"             // _Py_EnterRecursiveCall()
+#include "pycore_critical_section.h"  // Py_BEGIN_CRITICAL_SECTION()
+#include "pycore_long.h"              // _PyLong_AsByteArray()
+#include "pycore_moduleobject.h"      // _PyModule_GetState()
+#include "pycore_object.h"            // _PyNone_Type
+#include "pycore_pystate.h"           // _PyThreadState_GET()
+#include "pycore_runtime.h"           // _Py_ID()
+#include "pycore_setobject.h"         // _PySet_NextEntry()
+#include "pycore_sysmodule.h"         // _PySys_GetAttr()
 
 #include <stdlib.h>               // strtol()
 
@@ -3413,15 +3414,21 @@ save_set(PickleState *state, PicklerObject *self, PyObject *obj)
         i = 0;
         if (_Pickler_Write(self, &mark_op, 1) < 0)
             return -1;
-        while (_PySet_NextEntry(obj, &ppos, &item, &hash)) {
-            Py_INCREF(item);
-            int err = save(state, self, item, 0);
+
+        int err = 0;
+        Py_BEGIN_CRITICAL_SECTION(obj);
+        while (_PySet_NextEntryRef(obj, &ppos, &item, &hash)) {
+            err = save(state, self, item, 0);
             Py_CLEAR(item);
             if (err < 0)
-                return -1;
+                break;
             if (++i == BATCHSIZE)
                 break;
         }
+        Py_END_CRITICAL_SECTION();
+        if (err < 0) {
+            return -1;
+        }
         if (_Pickler_Write(self, &additems_op, 1) < 0)
             return -1;
         if (PySet_GET_SIZE(obj) != set_size) {
index 0305a7885d217c8fba7754479dc3bcb0b70b8f01..01aab03cc109ed4f85ef577cb5f743bf44cfab13 100644 (file)
@@ -1,6 +1,7 @@
 #include "parts.h"
 #include "../_testcapi/util.h"  // NULLABLE, RETURN_INT
 
+#include "pycore_critical_section.h"
 #include "pycore_setobject.h"
 
 
@@ -27,10 +28,13 @@ set_next_entry(PyObject *self, PyObject *args)
         return NULL;
     }
     NULLABLE(set);
-
-    rc = _PySet_NextEntry(set, &pos, &item, &hash);
+    Py_BEGIN_CRITICAL_SECTION(set);
+    rc = _PySet_NextEntryRef(set, &pos, &item, &hash);
+    Py_END_CRITICAL_SECTION();
     if (rc == 1) {
-        return Py_BuildValue("innO", rc, pos, hash, item);
+        PyObject *ret = Py_BuildValue("innO", rc, pos, hash, item);
+        Py_DECREF(item);
+        return ret;
     }
     assert(item == UNINITIALIZED_PTR);
     assert(hash == (Py_hash_t)UNINITIALIZED_SIZE);
index 003a03fd7417025da924403613b0386820068532..58f34c32a87ea94d754d3876881595f994e59292 100644 (file)
@@ -2979,8 +2979,9 @@ dict_set_fromkeys(PyInterpreterState *interp, PyDictObject *mp,
         return NULL;
     }
 
-    while (_PySet_NextEntry(iterable, &pos, &key, &hash)) {
-        if (insertdict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value))) {
+    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(iterable);
+    while (_PySet_NextEntryRef(iterable, &pos, &key, &hash)) {
+        if (insertdict(interp, mp, key, hash, Py_NewRef(value))) {
             Py_DECREF(mp);
             return NULL;
         }
index 472c471d9968a46860b1afabacf8e9e459e4f0f3..4eaf20033fa2627c7d54198115a1a0ad1cc4dff2 100644 (file)
@@ -1287,8 +1287,7 @@ list_extend_set(PyListObject *self, PySetObject *other)
     Py_hash_t hash;
     PyObject *key;
     PyObject **dest = self->ob_item + m;
-    while (_PySet_NextEntry((PyObject *)other, &setpos, &key, &hash)) {
-        Py_INCREF(key);
+    while (_PySet_NextEntryRef((PyObject *)other, &setpos, &key, &hash)) {
         FT_ATOMIC_STORE_PTR_RELEASE(*dest, key);
         dest++;
     }
index 66ca80e8fc25f96a20a5322066d976b8d592da24..7af0ae166f9da3183dad0c8869f8aa0d8272dbe2 100644 (file)
@@ -2661,7 +2661,6 @@ PySet_Add(PyObject *anyset, PyObject *key)
     return rv;
 }
 
-// TODO: Make thread-safe in free-threaded builds
 int
 _PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash)
 {
@@ -2678,6 +2677,23 @@ _PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash
     return 1;
 }
 
+int
+_PySet_NextEntryRef(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash)
+{
+    setentry *entry;
+
+    if (!PyAnySet_Check(set)) {
+        PyErr_BadInternalCall();
+        return -1;
+    }
+    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(set);
+    if (set_next((PySetObject *)set, pos, &entry) == 0)
+        return 0;
+    *key = Py_NewRef(entry->key);
+    *hash = entry->hash;
+    return 1;
+}
+
 PyObject *
 PySet_Pop(PyObject *set)
 {
index 21d242bbb9757e8c1153db2e2517b7323270d1ca..4274f90206b2405d24112c2738c4d4867ba09bb0 100644 (file)
@@ -7,12 +7,13 @@
    and sharing. */
 
 #include "Python.h"
-#include "pycore_call.h"          // _PyObject_CallNoArgs()
-#include "pycore_code.h"          // _PyCode_New()
-#include "pycore_hashtable.h"     // _Py_hashtable_t
-#include "pycore_long.h"          // _PyLong_DigitCount
-#include "pycore_setobject.h"     // _PySet_NextEntry()
-#include "marshal.h"              // Py_MARSHAL_VERSION
+#include "pycore_call.h"             // _PyObject_CallNoArgs()
+#include "pycore_code.h"             // _PyCode_New()
+#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION()
+#include "pycore_hashtable.h"        // _Py_hashtable_t
+#include "pycore_long.h"             // _PyLong_DigitCount
+#include "pycore_setobject.h"        // _PySet_NextEntry()
+#include "marshal.h"                 // Py_MARSHAL_VERSION
 
 #ifdef __APPLE__
 #  include "TargetConditionals.h"
@@ -531,23 +532,29 @@ w_complex_object(PyObject *v, char flag, WFILE *p)
             return;
         }
         Py_ssize_t i = 0;
-        while (_PySet_NextEntry(v, &pos, &value, &hash)) {
+        Py_BEGIN_CRITICAL_SECTION(v);
+        while (_PySet_NextEntryRef(v, &pos, &value, &hash)) {
             PyObject *dump = _PyMarshal_WriteObjectToString(value,
                                     p->version, p->allow_code);
             if (dump == NULL) {
                 p->error = WFERR_UNMARSHALLABLE;
-                Py_DECREF(pairs);
-                return;
+                Py_DECREF(value);
+                break;
             }
             PyObject *pair = PyTuple_Pack(2, dump, value);
             Py_DECREF(dump);
+            Py_DECREF(value);
             if (pair == NULL) {
                 p->error = WFERR_NOMEMORY;
-                Py_DECREF(pairs);
-                return;
+                break;
             }
             PyList_SET_ITEM(pairs, i++, pair);
         }
+        Py_END_CRITICAL_SECTION();
+        if (p->error == WFERR_UNMARSHALLABLE || p->error == WFERR_NOMEMORY) {
+            Py_DECREF(pairs);
+            return;
+        }
         assert(i == n);
         if (PyList_Sort(pairs)) {
             p->error = WFERR_NOMEMORY;
index efb25878312d85270ceb127433a7c0ea2112bbc6..cc1824634e7a7f5260e22924d64d82772d1fced1 100644 (file)
@@ -2910,6 +2910,7 @@ _Py_DumpExtensionModules(int fd, PyInterpreterState *interp)
             Py_ssize_t i = 0;
             PyObject *item;
             Py_hash_t hash;
+            // if stdlib_module_names is not NULL, it is always a frozenset.
             while (_PySet_NextEntry(stdlib_module_names, &i, &item, &hash)) {
                 if (PyUnicode_Check(item)
                     && PyUnicode_Compare(key, item) == 0)