mirror of https://github.com/python/cpython
gh-117376: Make code objects use deferred reference counting (#117823)
We want code objects to use deferred reference counting in the free-threaded build. This requires them to be tracked by the GC, so we set `Py_TPFLAGS_HAVE_GC` in the free-threaded build, but not the default build.
This commit is contained in:
parent
a734fd5cf7
commit
241ed5f2cd
|
@ -1,7 +1,7 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from contextlib import contextmanager, ExitStack
|
from contextlib import contextmanager, ExitStack
|
||||||
from test.support import catch_unraisable_exception, import_helper
|
from test.support import catch_unraisable_exception, import_helper, gc_collect
|
||||||
|
|
||||||
|
|
||||||
# Skip this test if the _testcapi module isn't available.
|
# Skip this test if the _testcapi module isn't available.
|
||||||
|
@ -372,6 +372,7 @@ class TestCodeObjectWatchers(unittest.TestCase):
|
||||||
|
|
||||||
def assert_event_counts(self, exp_created_0, exp_destroyed_0,
|
def assert_event_counts(self, exp_created_0, exp_destroyed_0,
|
||||||
exp_created_1, exp_destroyed_1):
|
exp_created_1, exp_destroyed_1):
|
||||||
|
gc_collect() # code objects are collected by GC in free-threaded build
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
exp_created_0, _testcapi.get_code_watcher_num_created_events(0))
|
exp_created_0, _testcapi.get_code_watcher_num_created_events(0))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -432,6 +433,7 @@ class TestCodeObjectWatchers(unittest.TestCase):
|
||||||
with self.code_watcher(2):
|
with self.code_watcher(2):
|
||||||
with catch_unraisable_exception() as cm:
|
with catch_unraisable_exception() as cm:
|
||||||
del co
|
del co
|
||||||
|
gc_collect()
|
||||||
|
|
||||||
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
|
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
|
||||||
|
|
||||||
|
|
|
@ -226,7 +226,9 @@ class GCTests(unittest.TestCase):
|
||||||
exec("def f(): pass\n", d)
|
exec("def f(): pass\n", d)
|
||||||
gc.collect()
|
gc.collect()
|
||||||
del d
|
del d
|
||||||
self.assertEqual(gc.collect(), 2)
|
# In the free-threaded build, the count returned by `gc.collect()`
|
||||||
|
# is 3 because it includes f's code object.
|
||||||
|
self.assertIn(gc.collect(), (2, 3))
|
||||||
|
|
||||||
def test_function_tp_clear_leaves_consistent_state(self):
|
def test_function_tp_clear_leaves_consistent_state(self):
|
||||||
# https://github.com/python/cpython/issues/91636
|
# https://github.com/python/cpython/issues/91636
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "pycore_code.h" // _PyCodeConstructor
|
#include "pycore_code.h" // _PyCodeConstructor
|
||||||
#include "pycore_frame.h" // FRAME_SPECIALS_SIZE
|
#include "pycore_frame.h" // FRAME_SPECIALS_SIZE
|
||||||
#include "pycore_interp.h" // PyInterpreterState.co_extra_freefuncs
|
#include "pycore_interp.h" // PyInterpreterState.co_extra_freefuncs
|
||||||
|
#include "pycore_object.h" // _PyObject_SetDeferredRefcount
|
||||||
#include "pycore_opcode_metadata.h" // _PyOpcode_Deopt, _PyOpcode_Caches
|
#include "pycore_opcode_metadata.h" // _PyOpcode_Deopt, _PyOpcode_Caches
|
||||||
#include "pycore_opcode_utils.h" // RESUME_AT_FUNC_START
|
#include "pycore_opcode_utils.h" // RESUME_AT_FUNC_START
|
||||||
#include "pycore_pystate.h" // _PyInterpreterState_GET()
|
#include "pycore_pystate.h" // _PyInterpreterState_GET()
|
||||||
|
@ -557,13 +558,22 @@ _PyCode_New(struct _PyCodeConstructor *con)
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_ssize_t size = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT);
|
Py_ssize_t size = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT);
|
||||||
PyCodeObject *co = PyObject_NewVar(PyCodeObject, &PyCode_Type, size);
|
PyCodeObject *co;
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
co = PyObject_GC_NewVar(PyCodeObject, &PyCode_Type, size);
|
||||||
|
#else
|
||||||
|
co = PyObject_NewVar(PyCodeObject, &PyCode_Type, size);
|
||||||
|
#endif
|
||||||
if (co == NULL) {
|
if (co == NULL) {
|
||||||
Py_XDECREF(replacement_locations);
|
Py_XDECREF(replacement_locations);
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
init_code(co, con);
|
init_code(co, con);
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
_PyObject_SetDeferredRefcount((PyObject *)co);
|
||||||
|
_PyObject_GC_TRACK(co);
|
||||||
|
#endif
|
||||||
Py_XDECREF(replacement_locations);
|
Py_XDECREF(replacement_locations);
|
||||||
return co;
|
return co;
|
||||||
}
|
}
|
||||||
|
@ -1710,6 +1720,10 @@ code_dealloc(PyCodeObject *co)
|
||||||
}
|
}
|
||||||
Py_SET_REFCNT(co, 0);
|
Py_SET_REFCNT(co, 0);
|
||||||
|
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
PyObject_GC_UnTrack(co);
|
||||||
|
#endif
|
||||||
|
|
||||||
_PyFunction_ClearCodeByVersion(co->co_version);
|
_PyFunction_ClearCodeByVersion(co->co_version);
|
||||||
if (co->co_extra != NULL) {
|
if (co->co_extra != NULL) {
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
|
@ -1752,6 +1766,15 @@ code_dealloc(PyCodeObject *co)
|
||||||
PyObject_Free(co);
|
PyObject_Free(co);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
static int
|
||||||
|
code_traverse(PyCodeObject *co, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
Py_VISIT(co->co_consts);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
code_repr(PyCodeObject *co)
|
code_repr(PyCodeObject *co)
|
||||||
{
|
{
|
||||||
|
@ -2196,9 +2219,17 @@ PyTypeObject PyCode_Type = {
|
||||||
PyObject_GenericGetAttr, /* tp_getattro */
|
PyObject_GenericGetAttr, /* tp_getattro */
|
||||||
0, /* tp_setattro */
|
0, /* tp_setattro */
|
||||||
0, /* tp_as_buffer */
|
0, /* tp_as_buffer */
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
||||||
|
#else
|
||||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||||
|
#endif
|
||||||
code_new__doc__, /* tp_doc */
|
code_new__doc__, /* tp_doc */
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
(traverseproc)code_traverse, /* tp_traverse */
|
||||||
|
#else
|
||||||
0, /* tp_traverse */
|
0, /* tp_traverse */
|
||||||
|
#endif
|
||||||
0, /* tp_clear */
|
0, /* tp_clear */
|
||||||
code_richcompare, /* tp_richcompare */
|
code_richcompare, /* tp_richcompare */
|
||||||
offsetof(PyCodeObject, co_weakreflist), /* tp_weaklistoffset */
|
offsetof(PyCodeObject, co_weakreflist), /* tp_weaklistoffset */
|
||||||
|
|
Loading…
Reference in New Issue