mirror of
https://github.com/python/cpython.git
synced 2026-05-31 06:40:53 +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
22
Python/generated_cases.c.h
generated
22
Python/generated_cases.c.h
generated
|
|
@ -5548,14 +5548,7 @@
|
|||
assert(_PyOpcode_Deopt[opcode] == (FOR_ITER));
|
||||
JUMP_TO_PREDICTED(FOR_ITER);
|
||||
}
|
||||
#ifdef Py_GIL_DISABLED
|
||||
if (!_PyObject_IsUniquelyReferenced((PyObject *)gen)) {
|
||||
UPDATE_MISS_STATS(FOR_ITER);
|
||||
assert(_PyOpcode_Deopt[opcode] == (FOR_ITER));
|
||||
JUMP_TO_PREDICTED(FOR_ITER);
|
||||
}
|
||||
#endif
|
||||
if (gen->gi_frame_state >= FRAME_EXECUTING) {
|
||||
if (!gen_try_set_executing((PyGenObject *)gen)) {
|
||||
UPDATE_MISS_STATS(FOR_ITER);
|
||||
assert(_PyOpcode_Deopt[opcode] == (FOR_ITER));
|
||||
JUMP_TO_PREDICTED(FOR_ITER);
|
||||
|
|
@ -5563,7 +5556,6 @@
|
|||
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;
|
||||
|
|
@ -7327,7 +7319,6 @@
|
|||
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;
|
||||
stack_pointer += -1;
|
||||
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
|
||||
|
|
@ -7338,6 +7329,8 @@
|
|||
_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);
|
||||
assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
|
||||
#if TIER_ONE
|
||||
assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE ||
|
||||
|
|
@ -10301,14 +10294,13 @@
|
|||
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));
|
||||
stack_pointer += -1;
|
||||
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
|
||||
gen->gi_frame_state = FRAME_EXECUTING;
|
||||
gen->gi_exc_state.previous_item = tstate->exc_info;
|
||||
tstate->exc_info = &gen->gi_exc_state;
|
||||
assert( 2u + oparg <= UINT16_MAX);
|
||||
|
|
@ -10401,7 +10393,7 @@
|
|||
assert(_PyOpcode_Deopt[opcode] == (SEND));
|
||||
JUMP_TO_PREDICTED(SEND);
|
||||
}
|
||||
if (gen->gi_frame_state >= FRAME_EXECUTING) {
|
||||
if (!gen_try_set_executing((PyGenObject *)gen)) {
|
||||
UPDATE_MISS_STATS(SEND);
|
||||
assert(_PyOpcode_Deopt[opcode] == (SEND));
|
||||
JUMP_TO_PREDICTED(SEND);
|
||||
|
|
@ -10409,7 +10401,6 @@
|
|||
STAT_INC(SEND, hit);
|
||||
_PyInterpreterFrame *pushed_frame = &gen->gi_iframe;
|
||||
_PyFrame_StackPush(pushed_frame, PyStackRef_MakeHeapSafe(v));
|
||||
gen->gi_frame_state = FRAME_EXECUTING;
|
||||
gen->gi_exc_state.previous_item = tstate->exc_info;
|
||||
tstate->exc_info = &gen->gi_exc_state;
|
||||
assert( 2u + oparg <= UINT16_MAX);
|
||||
|
|
@ -12045,7 +12036,6 @@
|
|||
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;
|
||||
stack_pointer += -1;
|
||||
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
|
||||
|
|
@ -12056,6 +12046,8 @@
|
|||
_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);
|
||||
assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
|
||||
#if TIER_ONE
|
||||
assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE ||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue