gh-112069: Add _PySet_NextEntryRef to be thread-safe. (gh-117990)

This commit is contained in:
Donghee Na 2024-04-19 00:18:22 +09:00 committed by GitHub
parent 40f4d641a9
commit 94444ea45a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 76 additions and 34 deletions

View File

@ -8,13 +8,20 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define" # error "this header requires Py_BUILD_CORE define"
#endif #endif
// Export for '_pickle' shared extension // Export for '_abc' shared extension
PyAPI_FUNC(int) _PySet_NextEntry( PyAPI_FUNC(int) _PySet_NextEntry(
PyObject *set, PyObject *set,
Py_ssize_t *pos, Py_ssize_t *pos,
PyObject **key, PyObject **key,
Py_hash_t *hash); 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 // Export for '_pickle' shared extension
PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable); PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable);

View File

@ -862,7 +862,7 @@ subclasscheck_check_registry(_abc_data *impl, PyObject *subclass,
// Make a local copy of the registry to protect against concurrent // Make a local copy of the registry to protect against concurrent
// modifications of _abc_registry. // modifications of _abc_registry.
PyObject *registry = PySet_New(registry_shared); PyObject *registry = PyFrozenSet_New(registry_shared);
if (registry == NULL) { if (registry == NULL) {
return -1; return -1;
} }

View File

@ -9,15 +9,16 @@
#endif #endif
#include "Python.h" #include "Python.h"
#include "pycore_bytesobject.h" // _PyBytesWriter #include "pycore_bytesobject.h" // _PyBytesWriter
#include "pycore_ceval.h" // _Py_EnterRecursiveCall() #include "pycore_ceval.h" // _Py_EnterRecursiveCall()
#include "pycore_long.h" // _PyLong_AsByteArray() #include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION()
#include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_long.h" // _PyLong_AsByteArray()
#include "pycore_object.h" // _PyNone_Type #include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_object.h" // _PyNone_Type
#include "pycore_runtime.h" // _Py_ID() #include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_setobject.h" // _PySet_NextEntry() #include "pycore_runtime.h" // _Py_ID()
#include "pycore_sysmodule.h" // _PySys_GetAttr() #include "pycore_setobject.h" // _PySet_NextEntry()
#include "pycore_sysmodule.h" // _PySys_GetAttr()
#include <stdlib.h> // strtol() #include <stdlib.h> // strtol()
@ -3413,15 +3414,21 @@ save_set(PickleState *state, PicklerObject *self, PyObject *obj)
i = 0; i = 0;
if (_Pickler_Write(self, &mark_op, 1) < 0) if (_Pickler_Write(self, &mark_op, 1) < 0)
return -1; return -1;
while (_PySet_NextEntry(obj, &ppos, &item, &hash)) {
Py_INCREF(item); int err = 0;
int err = save(state, self, item, 0); Py_BEGIN_CRITICAL_SECTION(obj);
while (_PySet_NextEntryRef(obj, &ppos, &item, &hash)) {
err = save(state, self, item, 0);
Py_CLEAR(item); Py_CLEAR(item);
if (err < 0) if (err < 0)
return -1; break;
if (++i == BATCHSIZE) if (++i == BATCHSIZE)
break; break;
} }
Py_END_CRITICAL_SECTION();
if (err < 0) {
return -1;
}
if (_Pickler_Write(self, &additems_op, 1) < 0) if (_Pickler_Write(self, &additems_op, 1) < 0)
return -1; return -1;
if (PySet_GET_SIZE(obj) != set_size) { if (PySet_GET_SIZE(obj) != set_size) {

View File

@ -1,6 +1,7 @@
#include "parts.h" #include "parts.h"
#include "../_testcapi/util.h" // NULLABLE, RETURN_INT #include "../_testcapi/util.h" // NULLABLE, RETURN_INT
#include "pycore_critical_section.h"
#include "pycore_setobject.h" #include "pycore_setobject.h"
@ -27,10 +28,13 @@ set_next_entry(PyObject *self, PyObject *args)
return NULL; return NULL;
} }
NULLABLE(set); NULLABLE(set);
Py_BEGIN_CRITICAL_SECTION(set);
rc = _PySet_NextEntry(set, &pos, &item, &hash); rc = _PySet_NextEntryRef(set, &pos, &item, &hash);
Py_END_CRITICAL_SECTION();
if (rc == 1) { 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(item == UNINITIALIZED_PTR);
assert(hash == (Py_hash_t)UNINITIALIZED_SIZE); assert(hash == (Py_hash_t)UNINITIALIZED_SIZE);

View File

@ -2979,8 +2979,9 @@ dict_set_fromkeys(PyInterpreterState *interp, PyDictObject *mp,
return NULL; return NULL;
} }
while (_PySet_NextEntry(iterable, &pos, &key, &hash)) { _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(iterable);
if (insertdict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value))) { while (_PySet_NextEntryRef(iterable, &pos, &key, &hash)) {
if (insertdict(interp, mp, key, hash, Py_NewRef(value))) {
Py_DECREF(mp); Py_DECREF(mp);
return NULL; return NULL;
} }

View File

@ -1287,8 +1287,7 @@ list_extend_set(PyListObject *self, PySetObject *other)
Py_hash_t hash; Py_hash_t hash;
PyObject *key; PyObject *key;
PyObject **dest = self->ob_item + m; PyObject **dest = self->ob_item + m;
while (_PySet_NextEntry((PyObject *)other, &setpos, &key, &hash)) { while (_PySet_NextEntryRef((PyObject *)other, &setpos, &key, &hash)) {
Py_INCREF(key);
FT_ATOMIC_STORE_PTR_RELEASE(*dest, key); FT_ATOMIC_STORE_PTR_RELEASE(*dest, key);
dest++; dest++;
} }

View File

@ -2661,7 +2661,6 @@ PySet_Add(PyObject *anyset, PyObject *key)
return rv; return rv;
} }
// TODO: Make thread-safe in free-threaded builds
int int
_PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash) _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; 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 * PyObject *
PySet_Pop(PyObject *set) PySet_Pop(PyObject *set)
{ {

View File

@ -7,12 +7,13 @@
and sharing. */ and sharing. */
#include "Python.h" #include "Python.h"
#include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_code.h" // _PyCode_New() #include "pycore_code.h" // _PyCode_New()
#include "pycore_hashtable.h" // _Py_hashtable_t #include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION()
#include "pycore_long.h" // _PyLong_DigitCount #include "pycore_hashtable.h" // _Py_hashtable_t
#include "pycore_setobject.h" // _PySet_NextEntry() #include "pycore_long.h" // _PyLong_DigitCount
#include "marshal.h" // Py_MARSHAL_VERSION #include "pycore_setobject.h" // _PySet_NextEntry()
#include "marshal.h" // Py_MARSHAL_VERSION
#ifdef __APPLE__ #ifdef __APPLE__
# include "TargetConditionals.h" # include "TargetConditionals.h"
@ -531,23 +532,29 @@ w_complex_object(PyObject *v, char flag, WFILE *p)
return; return;
} }
Py_ssize_t i = 0; 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, PyObject *dump = _PyMarshal_WriteObjectToString(value,
p->version, p->allow_code); p->version, p->allow_code);
if (dump == NULL) { if (dump == NULL) {
p->error = WFERR_UNMARSHALLABLE; p->error = WFERR_UNMARSHALLABLE;
Py_DECREF(pairs); Py_DECREF(value);
return; break;
} }
PyObject *pair = PyTuple_Pack(2, dump, value); PyObject *pair = PyTuple_Pack(2, dump, value);
Py_DECREF(dump); Py_DECREF(dump);
Py_DECREF(value);
if (pair == NULL) { if (pair == NULL) {
p->error = WFERR_NOMEMORY; p->error = WFERR_NOMEMORY;
Py_DECREF(pairs); break;
return;
} }
PyList_SET_ITEM(pairs, i++, pair); 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); assert(i == n);
if (PyList_Sort(pairs)) { if (PyList_Sort(pairs)) {
p->error = WFERR_NOMEMORY; p->error = WFERR_NOMEMORY;

View File

@ -2910,6 +2910,7 @@ _Py_DumpExtensionModules(int fd, PyInterpreterState *interp)
Py_ssize_t i = 0; Py_ssize_t i = 0;
PyObject *item; PyObject *item;
Py_hash_t hash; 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)) { while (_PySet_NextEntry(stdlib_module_names, &i, &item, &hash)) {
if (PyUnicode_Check(item) if (PyUnicode_Check(item)
&& PyUnicode_Compare(key, item) == 0) && PyUnicode_Compare(key, item) == 0)