Skip to content

Commit 3feccd1

Browse files
eendebakptclaude
andauthored
gh-161242: Add a freelist for traceback objects (#161243)
* Add a freelist for traceback objects A traceback object is created for every raised exception. Keep up to 8 of them on a per-thread freelist, and replace the argument check in tb_create_raw() with asserts since all callers validate their arguments. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Apply suggestion from @eendebakpt * Apply suggestion from @eendebakpt --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
1 parent 4b50f88 commit 3feccd1

3 files changed

Lines changed: 12 additions & 7 deletions

File tree

‎Include/internal/pycore_freelist_state.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ extern "C" {
3131
# define Py_pycfunctionobject_MAXFREELIST 16
3232
# define Py_pycmethodobject_MAXFREELIST 16
3333
# define Py_pymethodobjects_MAXFREELIST 20
34+
# define Py_tracebacks_MAXFREELIST 8
3435

3536
// A generic freelist of either PyObjects or other data structures.
3637
struct _Py_freelist {
@@ -66,6 +67,7 @@ struct _Py_freelists {
6667
struct _Py_freelist pycfunctionobject;
6768
struct _Py_freelist pycmethodobject;
6869
struct _Py_freelist pymethodobjects;
70+
struct _Py_freelist tracebacks;
6971
};
7072

7173
#ifdef __cplusplus

‎Objects/object.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,7 @@ _PyObject_ClearFreeLists(struct _Py_freelists *freelists, int is_finalization)
956956
clear_freelist(&freelists->pycfunctionobject, is_finalization, PyObject_GC_Del);
957957
clear_freelist(&freelists->pycmethodobject, is_finalization, PyObject_GC_Del);
958958
clear_freelist(&freelists->pymethodobjects, is_finalization, free_object);
959+
clear_freelist(&freelists->tracebacks, is_finalization, PyObject_GC_Del);
959960
}
960961

961962
/*

‎Python/traceback.c‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "Python.h"
55
#include "pycore_call.h" // _PyObject_CallMethod()
66
#include "pycore_fileutils.h" // _Py_BEGIN_SUPPRESS_IPH
7+
#include "pycore_freelist.h" // _Py_FREELIST_POP()
78
#include "pycore_frame.h" // PyFrameObject
89
#include "pycore_interp.h" // PyInterpreterState.gc
910
#include "pycore_interpframe.h" // _PyFrame_GetCode()
@@ -79,15 +80,16 @@ tb_create_raw(PyTracebackObject *next, PyFrameObject *frame, int lasti,
7980
int lineno)
8081
{
8182
PyTracebackObject *tb;
82-
if ((next != NULL && !PyTraceBack_Check(next)) ||
83-
frame == NULL || !PyFrame_Check(frame)) {
84-
PyErr_BadInternalCall();
85-
return NULL;
83+
assert(next == NULL || PyTraceBack_Check(next));
84+
assert(frame != NULL);
85+
assert(PyFrame_Check(frame));
86+
tb = _Py_FREELIST_POP(PyTracebackObject, tracebacks);
87+
if (tb == NULL) {
88+
tb = PyObject_GC_New(PyTracebackObject, &PyTraceBack_Type);
8689
}
87-
tb = PyObject_GC_New(PyTracebackObject, &PyTraceBack_Type);
8890
if (tb != NULL) {
8991
tb->tb_next = (PyTracebackObject*)Py_XNewRef(next);
90-
tb->tb_frame = (PyFrameObject*)Py_XNewRef(frame);
92+
tb->tb_frame = (PyFrameObject*)Py_NewRef(frame);
9193
tb->tb_lasti = lasti;
9294
tb->tb_lineno = lineno;
9395
PyObject_GC_Track(tb);
@@ -237,7 +239,7 @@ tb_dealloc(PyObject *op)
237239
PyObject_GC_UnTrack(tb);
238240
Py_XDECREF(tb->tb_next);
239241
Py_XDECREF(tb->tb_frame);
240-
PyObject_GC_Del(tb);
242+
_Py_FREELIST_FREE(tracebacks, tb, PyObject_GC_Del);
241243
}
242244

243245
static int

0 commit comments

Comments
 (0)