Skip to content

Commit 89a680f

Browse files
committed
Optimize PyFrame_ClearExceptCode
1 parent 2ff5eb8 commit 89a680f

1 file changed

Lines changed: 31 additions & 3 deletions

File tree

Python/frame.c

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,18 +111,46 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
111111
// GH-99729: Clearing this frame can expose the stack (via finalizers). It's
112112
// crucial that this frame has been unlinked, and is no longer visible:
113113
assert(_PyThreadState_GET()->current_frame != frame);
114+
115+
// Clear the frame object first
116+
PyFrameObject *f = NULL;
114117
if (frame->frame_obj) {
115-
PyFrameObject *f = frame->frame_obj;
118+
f = frame->frame_obj;
116119
frame->frame_obj = NULL;
117120
if (!_PyObject_IsUniquelyReferenced((PyObject *)f)) {
118121
take_ownership(f, frame);
119122
Py_DECREF(f);
120123
return;
121124
}
122-
Py_DECREF(f);
123125
}
124-
_PyFrame_ClearLocals(frame);
126+
127+
// Inline _PyFrame_ClearLocals for better performance
128+
if (frame->stackpointer != NULL) {
129+
_PyStackRef *sp = frame->stackpointer;
130+
_PyStackRef *locals = frame->localsplus;
131+
132+
// Fast path for empty stack
133+
if (sp <= locals) {
134+
frame->stackpointer = locals;
135+
} else {
136+
// Set stackpointer to locals first to avoid reprocessing in case of exception
137+
frame->stackpointer = locals;
138+
// Process all stack items at once
139+
do {
140+
sp--;
141+
PyStackRef_XCLOSE(*sp);
142+
} while (sp > locals);
143+
}
144+
}
145+
146+
// Clear locals dict and function object
147+
Py_CLEAR(frame->f_locals);
125148
PyStackRef_CLEAR(frame->f_funcobj);
149+
150+
// Now it's safe to decref the frame object
151+
if (f) {
152+
Py_DECREF(f);
153+
}
126154
}
127155

128156
/* Unstable API functions */

0 commit comments

Comments
 (0)