Skip to content

Commit 8cc6498

Browse files
committed
pythongh-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.
1 parent 69f2dc5 commit 8cc6498

File tree

4 files changed

+27
-35
lines changed

4 files changed

+27
-35
lines changed

Include/internal/pycore_dict.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ extern "C" {
1414
// Unsafe flavor of PyDict_GetItemWithError(): no error checking
1515
extern PyObject* _PyDict_GetItemWithError(PyObject *dp, PyObject *key);
1616

17-
extern int _PyDict_DelItemIf(PyObject *mp, PyObject *key,
18-
int (*predicate)(PyObject *value));
17+
// Export for '_asyncio' shared extension
18+
PyAPI_FUNC(int) _PyDict_DelItemIf(PyObject *mp, PyObject *key,
19+
int (*predicate)(PyObject *value, void *arg),
20+
void *arg);
1921

2022
// "KnownHash" variants
2123
// Export for '_asyncio' shared extension

Modules/_asynciomodule.c

+10-11
Original file line numberDiff line numberDiff line change
@@ -2031,18 +2031,9 @@ enter_task(asyncio_state *state, PyObject *loop, PyObject *task)
20312031
return _PyDict_SetItem_KnownHash(state->current_tasks, loop, task, hash);
20322032
}
20332033

2034-
20352034
static int
2036-
leave_task(asyncio_state *state, PyObject *loop, PyObject *task)
2037-
/*[clinic end generated code: output=0ebf6db4b858fb41 input=51296a46313d1ad8]*/
2035+
leave_task_predicate(PyObject *item, void *task)
20382036
{
2039-
PyObject *item;
2040-
Py_hash_t hash;
2041-
hash = PyObject_Hash(loop);
2042-
if (hash == -1) {
2043-
return -1;
2044-
}
2045-
item = _PyDict_GetItem_KnownHash(state->current_tasks, loop, hash);
20462037
if (item != task) {
20472038
if (item == NULL) {
20482039
/* Not entered, replace with None */
@@ -2054,7 +2045,15 @@ leave_task(asyncio_state *state, PyObject *loop, PyObject *task)
20542045
task, item, NULL);
20552046
return -1;
20562047
}
2057-
return _PyDict_DelItem_KnownHash(state->current_tasks, loop, hash);
2048+
return 1;
2049+
}
2050+
2051+
static int
2052+
leave_task(asyncio_state *state, PyObject *loop, PyObject *task)
2053+
/*[clinic end generated code: output=0ebf6db4b858fb41 input=51296a46313d1ad8]*/
2054+
{
2055+
return _PyDict_DelItemIf(state->current_tasks, loop, leave_task_predicate,
2056+
task);
20582057
}
20592058

20602059
static PyObject *

Modules/_weakref.c

+3-10
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ _weakref_getweakrefcount_impl(PyObject *module, PyObject *object)
3131

3232

3333
static int
34-
is_dead_weakref(PyObject *value)
34+
is_dead_weakref(PyObject *value, void *unused)
3535
{
3636
if (!PyWeakref_Check(value)) {
3737
PyErr_SetString(PyExc_TypeError, "not a weakref");
@@ -56,15 +56,8 @@ _weakref__remove_dead_weakref_impl(PyObject *module, PyObject *dct,
5656
PyObject *key)
5757
/*[clinic end generated code: output=d9ff53061fcb875c input=19fc91f257f96a1d]*/
5858
{
59-
if (_PyDict_DelItemIf(dct, key, is_dead_weakref) < 0) {
60-
if (PyErr_ExceptionMatches(PyExc_KeyError))
61-
/* This function is meant to allow safe weak-value dicts
62-
with GC in another thread (see issue #28427), so it's
63-
ok if the key doesn't exist anymore.
64-
*/
65-
PyErr_Clear();
66-
else
67-
return NULL;
59+
if (_PyDict_DelItemIf(dct, key, is_dead_weakref, NULL) < 0) {
60+
return NULL;
6861
}
6962
Py_RETURN_NONE;
7063
}

Objects/dictobject.c

+10-12
Original file line numberDiff line numberDiff line change
@@ -2608,7 +2608,8 @@ _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
26082608

26092609
static int
26102610
delitemif_lock_held(PyObject *op, PyObject *key,
2611-
int (*predicate)(PyObject *value))
2611+
int (*predicate)(PyObject *value, void *arg),
2612+
void *arg)
26122613
{
26132614
Py_ssize_t ix;
26142615
PyDictObject *mp;
@@ -2618,10 +2619,6 @@ delitemif_lock_held(PyObject *op, PyObject *key,
26182619

26192620
ASSERT_DICT_LOCKED(op);
26202621

2621-
if (!PyDict_Check(op)) {
2622-
PyErr_BadInternalCall();
2623-
return -1;
2624-
}
26252622
assert(key);
26262623
hash = PyObject_Hash(key);
26272624
if (hash == -1)
@@ -2630,12 +2627,8 @@ delitemif_lock_held(PyObject *op, PyObject *key,
26302627
ix = _Py_dict_lookup(mp, key, hash, &old_value);
26312628
if (ix == DKIX_ERROR)
26322629
return -1;
2633-
if (ix == DKIX_EMPTY || old_value == NULL) {
2634-
_PyErr_SetKeyError(key);
2635-
return -1;
2636-
}
26372630

2638-
res = predicate(old_value);
2631+
res = predicate(old_value, arg);
26392632
if (res == -1)
26402633
return -1;
26412634

@@ -2655,11 +2648,16 @@ delitemif_lock_held(PyObject *op, PyObject *key,
26552648
*/
26562649
int
26572650
_PyDict_DelItemIf(PyObject *op, PyObject *key,
2658-
int (*predicate)(PyObject *value))
2651+
int (*predicate)(PyObject *value, void *arg),
2652+
void *arg)
26592653
{
2654+
if (!PyDict_Check(op)) {
2655+
PyErr_BadInternalCall();
2656+
return -1;
2657+
}
26602658
int res;
26612659
Py_BEGIN_CRITICAL_SECTION(op);
2662-
res = delitemif_lock_held(op, key, predicate);
2660+
res = delitemif_lock_held(op, key, predicate, arg);
26632661
Py_END_CRITICAL_SECTION();
26642662
return res;
26652663
}

0 commit comments

Comments
 (0)