gh-120974: Make _asyncio._leave_task atomic in the free-threaded build (#122139)

* gh-120974: Make _asyncio._leave_task atomic in the free-threaded build

Update `_PyDict_DelItemIf` to allow for an argument to be passed to the
predicate.
This commit is contained in:
Sam Gross 2024-07-23 13:06:03 -04:00 committed by GitHub
parent e6b25e9a09
commit a15feded71
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 49 additions and 46 deletions

View file

@ -1994,30 +1994,36 @@ enter_task(asyncio_state *state, PyObject *loop, PyObject *task)
return 0;
}
static int
err_leave_task(PyObject *item, PyObject *task)
{
PyErr_Format(
PyExc_RuntimeError,
"Leaving task %R does not match the current task %R.",
task, item);
return -1;
}
static int
leave_task_predicate(PyObject *item, void *task)
{
if (item != task) {
return err_leave_task(item, (PyObject *)task);
}
return 1;
}
static int
leave_task(asyncio_state *state, PyObject *loop, PyObject *task)
/*[clinic end generated code: output=0ebf6db4b858fb41 input=51296a46313d1ad8]*/
{
PyObject *item;
Py_hash_t hash;
hash = PyObject_Hash(loop);
if (hash == -1) {
return -1;
int res = _PyDict_DelItemIf(state->current_tasks, loop,
leave_task_predicate, task);
if (res == 0) {
// task was not found
return err_leave_task(Py_None, task);
}
item = _PyDict_GetItem_KnownHash(state->current_tasks, loop, hash);
if (item != task) {
if (item == NULL) {
/* Not entered, replace with None */
item = Py_None;
}
PyErr_Format(
PyExc_RuntimeError,
"Leaving task %R does not match the current task %R.",
task, item, NULL);
return -1;
}
return _PyDict_DelItem_KnownHash(state->current_tasks, loop, hash);
return res;
}
static PyObject *

View file

@ -31,7 +31,7 @@ _weakref_getweakrefcount_impl(PyObject *module, PyObject *object)
static int
is_dead_weakref(PyObject *value)
is_dead_weakref(PyObject *value, void *unused)
{
if (!PyWeakref_Check(value)) {
PyErr_SetString(PyExc_TypeError, "not a weakref");
@ -56,15 +56,8 @@ _weakref__remove_dead_weakref_impl(PyObject *module, PyObject *dct,
PyObject *key)
/*[clinic end generated code: output=d9ff53061fcb875c input=19fc91f257f96a1d]*/
{
if (_PyDict_DelItemIf(dct, key, is_dead_weakref) < 0) {
if (PyErr_ExceptionMatches(PyExc_KeyError))
/* This function is meant to allow safe weak-value dicts
with GC in another thread (see issue #28427), so it's
ok if the key doesn't exist anymore.
*/
PyErr_Clear();
else
return NULL;
if (_PyDict_DelItemIf(dct, key, is_dead_weakref, NULL) < 0) {
return NULL;
}
Py_RETURN_NONE;
}