Skip to content

Commit 37e9a7d

Browse files
fix thread safety
1 parent 0ef8d47 commit 37e9a7d

File tree

2 files changed

+124
-99
lines changed

2 files changed

+124
-99
lines changed

Modules/_asynciomodule.c

Lines changed: 98 additions & 73 deletions
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
@@ -3296,8 +3294,11 @@ task_step_handle_result_impl(asyncio_state *state, TaskObj *task, PyObject *resu
32963294
if (!fut->fut_blocking) {
32973295
goto yield_insteadof_yf;
32983296
}
3299-
3300-
if (future_awaited_by_add(state, result, (PyObject *)task)) {
3297+
int res;
3298+
Py_BEGIN_CRITICAL_SECTION(result);
3299+
res = future_awaited_by_add(state, (FutureObj *)result, (PyObject *)task);
3300+
Py_END_CRITICAL_SECTION();
3301+
if (res) {
33013302
goto fail;
33023303
}
33033304

@@ -3390,8 +3391,14 @@ task_step_handle_result_impl(asyncio_state *state, TaskObj *task, PyObject *resu
33903391
goto yield_insteadof_yf;
33913392
}
33923393

3393-
if (future_awaited_by_add(state, result, (PyObject *)task)) {
3394-
goto fail;
3394+
if (TaskOrFuture_Check(state, result)) {
3395+
int res;
3396+
Py_BEGIN_CRITICAL_SECTION(result);
3397+
res = future_awaited_by_add(state, (FutureObj *)result, (PyObject *)task);
3398+
Py_END_CRITICAL_SECTION();
3399+
if (res) {
3400+
goto fail;
3401+
}
33953402
}
33963403

33973404
/* result._asyncio_future_blocking = False */
@@ -3606,8 +3613,14 @@ task_wakeup_lock_held(TaskObj *task, PyObject *o)
36063613

36073614
asyncio_state *state = get_asyncio_state_by_def((PyObject *)task);
36083615

3609-
if (future_awaited_by_discard(state, o, (PyObject *)task)) {
3610-
return NULL;
3616+
if (TaskOrFuture_Check(state, o)) {
3617+
int res;
3618+
Py_BEGIN_CRITICAL_SECTION(o);
3619+
res = future_awaited_by_discard(state, (FutureObj *)o, (PyObject *)task);
3620+
Py_END_CRITICAL_SECTION();
3621+
if (res) {
3622+
return NULL;
3623+
}
36113624
}
36123625

36133626
if (Future_CheckExact(state, o) || Task_CheckExact(state, o)) {
@@ -4110,8 +4123,14 @@ _asyncio_future_add_to_awaited_by_impl(PyObject *module, PyObject *fut,
41104123
/*[clinic end generated code: output=0ab9a1a63389e4df input=06e6eaac51f532b9]*/
41114124
{
41124125
asyncio_state *state = get_asyncio_state(module);
4113-
if (future_awaited_by_add(state, fut, waiter)) {
4114-
return NULL;
4126+
if (TaskOrFuture_Check(state, fut) && TaskOrFuture_Check(state, waiter)) {
4127+
int res;
4128+
Py_BEGIN_CRITICAL_SECTION(fut);
4129+
res = future_awaited_by_add(state, (FutureObj *)fut, waiter);
4130+
Py_END_CRITICAL_SECTION();
4131+
if (res) {
4132+
return NULL;
4133+
}
41154134
}
41164135
Py_RETURN_NONE;
41174136
}
@@ -4131,8 +4150,14 @@ _asyncio_future_discard_from_awaited_by_impl(PyObject *module, PyObject *fut,
41314150
/*[clinic end generated code: output=a03b0b4323b779de input=3833f7639e88e483]*/
41324151
{
41334152
asyncio_state *state = get_asyncio_state(module);
4134-
if (future_awaited_by_discard(state, fut, waiter)) {
4135-
return NULL;
4153+
if (TaskOrFuture_Check(state, fut) && TaskOrFuture_Check(state, waiter)) {
4154+
int res;
4155+
Py_BEGIN_CRITICAL_SECTION(fut);
4156+
res = future_awaited_by_add(state, (FutureObj *)fut, waiter);
4157+
Py_END_CRITICAL_SECTION();
4158+
if (res) {
4159+
return NULL;
4160+
}
41364161
}
41374162
Py_RETURN_NONE;
41384163
}

Modules/clinic/_asynciomodule.c.h

Lines changed: 26 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)