Skip to content

Commit bfddfee

Browse files
Dian Shengmeta-codesync[bot]
authored andcommitted
Revert D91839270: Generator ref leak fixes
Differential Revision: D91839270 Original commit changeset: 2e532727423d Original Phabricator Diff: D91839270 fbshipit-source-id: 51e829ac064c77598dd0fd920e3108417ef99966
1 parent a1973a9 commit bfddfee

3 files changed

Lines changed: 20 additions & 80 deletions

File tree

cinderx/Jit/frame.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -432,10 +432,7 @@ void jitFrameRemoveReifier(_PyInterpreterFrame* frame) {
432432
// interpreter.
433433
if (!hasRtfsFunction(frame)) {
434434
// ownership is transferred
435-
if (jitFrameGetFunction(frame) != nullptr) {
436-
setFrameFunction(frame, jitFrameGetFunction(frame));
437-
jitFrameGetHeader(frame)->rtfs = JIT_FRAME_INITIALIZED;
438-
}
435+
setFrameFunction(frame, jitFrameGetFunction(frame));
439436
} else {
440437
RuntimeFrameState* rtfs = jitFrameGetRtfs(frame);
441438
auto func = rtfs->func();
@@ -639,8 +636,7 @@ void jitFrameClearExceptCode(_PyInterpreterFrame* frame) {
639636
Ci_STACK_CLOSE(frame->f_funcobj);
640637
if constexpr (PY_VERSION_HEX < 0x030E0000) {
641638
if (!hasRtfsFunction(frame)) {
642-
Py_XDECREF(jitFrameGetFunction(frame));
643-
jitFrameGetHeader(frame)->rtfs = JIT_FRAME_INITIALIZED;
639+
Py_DECREF(jitFrameGetFunction(frame));
644640
}
645641
}
646642
}

cinderx/Jit/generators_rt.cpp

Lines changed: 18 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -50,42 +50,25 @@ int jitgen_traverse(PyObject* obj, visitproc visit, void* arg) {
5050
JitGenObject* jit_gen = JitGenObject::cast(obj);
5151
if (jit_gen != nullptr) {
5252
const GenDataFooter* gen_footer = jit_gen->genDataFooter();
53-
// Only visit JIT-specific live values if we have a valid yield point.
54-
// If yieldPoint is null, the generator hasn't yielded yet or has completed,
55-
// but we still need to call PyGen_Type.tp_traverse below to visit standard
56-
// generator references (gi_code, gi_frame, etc.).
57-
if (gen_footer->yieldPoint != nullptr) {
58-
size_t deopt_idx = gen_footer->yieldPoint->deoptIdx();
59-
const DeoptMetadata& meta =
60-
gen_footer->code_rt->getDeoptMetadata(deopt_idx);
61-
for (const LiveValue& value : meta.live_values) {
62-
if (value.ref_kind != hir::RefKind::kOwned) {
63-
continue;
64-
}
65-
codegen::PhyLocation loc = value.location;
66-
JIT_CHECK(
67-
!loc.is_register(),
68-
"DeoptMetadata for Yields should not reference registers");
69-
PyObject* v = *reinterpret_cast<PyObject**>(
70-
reinterpret_cast<uintptr_t>(gen_footer) + loc.loc);
71-
Py_VISIT(v);
72-
}
73-
JIT_CHECK(JitGen_CheckAny(obj), "Deopted during GC traversal");
53+
if (gen_footer->yieldPoint == nullptr) {
54+
return 0;
7455
}
75-
76-
#if PY_VERSION_HEX < 0x030E0000
77-
// In lightweight frame mode, frame->f_funcobj is set to a reifier singleton
78-
// rather than the actual function. The real function is stored in the
79-
// FrameHeader and contains func_closure with closure cells that may
80-
// participate in reference cycles. We must visit it explicitly since
81-
// _PyFrame_Traverse won't see it.
82-
if (getConfig().frame_mode == FrameMode::kLightweight &&
83-
jit_gen->gi_frame_state < FRAME_CLEARED) {
84-
_PyInterpreterFrame* frame = generatorFrame(jit_gen);
85-
BorrowedRef<PyFunctionObject> func = jitFrameGetFunction(frame);
86-
Py_VISIT(func.get());
56+
size_t deopt_idx = gen_footer->yieldPoint->deoptIdx();
57+
const DeoptMetadata& meta =
58+
gen_footer->code_rt->getDeoptMetadata(deopt_idx);
59+
for (const LiveValue& value : meta.live_values) {
60+
if (value.ref_kind != hir::RefKind::kOwned) {
61+
continue;
62+
}
63+
codegen::PhyLocation loc = value.location;
64+
JIT_CHECK(
65+
!loc.is_register(),
66+
"DeoptMetadata for Yields should not reference registers");
67+
PyObject* v = *reinterpret_cast<PyObject**>(
68+
reinterpret_cast<uintptr_t>(gen_footer) + loc.loc);
69+
Py_VISIT(v);
8770
}
88-
#endif
71+
JIT_CHECK(JitGen_CheckAny(obj), "Deopted during GC traversal");
8972
}
9073
// Try to use CPython traverse as much as we can as it has internals which
9174
// are hard to borrow in 3.14 (compares 'visit' to a speicifc internal
@@ -354,26 +337,6 @@ void jitgen_finalize(PyObject* obj) {
354337
PyGen_Type.tp_finalize(obj);
355338
}
356339

357-
int jitgen_clear(PyObject* obj) {
358-
// tp_clear is called by the cyclic GC to break reference cycles.
359-
// We need to deopt the JIT generator so that the standard generator
360-
// tp_clear can properly clear all references.
361-
//
362-
// If the generator is currently executing (gi_frame_state ==
363-
// FRAME_EXECUTING), we cannot deopt it. In that case, return 0 without
364-
// clearing - the GC will try again later or the generator will complete and
365-
// be collected normally.
366-
if (!deopt_jit_gen(obj)) {
367-
return 0;
368-
}
369-
// After deopting, the object is now a regular PyGenObject/PyCoroObject.
370-
// Delegate to the base type's tp_clear to break cycles.
371-
if (PyGen_Type.tp_clear != nullptr) {
372-
return PyGen_Type.tp_clear(obj);
373-
}
374-
return 0;
375-
}
376-
377340
typedef struct {
378341
PyObject_HEAD
379342
PyObject* cw_coroutine;
@@ -637,7 +600,6 @@ static PyAsyncMethods jitcoro_as_async = {
637600
PyType_Slot gen_slots[] = {
638601
{Py_tp_dealloc, reinterpret_cast<void*>(jitgen_dealloc)},
639602
{Py_tp_traverse, reinterpret_cast<void*>(jitgen_traverse)},
640-
{Py_tp_clear, reinterpret_cast<void*>(jitgen_clear)},
641603
{Py_tp_finalize, reinterpret_cast<void*>(jitgen_finalize)},
642604
{Py_tp_iter, reinterpret_cast<void*>(PyObject_SelfIter)},
643605
{Py_tp_iternext, reinterpret_cast<void*>(jitgen_iternext)},
@@ -673,7 +635,6 @@ static_assert(sizeof(PyGenObject) == sizeof(PyCoroObject));
673635
PyType_Slot coro_slots[] = {
674636
{Py_tp_dealloc, reinterpret_cast<void*>(jitgen_dealloc)},
675637
{Py_tp_traverse, reinterpret_cast<void*>(jitgen_traverse)},
676-
{Py_tp_clear, reinterpret_cast<void*>(jitgen_clear)},
677638
{Py_tp_finalize, reinterpret_cast<void*>(jitgen_finalize)},
678639
{Py_tp_methods, jitcoro_methods},
679640
{Py_tp_members, jitcoro_memberlist},
@@ -705,16 +666,7 @@ void deopt_jit_gen_object_only(JitGenObject* gen) {
705666
Py_SET_TYPE(reinterpret_cast<PyObject*>(gen), type);
706667
if (getConfig().frame_mode == FrameMode::kLightweight) {
707668
auto frame = generatorFrame(gen);
708-
if (gen->gi_frame_state != FRAME_CLEARED) {
709-
jitFrameRemoveReifier(frame);
710-
} else if constexpr (PY_VERSION_HEX < 0x030E0000) {
711-
// Normally we'll clear the function via jitFrameClearExceptCode. But
712-
// a user can call clear on a reified frame object which transfers
713-
// ownership of the _PyInterpreterFrame to the PyFrameObject and marks
714-
// the generator frame as cleared. In that case we still need to decref
715-
// the function which is stored before the _PyInterpreterFrame in 3.12.
716-
Py_XDECREF(jitFrameGetFunction(frame));
717-
}
669+
jitFrameRemoveReifier(frame);
718670
}
719671
}
720672

cinderx/Jit/jit_rt.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -677,18 +677,10 @@ void JITRT_InitFrameCellVars(
677677
PyCodeObject* co = (PyCodeObject*)func->func_code;
678678
int offset = co->co_nlocalsplus - nvars;
679679
_PyInterpreterFrame* frame = interpFrameFromThreadState(tstate);
680-
for (int i = 0; i < offset; i++) {
681-
frame->localsplus[i] = Ci_STACK_NULL;
682-
}
683680
for (int i = 0; i < nvars; i++) {
684681
frame->localsplus[offset + i] =
685682
Ci_STACK_NEWREF(PyTuple_GET_ITEM(closure, i));
686683
}
687-
#if PY_VERSION_HEX < 0x030E0000
688-
frame->stacktop = nvars + offset;
689-
#else
690-
frame->stackpointer = &frame->localsplus[nvars + offset];
691-
#endif
692684
}
693685

694686
std::pair<PyThreadState*, jit::GenDataFooter*>

0 commit comments

Comments
 (0)