Skip to content

Commit 6667156

Browse files
authored
Merge pull request #218 from sumerc/fix-217
fix: decref PyFrame_GetGenerator
2 parents 210bbe0 + ead996a commit 6667156

2 files changed

Lines changed: 41 additions & 3 deletions

File tree

tests/test_asyncio.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,41 @@ async def a(n):
6969
stats = yappi.get_func_stats()
7070
self.assert_traces_almost_equal(r1, stats)
7171

72+
def test_mem_leak(self):
73+
import gc
74+
import types
75+
76+
async def noop():
77+
return
78+
79+
async def run_batch(n):
80+
await asyncio.gather(*(noop() for _ in range(n)))
81+
82+
def exhausted_coroutines():
83+
gc.collect()
84+
return sum(
85+
1
86+
for o in gc.get_objects()
87+
if isinstance(o, types.CoroutineType) and o.cr_frame is None
88+
)
89+
90+
batch_size = 1000
91+
92+
yappi.set_clock_type("cpu")
93+
yappi.start()
94+
asyncio.get_event_loop().run_until_complete(run_batch(batch_size))
95+
yappi.stop()
96+
yappi.clear_stats()
97+
baseline = exhausted_coroutines()
98+
99+
yappi.start()
100+
asyncio.get_event_loop().run_until_complete(run_batch(batch_size))
101+
yappi.stop()
102+
yappi.clear_stats()
103+
after = exhausted_coroutines()
104+
105+
self.assertLess(after - baseline, batch_size // 3)
106+
72107
def test_basic_old_style(self):
73108

74109
async def a():

yappi/_yappi.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,17 +225,20 @@ int
225225
IS_SUSPENDED(PyFrameObject *frame) {
226226
#if PY_VERSION_HEX >= 0x030B0000 // Python 3.11+
227227
PyGenObject *gen = (PyGenObject *)PyFrame_GetGenerator(frame);
228+
int suspended;
228229
if (gen == NULL) {
229230
return 0;
230231
}
231232
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION == 14
232233
unsigned char curr_op_code = (*frame->f_frame->instr_ptr).op.code;
233-
return curr_op_code == YIELD_VALUE || curr_op_code == INSTRUMENTED_YIELD_VALUE;
234+
suspended = curr_op_code == YIELD_VALUE || curr_op_code == INSTRUMENTED_YIELD_VALUE;
234235
#elif PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION == 13
235-
return FRAME_STATE_SUSPENDED(gen->gi_frame_state);
236+
suspended = FRAME_STATE_SUSPENDED(gen->gi_frame_state);
236237
#else
237-
return gen->gi_frame_state == FRAME_SUSPENDED;
238+
suspended = gen->gi_frame_state == FRAME_SUSPENDED;
238239
#endif
240+
Py_DECREF(gen);
241+
return suspended;
239242
#elif PY_VERSION_HEX >= 0x030A0000 // Python 3.10+
240243
return (frame->f_state == FRAME_SUSPENDED);
241244
#else

0 commit comments

Comments
 (0)