Skip to content

Commit 15bbe37

Browse files
committed
Clear frame locals and stack on generator close
Add Frame::clear_locals_and_stack() to release references held by closed generators/coroutines, matching _PyFrame_ClearLocals behavior. Call it from Coro::close() after marking the coroutine as closed. Update test_generators.py expectedFailure markers accordingly.
1 parent 9f250a0 commit 15bbe37

File tree

3 files changed

+14
-1
lines changed

3 files changed

+14
-1
lines changed

Lib/test/test_generators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,6 @@ def f():
660660
with self.assertRaises(RuntimeError):
661661
gen.close()
662662

663-
@unittest.expectedFailure # TODO: RUSTPYTHON; no deterministic GC finalization
664663
def test_close_releases_frame_locals(self):
665664
# See gh-118272
666665

@@ -684,6 +683,7 @@ def genfn():
684683

685684
# See https://github.com/python/cpython/issues/125723
686685
class GeneratorDeallocTest(unittest.TestCase):
686+
@unittest.expectedFailure # TODO: RUSTPYTHON; frame uses shared Arc, no ownership transfer
687687
def test_frame_outlives_generator(self):
688688
def g1():
689689
a = 42

crates/vm/src/coroutine.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ impl Coro {
207207
)
208208
});
209209
self.closed.store(true);
210+
// Release frame locals and stack to free references held by the
211+
// closed generator, matching gen_send_ex2 with close_on_completion.
212+
self.frame.clear_locals_and_stack();
210213
match result {
211214
Ok(ExecutionResult::Yield(_)) => {
212215
Err(vm.new_runtime_error(format!("{} ignored GeneratorExit", gen_name(jen, vm))))

crates/vm/src/frame.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,16 @@ impl Frame {
223223
self.state.lock().stack.clear();
224224
}
225225

226+
/// Clear locals and stack after generator/coroutine close.
227+
/// Releases references held by the frame, matching _PyFrame_ClearLocals.
228+
pub(crate) fn clear_locals_and_stack(&self) {
229+
self.state.lock().stack.clear();
230+
let mut fastlocals = self.fastlocals.lock();
231+
for slot in fastlocals.iter_mut() {
232+
*slot = None;
233+
}
234+
}
235+
226236
/// Store a borrowed back-reference to the owning generator/coroutine.
227237
/// The caller must ensure the generator outlives the frame.
228238
pub fn set_generator(&self, generator: &PyObject) {

0 commit comments

Comments
 (0)