Skip to content

Commit de13810

Browse files
committed
Avoid extra copies in take_ownership
1 parent 6fde7b0 commit de13810

File tree

6 files changed

+26
-20
lines changed

6 files changed

+26
-20
lines changed

Include/internal/pycore_frame.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,20 +153,27 @@ _PyFrame_NumSlotsForCodeObject(PyCodeObject *code)
153153
}
154154

155155
static inline void
156-
_PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest)
156+
_PyFrame_CopyToHeap(_PyInterpreterFrame *src, _PyInterpreterFrame *dest)
157157
{
158158
*dest = *src;
159159
assert(src->stackpointer != NULL);
160160
int stacktop = (int)(src->stackpointer - src->localsplus);
161161
assert(stacktop >= _PyFrame_GetCode(src)->co_nlocalsplus);
162162
dest->stackpointer = dest->localsplus + stacktop;
163-
// The generator may outlive any references that were providing "support"
164-
// for borrowed references in the frame. Convert them to strong references.
163+
// The destination frame may outlive any references that were providing
164+
// "support" for borrowed references in the source frame. Convert any
165+
// borrowed references that were copied into dest into strong references.
165166
for (int i = 0; i < stacktop; i++) {
166167
dest->localsplus[i] = _PyStackRef_NewIfBorrowedOrSteal(src->localsplus[i]);
167168
}
168169
dest->f_executable = _PyStackRef_NewIfBorrowedOrSteal(dest->f_executable);
169170
dest->f_funcobj = _PyStackRef_NewIfBorrowedOrSteal(dest->f_funcobj);
171+
}
172+
173+
static inline void
174+
_PyFrame_CopyToNewGen(_PyInterpreterFrame *src, _PyInterpreterFrame *dest)
175+
{
176+
_PyFrame_CopyToHeap(src, dest);
170177
// Don't leave a dangling pointer to the old frame when creating generators
171178
// and coroutines:
172179
dest->previous = NULL;

Objects/genobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,7 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,
989989
assert(f->f_frame->frame_obj == NULL);
990990
assert(f->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT);
991991
_PyInterpreterFrame *frame = &gen->gi_iframe;
992-
_PyFrame_Copy((_PyInterpreterFrame *)f->_f_frame_data, frame);
992+
_PyFrame_CopyToNewGen((_PyInterpreterFrame *)f->_f_frame_data, frame);
993993
gen->gi_frame_state = FRAME_CREATED;
994994
assert(frame->frame_obj == f);
995995
f->f_frame = frame;

Python/bytecodes.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4694,7 +4694,7 @@ dummy_func(
46944694
SAVE_STACK();
46954695
_PyInterpreterFrame *gen_frame = &gen->gi_iframe;
46964696
frame->instr_ptr++;
4697-
_PyFrame_Copy(frame, gen_frame);
4697+
_PyFrame_CopyToNewGen(frame, gen_frame);
46984698
assert(frame->frame_obj == NULL);
46994699
gen->gi_frame_state = FRAME_CREATED;
47004700
gen_frame->owner = FRAME_OWNED_BY_GENERATOR;

Python/executor_cases.c.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/frame.c

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,19 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame)
5050
{
5151
assert(frame->owner < FRAME_OWNED_BY_INTERPRETER);
5252
assert(frame->owner != FRAME_OWNED_BY_FRAME_OBJECT);
53-
Py_ssize_t size = ((char*)frame->stackpointer) - (char *)frame;
54-
memcpy((_PyInterpreterFrame *)f->_f_frame_data, frame, size);
55-
frame = (_PyInterpreterFrame *)f->_f_frame_data;
56-
frame->stackpointer = (_PyStackRef *)(((char *)frame) + size);
53+
// While ownership of f_executable is transferred to the frame object, the
54+
// reference in the frame will also be closed when the frame is popped from
55+
// the stack. Create another reference to ensure that works correctly.
56+
//
57+
// This must happen in the source frame (not in the frame
58+
// object). _PyFrame_CopyToHeap converts borrowed references in the source
59+
// frame into strong references in the destination frame. duping the
60+
// reference in the dest frame would result in a leak if the source was
61+
// borrowed because the close on the source would not destroy the newly
62+
// created reference.
5763
frame->f_executable = PyStackRef_DUP(frame->f_executable);
58-
int stacktop = (int)(frame->stackpointer - frame->localsplus);
59-
assert(stacktop >= _PyFrame_GetCode(frame)->co_nlocalsplus);
60-
// The frame object may outlive any references that were providing "support"
61-
// for borrowed references in the frame. Convert them to strong references.
62-
frame->f_executable = _PyStackRef_NewIfBorrowedOrSteal(frame->f_executable);
63-
frame->f_funcobj = _PyStackRef_NewIfBorrowedOrSteal(frame->f_funcobj);
64-
for (int i = 0; i < stacktop; i++) {
65-
frame->localsplus[i] = _PyStackRef_NewIfBorrowedOrSteal(frame->localsplus[i]);
66-
}
64+
_PyFrame_CopyToHeap(frame, (_PyInterpreterFrame *) f->_f_frame_data);
65+
frame = (_PyInterpreterFrame *)f->_f_frame_data;
6766
f->f_frame = frame;
6867
frame->owner = FRAME_OWNED_BY_FRAME_OBJECT;
6968
if (_PyFrame_IsIncomplete(frame)) {

Python/generated_cases.c.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)