mirror of
https://github.com/python/cpython.git
synced 2025-12-31 12:33:28 +00:00
gh-120321: Make gi_frame_state transitions atomic in FT build (gh-142599)
This makes generator frame state transitions atomic in the free threading build, which avoids segfaults when trying to execute a generator from multiple threads concurrently. There are still a few operations that aren't thread-safe and may crash if performed concurrently on the same generator/coroutine: * Accessing gi_yieldfrom/cr_await/ag_await * Accessing gi_frame/cr_frame/ag_frame * Async generator operations
This commit is contained in:
parent
e2a7db7175
commit
08bc03ff2a
16 changed files with 1124 additions and 883 deletions
|
|
@ -1312,14 +1312,13 @@ dummy_func(
|
|||
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
|
||||
if ((tstate->interp->eval_frame == NULL) &&
|
||||
(Py_TYPE(receiver_o) == &PyGen_Type || Py_TYPE(receiver_o) == &PyCoro_Type) &&
|
||||
((PyGenObject *)receiver_o)->gi_frame_state < FRAME_EXECUTING)
|
||||
gen_try_set_executing((PyGenObject *)receiver_o))
|
||||
{
|
||||
PyGenObject *gen = (PyGenObject *)receiver_o;
|
||||
_PyInterpreterFrame *gen_frame = &gen->gi_iframe;
|
||||
_PyFrame_StackPush(gen_frame, PyStackRef_MakeHeapSafe(v));
|
||||
DEAD(v);
|
||||
SYNC_SP();
|
||||
gen->gi_frame_state = FRAME_EXECUTING;
|
||||
gen->gi_exc_state.previous_item = tstate->exc_info;
|
||||
tstate->exc_info = &gen->gi_exc_state;
|
||||
assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX);
|
||||
|
|
@ -1360,12 +1359,11 @@ dummy_func(
|
|||
op(_SEND_GEN_FRAME, (receiver, v -- receiver, gen_frame)) {
|
||||
PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(receiver);
|
||||
DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type);
|
||||
DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING);
|
||||
DEOPT_IF(!gen_try_set_executing((PyGenObject *)gen));
|
||||
STAT_INC(SEND, hit);
|
||||
_PyInterpreterFrame *pushed_frame = &gen->gi_iframe;
|
||||
_PyFrame_StackPush(pushed_frame, PyStackRef_MakeHeapSafe(v));
|
||||
DEAD(v);
|
||||
gen->gi_frame_state = FRAME_EXECUTING;
|
||||
gen->gi_exc_state.previous_item = tstate->exc_info;
|
||||
tstate->exc_info = &gen->gi_exc_state;
|
||||
assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX);
|
||||
|
|
@ -1389,7 +1387,6 @@ dummy_func(
|
|||
PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame);
|
||||
assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1);
|
||||
assert(oparg == 0 || oparg == 1);
|
||||
gen->gi_frame_state = FRAME_SUSPENDED + oparg;
|
||||
_PyStackRef temp = retval;
|
||||
DEAD(retval);
|
||||
SAVE_STACK();
|
||||
|
|
@ -1399,6 +1396,8 @@ dummy_func(
|
|||
_PyInterpreterFrame *gen_frame = frame;
|
||||
frame = tstate->current_frame = frame->previous;
|
||||
gen_frame->previous = NULL;
|
||||
((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD;
|
||||
FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg);
|
||||
/* We don't know which of these is relevant here, so keep them equal */
|
||||
assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
|
||||
#if TIER_ONE
|
||||
|
|
@ -3405,18 +3404,10 @@ dummy_func(
|
|||
op(_FOR_ITER_GEN_FRAME, (iter, null -- iter, null, gen_frame)) {
|
||||
PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(iter);
|
||||
DEOPT_IF(Py_TYPE(gen) != &PyGen_Type);
|
||||
#ifdef Py_GIL_DISABLED
|
||||
// Since generators can't be used by multiple threads anyway we
|
||||
// don't need to deopt here, but this lets us work on making
|
||||
// generators thread-safe without necessarily having to
|
||||
// specialize them thread-safely as well.
|
||||
DEOPT_IF(!_PyObject_IsUniquelyReferenced((PyObject *)gen));
|
||||
#endif
|
||||
DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING);
|
||||
DEOPT_IF(!gen_try_set_executing((PyGenObject *)gen));
|
||||
STAT_INC(FOR_ITER, hit);
|
||||
_PyInterpreterFrame *pushed_frame = &gen->gi_iframe;
|
||||
_PyFrame_StackPush(pushed_frame, PyStackRef_None);
|
||||
gen->gi_frame_state = FRAME_EXECUTING;
|
||||
gen->gi_exc_state.previous_item = tstate->exc_info;
|
||||
tstate->exc_info = &gen->gi_exc_state;
|
||||
pushed_frame->previous = frame;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue