mirror of https://github.com/python/cpython
[3.13] gh-120974: Make _asyncio._leave_task atomic in the free-threaded build (GH-122139) (#122186)
gh-120974: Make _asyncio._leave_task atomic in the free-threaded build (GH-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.
(cherry picked from commit a15feded71
)
Co-authored-by: Sam Gross <colesbury@gmail.com>
This commit is contained in:
parent
214b430faf
commit
77ab53a5f3
|
@ -16,8 +16,12 @@ extern "C" {
|
||||||
// Unsafe flavor of PyDict_GetItemWithError(): no error checking
|
// Unsafe flavor of PyDict_GetItemWithError(): no error checking
|
||||||
extern PyObject* _PyDict_GetItemWithError(PyObject *dp, PyObject *key);
|
extern PyObject* _PyDict_GetItemWithError(PyObject *dp, PyObject *key);
|
||||||
|
|
||||||
extern int _PyDict_DelItemIf(PyObject *mp, PyObject *key,
|
// Delete an item from a dict if a predicate is true
|
||||||
int (*predicate)(PyObject *value));
|
// Returns -1 on error, 1 if the item was deleted, 0 otherwise
|
||||||
|
// Export for '_asyncio' shared extension
|
||||||
|
PyAPI_FUNC(int) _PyDict_DelItemIf(PyObject *mp, PyObject *key,
|
||||||
|
int (*predicate)(PyObject *value, void *arg),
|
||||||
|
void *arg);
|
||||||
|
|
||||||
// "KnownHash" variants
|
// "KnownHash" variants
|
||||||
// Export for '_asyncio' shared extension
|
// Export for '_asyncio' shared extension
|
||||||
|
|
|
@ -1945,30 +1945,36 @@ enter_task(asyncio_state *state, PyObject *loop, PyObject *task)
|
||||||
return 0;
|
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
|
static int
|
||||||
leave_task(asyncio_state *state, PyObject *loop, PyObject *task)
|
leave_task(asyncio_state *state, PyObject *loop, PyObject *task)
|
||||||
/*[clinic end generated code: output=0ebf6db4b858fb41 input=51296a46313d1ad8]*/
|
/*[clinic end generated code: output=0ebf6db4b858fb41 input=51296a46313d1ad8]*/
|
||||||
{
|
{
|
||||||
PyObject *item;
|
int res = _PyDict_DelItemIf(state->current_tasks, loop,
|
||||||
Py_hash_t hash;
|
leave_task_predicate, task);
|
||||||
hash = PyObject_Hash(loop);
|
if (res == 0) {
|
||||||
if (hash == -1) {
|
// task was not found
|
||||||
return -1;
|
return err_leave_task(Py_None, task);
|
||||||
}
|
}
|
||||||
item = _PyDict_GetItem_KnownHash(state->current_tasks, loop, hash);
|
return res;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
|
|
@ -31,7 +31,7 @@ _weakref_getweakrefcount_impl(PyObject *module, PyObject *object)
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
is_dead_weakref(PyObject *value)
|
is_dead_weakref(PyObject *value, void *unused)
|
||||||
{
|
{
|
||||||
if (!PyWeakref_Check(value)) {
|
if (!PyWeakref_Check(value)) {
|
||||||
PyErr_SetString(PyExc_TypeError, "not a weakref");
|
PyErr_SetString(PyExc_TypeError, "not a weakref");
|
||||||
|
@ -56,15 +56,8 @@ _weakref__remove_dead_weakref_impl(PyObject *module, PyObject *dct,
|
||||||
PyObject *key)
|
PyObject *key)
|
||||||
/*[clinic end generated code: output=d9ff53061fcb875c input=19fc91f257f96a1d]*/
|
/*[clinic end generated code: output=d9ff53061fcb875c input=19fc91f257f96a1d]*/
|
||||||
{
|
{
|
||||||
if (_PyDict_DelItemIf(dct, key, is_dead_weakref) < 0) {
|
if (_PyDict_DelItemIf(dct, key, is_dead_weakref, NULL) < 0) {
|
||||||
if (PyErr_ExceptionMatches(PyExc_KeyError))
|
return NULL;
|
||||||
/* 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;
|
|
||||||
}
|
}
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2567,7 +2567,7 @@ delete_index_from_values(PyDictValues *values, Py_ssize_t ix)
|
||||||
values->size = size;
|
values->size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static void
|
||||||
delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix,
|
delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix,
|
||||||
PyObject *old_value, uint64_t new_version)
|
PyObject *old_value, uint64_t new_version)
|
||||||
{
|
{
|
||||||
|
@ -2609,7 +2609,6 @@ delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix,
|
||||||
Py_DECREF(old_value);
|
Py_DECREF(old_value);
|
||||||
|
|
||||||
ASSERT_CONSISTENT(mp);
|
ASSERT_CONSISTENT(mp);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -2652,7 +2651,8 @@ delitem_knownhash_lock_held(PyObject *op, PyObject *key, Py_hash_t hash)
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
uint64_t new_version = _PyDict_NotifyEvent(
|
uint64_t new_version = _PyDict_NotifyEvent(
|
||||||
interp, PyDict_EVENT_DELETED, mp, key, NULL);
|
interp, PyDict_EVENT_DELETED, mp, key, NULL);
|
||||||
return delitem_common(mp, hash, ix, old_value, new_version);
|
delitem_common(mp, hash, ix, old_value, new_version);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -2667,7 +2667,8 @@ _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
|
||||||
|
|
||||||
static int
|
static int
|
||||||
delitemif_lock_held(PyObject *op, PyObject *key,
|
delitemif_lock_held(PyObject *op, PyObject *key,
|
||||||
int (*predicate)(PyObject *value))
|
int (*predicate)(PyObject *value, void *arg),
|
||||||
|
void *arg)
|
||||||
{
|
{
|
||||||
Py_ssize_t ix;
|
Py_ssize_t ix;
|
||||||
PyDictObject *mp;
|
PyDictObject *mp;
|
||||||
|
@ -2677,24 +2678,20 @@ delitemif_lock_held(PyObject *op, PyObject *key,
|
||||||
|
|
||||||
ASSERT_DICT_LOCKED(op);
|
ASSERT_DICT_LOCKED(op);
|
||||||
|
|
||||||
if (!PyDict_Check(op)) {
|
|
||||||
PyErr_BadInternalCall();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
assert(key);
|
assert(key);
|
||||||
hash = PyObject_Hash(key);
|
hash = PyObject_Hash(key);
|
||||||
if (hash == -1)
|
if (hash == -1)
|
||||||
return -1;
|
return -1;
|
||||||
mp = (PyDictObject *)op;
|
mp = (PyDictObject *)op;
|
||||||
ix = _Py_dict_lookup(mp, key, hash, &old_value);
|
ix = _Py_dict_lookup(mp, key, hash, &old_value);
|
||||||
if (ix == DKIX_ERROR)
|
if (ix == DKIX_ERROR) {
|
||||||
return -1;
|
|
||||||
if (ix == DKIX_EMPTY || old_value == NULL) {
|
|
||||||
_PyErr_SetKeyError(key);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (ix == DKIX_EMPTY || old_value == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
res = predicate(old_value);
|
res = predicate(old_value, arg);
|
||||||
if (res == -1)
|
if (res == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -2702,7 +2699,8 @@ delitemif_lock_held(PyObject *op, PyObject *key,
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
uint64_t new_version = _PyDict_NotifyEvent(
|
uint64_t new_version = _PyDict_NotifyEvent(
|
||||||
interp, PyDict_EVENT_DELETED, mp, key, NULL);
|
interp, PyDict_EVENT_DELETED, mp, key, NULL);
|
||||||
return delitem_common(mp, hash, ix, old_value, new_version);
|
delitem_common(mp, hash, ix, old_value, new_version);
|
||||||
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2714,11 +2712,13 @@ delitemif_lock_held(PyObject *op, PyObject *key,
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
_PyDict_DelItemIf(PyObject *op, PyObject *key,
|
_PyDict_DelItemIf(PyObject *op, PyObject *key,
|
||||||
int (*predicate)(PyObject *value))
|
int (*predicate)(PyObject *value, void *arg),
|
||||||
|
void *arg)
|
||||||
{
|
{
|
||||||
|
assert(PyDict_Check(op));
|
||||||
int res;
|
int res;
|
||||||
Py_BEGIN_CRITICAL_SECTION(op);
|
Py_BEGIN_CRITICAL_SECTION(op);
|
||||||
res = delitemif_lock_held(op, key, predicate);
|
res = delitemif_lock_held(op, key, predicate, arg);
|
||||||
Py_END_CRITICAL_SECTION();
|
Py_END_CRITICAL_SECTION();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue