Skip to content

Commit 42d3e11

Browse files
committed
code: make code object use deferred reference counting
Code objects are frequently accessed by many threads, so enable deferred reference counting so that in the future we can skip ref count operations on code objects during frame evaluation. This requires making PyCode_Type support GC.
1 parent 357a207 commit 42d3e11

File tree

3 files changed

+19
-7
lines changed

3 files changed

+19
-7
lines changed

Lib/test/test_capi/test_watchers.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import unittest
22

33
from contextlib import contextmanager, ExitStack
4-
from test.support import catch_unraisable_exception, import_helper
4+
from test.support import catch_unraisable_exception, import_helper, gc_collect
55

66

77
# Skip this test if the _testcapi module isn't available.
@@ -347,6 +347,7 @@ def code_watcher(self, which_watcher):
347347

348348
def assert_event_counts(self, exp_created_0, exp_destroyed_0,
349349
exp_created_1, exp_destroyed_1):
350+
gc_collect()
350351
self.assertEqual(
351352
exp_created_0, _testcapi.get_code_watcher_num_created_events(0))
352353
self.assertEqual(

Lib/test/test_gc.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -220,12 +220,12 @@ class B(object):
220220

221221
def test_function(self):
222222
# Tricky: f -> d -> f, code should call d.clear() after the exec to
223-
# break the cycle.
223+
# break the cycle. May collect f.__code__ as well.
224224
d = {}
225225
exec("def f(): pass\n", d)
226226
gc.collect()
227227
del d
228-
self.assertEqual(gc.collect(), 2)
228+
self.assertTrue(2 <= gc.collect() <= 3)
229229

230230
def test_function_tp_clear_leaves_consistent_state(self):
231231
# https://github.com/python/cpython/issues/91636

Objects/codeobject.c

+15-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "pycore_code.h" // _PyCodeConstructor
77
#include "pycore_frame.h" // FRAME_SPECIALS_SIZE
88
#include "pycore_interp.h" // PyInterpreterState.co_extra_freefuncs
9+
#include "pycore_object.h" // _PyObject_SET_DEFERRED_REFCOUNT
910
#include "pycore_opcode.h" // _PyOpcode_Deopt
1011
#include "pycore_pystate.h" // _PyInterpreterState_GET()
1112
#include "pycore_tuple.h" // _PyTuple_ITEMS()
@@ -550,13 +551,15 @@ _PyCode_New(struct _PyCodeConstructor *con)
550551
}
551552

552553
Py_ssize_t size = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT);
553-
PyCodeObject *co = PyObject_NewVar(PyCodeObject, &PyCode_Type, size);
554+
PyCodeObject *co = PyObject_GC_NewVar(PyCodeObject, &PyCode_Type, size);
554555
if (co == NULL) {
555556
Py_XDECREF(replacement_locations);
556557
PyErr_NoMemory();
557558
return NULL;
558559
}
559560
init_code(co, con);
561+
_PyObject_SET_DEFERRED_REFCOUNT(co);
562+
_PyObject_GC_TRACK(co);
560563
Py_XDECREF(replacement_locations);
561564
return co;
562565
}
@@ -1668,6 +1671,7 @@ code_dealloc(PyCodeObject *co)
16681671
{
16691672
notify_code_watchers(PY_CODE_EVENT_DESTROY, co);
16701673

1674+
_PyObject_GC_UNTRACK(co);
16711675
if (co->co_extra != NULL) {
16721676
PyInterpreterState *interp = _PyInterpreterState_GET();
16731677
_PyCodeObjectExtra *co_extra = co->co_extra;
@@ -1705,7 +1709,14 @@ code_dealloc(PyCodeObject *co)
17051709
if (co->_co_linearray) {
17061710
PyMem_Free(co->_co_linearray);
17071711
}
1708-
PyObject_Free(co);
1712+
PyObject_GC_Del(co);
1713+
}
1714+
1715+
static int
1716+
code_traverse(PyCodeObject *co, visitproc visit, void *arg)
1717+
{
1718+
Py_VISIT(co->co_consts);
1719+
return 0;
17091720
}
17101721

17111722
static PyObject *
@@ -2114,9 +2125,9 @@ PyTypeObject PyCode_Type = {
21142125
PyObject_GenericGetAttr, /* tp_getattro */
21152126
0, /* tp_setattro */
21162127
0, /* tp_as_buffer */
2117-
Py_TPFLAGS_DEFAULT, /* tp_flags */
2128+
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC, /* tp_flags */
21182129
code_new__doc__, /* tp_doc */
2119-
0, /* tp_traverse */
2130+
(traverseproc)code_traverse, /* tp_traverse */
21202131
0, /* tp_clear */
21212132
code_richcompare, /* tp_richcompare */
21222133
offsetof(PyCodeObject, co_weakreflist), /* tp_weaklistoffset */

0 commit comments

Comments
 (0)