Skip to content

Commit 6a87edc

Browse files
colesburymiss-islington
authored andcommitted
pythongh-120974: Make asyncio swap_current_task safe in free-threaded build (pythonGH-122317)
* pythongh-120974: Make asyncio `swap_current_task` safe in free-threaded build (cherry picked from commit b5e6fb3) Co-authored-by: Sam Gross <[email protected]>
1 parent 56435a8 commit 6a87edc

File tree

3 files changed

+67
-31
lines changed

3 files changed

+67
-31
lines changed

Include/internal/pycore_dict.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,13 @@ PyAPI_FUNC(PyObject *)_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObjec
110110
/* Consumes references to key and value */
111111
PyAPI_FUNC(int) _PyDict_SetItem_Take2(PyDictObject *op, PyObject *key, PyObject *value);
112112
extern int _PyDict_SetItem_LockHeld(PyDictObject *dict, PyObject *name, PyObject *value);
113-
extern int _PyDict_GetItemRef_Unicode_LockHeld(PyDictObject *op, PyObject *key, PyObject **result);
113+
// Export for '_asyncio' shared extension
114+
PyAPI_FUNC(int) _PyDict_SetItem_KnownHash_LockHeld(PyDictObject *mp, PyObject *key,
115+
PyObject *value, Py_hash_t hash);
116+
// Export for '_asyncio' shared extension
117+
PyAPI_FUNC(int) _PyDict_GetItemRef_KnownHash_LockHeld(PyDictObject *op, PyObject *key, Py_hash_t hash, PyObject **result);
114118
extern int _PyDict_GetItemRef_KnownHash(PyDictObject *op, PyObject *key, Py_hash_t hash, PyObject **result);
119+
extern int _PyDict_GetItemRef_Unicode_LockHeld(PyDictObject *op, PyObject *key, PyObject **result);
115120
extern int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject *obj, PyObject **dictptr, PyObject *name, PyObject *value);
116121

117122
extern int _PyDict_Pop_KnownHash(

Modules/_asynciomodule.c

+23-14
Original file line numberDiff line numberDiff line change
@@ -1977,6 +1977,24 @@ leave_task(asyncio_state *state, PyObject *loop, PyObject *task)
19771977
return res;
19781978
}
19791979

1980+
static PyObject *
1981+
swap_current_task_lock_held(PyDictObject *current_tasks, PyObject *loop,
1982+
Py_hash_t hash, PyObject *task)
1983+
{
1984+
PyObject *prev_task;
1985+
if (_PyDict_GetItemRef_KnownHash_LockHeld(current_tasks, loop, hash, &prev_task) < 0) {
1986+
return NULL;
1987+
}
1988+
if (_PyDict_SetItem_KnownHash_LockHeld(current_tasks, loop, task, hash) < 0) {
1989+
Py_XDECREF(prev_task);
1990+
return NULL;
1991+
}
1992+
if (prev_task == NULL) {
1993+
Py_RETURN_NONE;
1994+
}
1995+
return prev_task;
1996+
}
1997+
19801998
static PyObject *
19811999
swap_current_task(asyncio_state *state, PyObject *loop, PyObject *task)
19822000
{
@@ -1992,24 +2010,15 @@ swap_current_task(asyncio_state *state, PyObject *loop, PyObject *task)
19922010
return prev_task;
19932011
}
19942012

1995-
Py_hash_t hash;
1996-
hash = PyObject_Hash(loop);
2013+
Py_hash_t hash = PyObject_Hash(loop);
19972014
if (hash == -1) {
19982015
return NULL;
19992016
}
2000-
prev_task = _PyDict_GetItem_KnownHash(state->current_tasks, loop, hash);
2001-
if (prev_task == NULL) {
2002-
if (PyErr_Occurred()) {
2003-
return NULL;
2004-
}
2005-
prev_task = Py_None;
2006-
}
2007-
Py_INCREF(prev_task);
2008-
if (_PyDict_SetItem_KnownHash(state->current_tasks, loop, task, hash) == -1) {
2009-
Py_DECREF(prev_task);
2010-
return NULL;
2011-
}
20122017

2018+
PyDictObject *current_tasks = (PyDictObject *)state->current_tasks;
2019+
Py_BEGIN_CRITICAL_SECTION(current_tasks);
2020+
prev_task = swap_current_task_lock_held(current_tasks, loop, hash, task);
2021+
Py_END_CRITICAL_SECTION();
20132022
return prev_task;
20142023
}
20152024

Objects/dictobject.c

+38-16
Original file line numberDiff line numberDiff line change
@@ -2275,6 +2275,29 @@ _PyDict_GetItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
22752275
return value; // borrowed reference
22762276
}
22772277

2278+
/* Gets an item and provides a new reference if the value is present.
2279+
* Returns 1 if the key is present, 0 if the key is missing, and -1 if an
2280+
* exception occurred.
2281+
*/
2282+
int
2283+
_PyDict_GetItemRef_KnownHash_LockHeld(PyDictObject *op, PyObject *key,
2284+
Py_hash_t hash, PyObject **result)
2285+
{
2286+
PyObject *value;
2287+
Py_ssize_t ix = _Py_dict_lookup(op, key, hash, &value);
2288+
assert(ix >= 0 || value == NULL);
2289+
if (ix == DKIX_ERROR) {
2290+
*result = NULL;
2291+
return -1;
2292+
}
2293+
if (value == NULL) {
2294+
*result = NULL;
2295+
return 0; // missing key
2296+
}
2297+
*result = Py_NewRef(value);
2298+
return 1; // key is present
2299+
}
2300+
22782301
/* Gets an item and provides a new reference if the value is present.
22792302
* Returns 1 if the key is present, 0 if the key is missing, and -1 if an
22802303
* exception occurred.
@@ -2519,33 +2542,32 @@ setitem_lock_held(PyDictObject *mp, PyObject *key, PyObject *value)
25192542

25202543

25212544
int
2522-
_PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value,
2523-
Py_hash_t hash)
2545+
_PyDict_SetItem_KnownHash_LockHeld(PyDictObject *mp, PyObject *key, PyObject *value,
2546+
Py_hash_t hash)
25242547
{
2525-
PyDictObject *mp;
2548+
PyInterpreterState *interp = _PyInterpreterState_GET();
2549+
if (mp->ma_keys == Py_EMPTY_KEYS) {
2550+
return insert_to_emptydict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value));
2551+
}
2552+
/* insertdict() handles any resizing that might be necessary */
2553+
return insertdict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value));
2554+
}
25262555

2556+
int
2557+
_PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value,
2558+
Py_hash_t hash)
2559+
{
25272560
if (!PyDict_Check(op)) {
25282561
PyErr_BadInternalCall();
25292562
return -1;
25302563
}
25312564
assert(key);
25322565
assert(value);
25332566
assert(hash != -1);
2534-
mp = (PyDictObject *)op;
25352567

25362568
int res;
2537-
PyInterpreterState *interp = _PyInterpreterState_GET();
2538-
2539-
Py_BEGIN_CRITICAL_SECTION(mp);
2540-
2541-
if (mp->ma_keys == Py_EMPTY_KEYS) {
2542-
res = insert_to_emptydict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value));
2543-
}
2544-
else {
2545-
/* insertdict() handles any resizing that might be necessary */
2546-
res = insertdict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value));
2547-
}
2548-
2569+
Py_BEGIN_CRITICAL_SECTION(op);
2570+
res = _PyDict_SetItem_KnownHash_LockHeld((PyDictObject *)op, key, value, hash);
25492571
Py_END_CRITICAL_SECTION();
25502572
return res;
25512573
}

0 commit comments

Comments
 (0)