Skip to content

Commit 75c5519

Browse files
gh-91048: fix thread safety for asyncio stack introspection APIs (#129399)
1 parent 8b2fb62 commit 75c5519

File tree

2 files changed

+124
-99
lines changed

2 files changed

+124
-99
lines changed

Modules/_asynciomodule.c

+98-73
Original file line numberDiff line numberDiff line change
@@ -549,30 +549,28 @@ future_init(FutureObj *fut, PyObject *loop)
549549
}
550550

551551
static int
552-
future_awaited_by_add(asyncio_state *state, PyObject *fut, PyObject *thing)
552+
future_awaited_by_add(asyncio_state *state, FutureObj *fut, PyObject *thing)
553553
{
554-
if (!TaskOrFuture_Check(state, fut) || !TaskOrFuture_Check(state, thing)) {
555-
// We only want to support native asyncio Futures.
556-
// For further insight see the comment in the Python
557-
// implementation of "future_add_to_awaited_by()".
558-
return 0;
559-
}
560-
561-
FutureObj *_fut = (FutureObj *)fut;
554+
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut);
555+
// We only want to support native asyncio Futures.
556+
// For further insight see the comment in the Python
557+
// implementation of "future_add_to_awaited_by()".
558+
assert(TaskOrFuture_Check(state, fut));
559+
assert(TaskOrFuture_Check(state, thing));
562560

563561
/* Most futures/task are only awaited by one entity, so we want
564562
to avoid always creating a set for `fut_awaited_by`.
565563
*/
566-
if (_fut->fut_awaited_by == NULL) {
567-
assert(!_fut->fut_awaited_by_is_set);
564+
if (fut->fut_awaited_by == NULL) {
565+
assert(!fut->fut_awaited_by_is_set);
568566
Py_INCREF(thing);
569-
_fut->fut_awaited_by = thing;
567+
fut->fut_awaited_by = thing;
570568
return 0;
571569
}
572570

573-
if (_fut->fut_awaited_by_is_set) {
574-
assert(PySet_CheckExact(_fut->fut_awaited_by));
575-
return PySet_Add(_fut->fut_awaited_by, thing);
571+
if (fut->fut_awaited_by_is_set) {
572+
assert(PySet_CheckExact(fut->fut_awaited_by));
573+
return PySet_Add(fut->fut_awaited_by, thing);
576574
}
577575

578576
PyObject *set = PySet_New(NULL);
@@ -583,40 +581,38 @@ future_awaited_by_add(asyncio_state *state, PyObject *fut, PyObject *thing)
583581
Py_DECREF(set);
584582
return -1;
585583
}
586-
if (PySet_Add(set, _fut->fut_awaited_by)) {
584+
if (PySet_Add(set, fut->fut_awaited_by)) {
587585
Py_DECREF(set);
588586
return -1;
589587
}
590-
Py_SETREF(_fut->fut_awaited_by, set);
591-
_fut->fut_awaited_by_is_set = 1;
588+
Py_SETREF(fut->fut_awaited_by, set);
589+
fut->fut_awaited_by_is_set = 1;
592590
return 0;
593591
}
594592

595593
static int
596-
future_awaited_by_discard(asyncio_state *state, PyObject *fut, PyObject *thing)
594+
future_awaited_by_discard(asyncio_state *state, FutureObj *fut, PyObject *thing)
597595
{
598-
if (!TaskOrFuture_Check(state, fut) || !TaskOrFuture_Check(state, thing)) {
599-
// We only want to support native asyncio Futures.
600-
// For further insight see the comment in the Python
601-
// implementation of "future_add_to_awaited_by()".
602-
return 0;
603-
}
604-
605-
FutureObj *_fut = (FutureObj *)fut;
596+
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut);
597+
// We only want to support native asyncio Futures.
598+
// For further insight see the comment in the Python
599+
// implementation of "future_add_to_awaited_by()".
600+
assert(TaskOrFuture_Check(state, fut));
601+
assert(TaskOrFuture_Check(state, thing));
606602

607603
/* Following the semantics of 'set.discard()' here in not
608604
raising an error if `thing` isn't in the `awaited_by` "set".
609605
*/
610-
if (_fut->fut_awaited_by == NULL) {
606+
if (fut->fut_awaited_by == NULL) {
611607
return 0;
612608
}
613-
if (_fut->fut_awaited_by == thing) {
614-
Py_CLEAR(_fut->fut_awaited_by);
609+
if (fut->fut_awaited_by == thing) {
610+
Py_CLEAR(fut->fut_awaited_by);
615611
return 0;
616612
}
617-
if (_fut->fut_awaited_by_is_set) {
618-
assert(PySet_CheckExact(_fut->fut_awaited_by));
619-
int err = PySet_Discard(_fut->fut_awaited_by, thing);
613+
if (fut->fut_awaited_by_is_set) {
614+
assert(PySet_CheckExact(fut->fut_awaited_by));
615+
int err = PySet_Discard(fut->fut_awaited_by, thing);
620616
if (err < 0) {
621617
return -1;
622618
} else {
@@ -626,36 +622,6 @@ future_awaited_by_discard(asyncio_state *state, PyObject *fut, PyObject *thing)
626622
return 0;
627623
}
628624

629-
/*[clinic input]
630-
@critical_section
631-
@getter
632-
_asyncio.Future._asyncio_awaited_by
633-
[clinic start generated code]*/
634-
635-
static PyObject *
636-
_asyncio_Future__asyncio_awaited_by_get_impl(FutureObj *self)
637-
/*[clinic end generated code: output=932af76d385d2e2a input=64c1783df2d44d2b]*/
638-
{
639-
/* Implementation of a Python getter. */
640-
if (self->fut_awaited_by == NULL) {
641-
Py_RETURN_NONE;
642-
}
643-
if (self->fut_awaited_by_is_set) {
644-
/* Already a set, just wrap it into a frozen set and return. */
645-
assert(PySet_CheckExact(self->fut_awaited_by));
646-
return PyFrozenSet_New(self->fut_awaited_by);
647-
}
648-
649-
PyObject *set = PyFrozenSet_New(NULL);
650-
if (set == NULL) {
651-
return NULL;
652-
}
653-
if (PySet_Add(set, self->fut_awaited_by)) {
654-
Py_DECREF(set);
655-
return NULL;
656-
}
657-
return set;
658-
}
659625

660626
static PyObject *
661627
future_set_result(asyncio_state *state, FutureObj *fut, PyObject *res)
@@ -1362,6 +1328,38 @@ _asyncio_Future_get_loop_impl(FutureObj *self, PyTypeObject *cls)
13621328
return Py_NewRef(self->fut_loop);
13631329
}
13641330

1331+
/*[clinic input]
1332+
@critical_section
1333+
@getter
1334+
_asyncio.Future._asyncio_awaited_by
1335+
[clinic start generated code]*/
1336+
1337+
static PyObject *
1338+
_asyncio_Future__asyncio_awaited_by_get_impl(FutureObj *self)
1339+
/*[clinic end generated code: output=932af76d385d2e2a input=64c1783df2d44d2b]*/
1340+
{
1341+
/* Implementation of a Python getter. */
1342+
if (self->fut_awaited_by == NULL) {
1343+
Py_RETURN_NONE;
1344+
}
1345+
if (self->fut_awaited_by_is_set) {
1346+
/* Already a set, just wrap it into a frozen set and return. */
1347+
assert(PySet_CheckExact(self->fut_awaited_by));
1348+
return PyFrozenSet_New(self->fut_awaited_by);
1349+
}
1350+
1351+
PyObject *set = PyFrozenSet_New(NULL);
1352+
if (set == NULL) {
1353+
return NULL;
1354+
}
1355+
if (PySet_Add(set, self->fut_awaited_by)) {
1356+
Py_DECREF(set);
1357+
return NULL;
1358+
}
1359+
return set;
1360+
}
1361+
1362+
13651363
/*[clinic input]
13661364
@critical_section
13671365
@getter
@@ -3298,8 +3296,11 @@ task_step_handle_result_impl(asyncio_state *state, TaskObj *task, PyObject *resu
32983296
if (!fut->fut_blocking) {
32993297
goto yield_insteadof_yf;
33003298
}
3301-
3302-
if (future_awaited_by_add(state, result, (PyObject *)task)) {
3299+
int res;
3300+
Py_BEGIN_CRITICAL_SECTION(result);
3301+
res = future_awaited_by_add(state, (FutureObj *)result, (PyObject *)task);
3302+
Py_END_CRITICAL_SECTION();
3303+
if (res) {
33033304
goto fail;
33043305
}
33053306

@@ -3392,8 +3393,14 @@ task_step_handle_result_impl(asyncio_state *state, TaskObj *task, PyObject *resu
33923393
goto yield_insteadof_yf;
33933394
}
33943395

3395-
if (future_awaited_by_add(state, result, (PyObject *)task)) {
3396-
goto fail;
3396+
if (TaskOrFuture_Check(state, result)) {
3397+
int res;
3398+
Py_BEGIN_CRITICAL_SECTION(result);
3399+
res = future_awaited_by_add(state, (FutureObj *)result, (PyObject *)task);
3400+
Py_END_CRITICAL_SECTION();
3401+
if (res) {
3402+
goto fail;
3403+
}
33973404
}
33983405

33993406
/* result._asyncio_future_blocking = False */
@@ -3608,8 +3615,14 @@ task_wakeup_lock_held(TaskObj *task, PyObject *o)
36083615

36093616
asyncio_state *state = get_asyncio_state_by_def((PyObject *)task);
36103617

3611-
if (future_awaited_by_discard(state, o, (PyObject *)task)) {
3612-
return NULL;
3618+
if (TaskOrFuture_Check(state, o)) {
3619+
int res;
3620+
Py_BEGIN_CRITICAL_SECTION(o);
3621+
res = future_awaited_by_discard(state, (FutureObj *)o, (PyObject *)task);
3622+
Py_END_CRITICAL_SECTION();
3623+
if (res) {
3624+
return NULL;
3625+
}
36133626
}
36143627

36153628
if (Future_CheckExact(state, o) || Task_CheckExact(state, o)) {
@@ -4112,8 +4125,14 @@ _asyncio_future_add_to_awaited_by_impl(PyObject *module, PyObject *fut,
41124125
/*[clinic end generated code: output=0ab9a1a63389e4df input=06e6eaac51f532b9]*/
41134126
{
41144127
asyncio_state *state = get_asyncio_state(module);
4115-
if (future_awaited_by_add(state, fut, waiter)) {
4116-
return NULL;
4128+
if (TaskOrFuture_Check(state, fut) && TaskOrFuture_Check(state, waiter)) {
4129+
int res;
4130+
Py_BEGIN_CRITICAL_SECTION(fut);
4131+
res = future_awaited_by_add(state, (FutureObj *)fut, waiter);
4132+
Py_END_CRITICAL_SECTION();
4133+
if (res) {
4134+
return NULL;
4135+
}
41174136
}
41184137
Py_RETURN_NONE;
41194138
}
@@ -4133,8 +4152,14 @@ _asyncio_future_discard_from_awaited_by_impl(PyObject *module, PyObject *fut,
41334152
/*[clinic end generated code: output=a03b0b4323b779de input=3833f7639e88e483]*/
41344153
{
41354154
asyncio_state *state = get_asyncio_state(module);
4136-
if (future_awaited_by_discard(state, fut, waiter)) {
4137-
return NULL;
4155+
if (TaskOrFuture_Check(state, fut) && TaskOrFuture_Check(state, waiter)) {
4156+
int res;
4157+
Py_BEGIN_CRITICAL_SECTION(fut);
4158+
res = future_awaited_by_add(state, (FutureObj *)fut, waiter);
4159+
Py_END_CRITICAL_SECTION();
4160+
if (res) {
4161+
return NULL;
4162+
}
41384163
}
41394164
Py_RETURN_NONE;
41404165
}

Modules/clinic/_asynciomodule.c.h

+26-26
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)