mirror of https://github.com/python/cpython
gh-125286: Share the Main Refchain With Legacy Interpreters (gh-125709)
They used to be shared, before 3.12. Returning to sharing them resolves a failure on Py_TRACE_REFS builds. Co-authored-by: Petr Viktorin <encukou@gmail.com>
This commit is contained in:
parent
de0d5c6e2e
commit
6f26d496d3
|
@ -920,6 +920,35 @@ always available.
|
||||||
It is not guaranteed to exist in all implementations of Python.
|
It is not guaranteed to exist in all implementations of Python.
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: getobjects(limit[, type])
|
||||||
|
|
||||||
|
This function only exists if CPython was built using the
|
||||||
|
specialized configure option :option:`--with-trace-refs`.
|
||||||
|
It is intended only for debugging garbage-collection issues.
|
||||||
|
|
||||||
|
Return a list of up to *limit* dynamically allocated Python objects.
|
||||||
|
If *type* is given, only objects of that exact type (not subtypes)
|
||||||
|
are included.
|
||||||
|
|
||||||
|
Objects from the list are not safe to use.
|
||||||
|
Specifically, the result will include objects from all interpreters that
|
||||||
|
share their object allocator state (that is, ones created with
|
||||||
|
:c:member:`PyInterpreterConfig.use_main_obmalloc` set to 1
|
||||||
|
or using :c:func:`Py_NewInterpreter`, and the
|
||||||
|
:ref:`main interpreter <sub-interpreter-support>`).
|
||||||
|
Mixing objects from different interpreters may lead to crashes
|
||||||
|
or other unexpected behavior.
|
||||||
|
|
||||||
|
.. impl-detail::
|
||||||
|
|
||||||
|
This function should be used for specialized purposes only.
|
||||||
|
It is not guaranteed to exist in all implementations of Python.
|
||||||
|
|
||||||
|
.. versionchanged:: next
|
||||||
|
|
||||||
|
The result may include objects from other interpreters.
|
||||||
|
|
||||||
|
|
||||||
.. function:: getprofile()
|
.. function:: getprofile()
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
|
|
|
@ -702,7 +702,7 @@ Debug options
|
||||||
Effects:
|
Effects:
|
||||||
|
|
||||||
* Define the ``Py_TRACE_REFS`` macro.
|
* Define the ``Py_TRACE_REFS`` macro.
|
||||||
* Add :func:`!sys.getobjects` function.
|
* Add :func:`sys.getobjects` function.
|
||||||
* Add :envvar:`PYTHONDUMPREFS` environment variable.
|
* Add :envvar:`PYTHONDUMPREFS` environment variable.
|
||||||
|
|
||||||
The :envvar:`PYTHONDUMPREFS` environment variable can be used to dump
|
The :envvar:`PYTHONDUMPREFS` environment variable can be used to dump
|
||||||
|
|
|
@ -416,6 +416,15 @@ symtable
|
||||||
|
|
||||||
(Contributed by Bénédikt Tran in :gh:`120029`.)
|
(Contributed by Bénédikt Tran in :gh:`120029`.)
|
||||||
|
|
||||||
|
|
||||||
|
sys
|
||||||
|
---
|
||||||
|
|
||||||
|
* The previously undocumented special function :func:`sys.getobjects`,
|
||||||
|
which only exists in specialized builds of Python, may now return objects
|
||||||
|
from other interpreters than the one it's called in.
|
||||||
|
|
||||||
|
|
||||||
unicodedata
|
unicodedata
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
|
|
@ -171,6 +171,48 @@ _PyDebug_PrintTotalRefs(void) {
|
||||||
#define REFCHAIN(interp) interp->object_state.refchain
|
#define REFCHAIN(interp) interp->object_state.refchain
|
||||||
#define REFCHAIN_VALUE ((void*)(uintptr_t)1)
|
#define REFCHAIN_VALUE ((void*)(uintptr_t)1)
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
has_own_refchain(PyInterpreterState *interp)
|
||||||
|
{
|
||||||
|
if (interp->feature_flags & Py_RTFLAGS_USE_MAIN_OBMALLOC) {
|
||||||
|
return (_Py_IsMainInterpreter(interp)
|
||||||
|
|| _PyInterpreterState_Main() == NULL);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
refchain_init(PyInterpreterState *interp)
|
||||||
|
{
|
||||||
|
if (!has_own_refchain(interp)) {
|
||||||
|
// Legacy subinterpreters share a refchain with the main interpreter.
|
||||||
|
REFCHAIN(interp) = REFCHAIN(_PyInterpreterState_Main());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_Py_hashtable_allocator_t alloc = {
|
||||||
|
// Don't use default PyMem_Malloc() and PyMem_Free() which
|
||||||
|
// require the caller to hold the GIL.
|
||||||
|
.malloc = PyMem_RawMalloc,
|
||||||
|
.free = PyMem_RawFree,
|
||||||
|
};
|
||||||
|
REFCHAIN(interp) = _Py_hashtable_new_full(
|
||||||
|
_Py_hashtable_hash_ptr, _Py_hashtable_compare_direct,
|
||||||
|
NULL, NULL, &alloc);
|
||||||
|
if (REFCHAIN(interp) == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
refchain_fini(PyInterpreterState *interp)
|
||||||
|
{
|
||||||
|
if (has_own_refchain(interp) && REFCHAIN(interp) != NULL) {
|
||||||
|
_Py_hashtable_destroy(REFCHAIN(interp));
|
||||||
|
}
|
||||||
|
REFCHAIN(interp) = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
_PyRefchain_IsTraced(PyInterpreterState *interp, PyObject *obj)
|
_PyRefchain_IsTraced(PyInterpreterState *interp, PyObject *obj)
|
||||||
{
|
{
|
||||||
|
@ -2191,16 +2233,7 @@ PyStatus
|
||||||
_PyObject_InitState(PyInterpreterState *interp)
|
_PyObject_InitState(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
#ifdef Py_TRACE_REFS
|
#ifdef Py_TRACE_REFS
|
||||||
_Py_hashtable_allocator_t alloc = {
|
if (refchain_init(interp) < 0) {
|
||||||
// Don't use default PyMem_Malloc() and PyMem_Free() which
|
|
||||||
// require the caller to hold the GIL.
|
|
||||||
.malloc = PyMem_RawMalloc,
|
|
||||||
.free = PyMem_RawFree,
|
|
||||||
};
|
|
||||||
REFCHAIN(interp) = _Py_hashtable_new_full(
|
|
||||||
_Py_hashtable_hash_ptr, _Py_hashtable_compare_direct,
|
|
||||||
NULL, NULL, &alloc);
|
|
||||||
if (REFCHAIN(interp) == NULL) {
|
|
||||||
return _PyStatus_NO_MEMORY();
|
return _PyStatus_NO_MEMORY();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -2211,8 +2244,7 @@ void
|
||||||
_PyObject_FiniState(PyInterpreterState *interp)
|
_PyObject_FiniState(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
#ifdef Py_TRACE_REFS
|
#ifdef Py_TRACE_REFS
|
||||||
_Py_hashtable_destroy(REFCHAIN(interp));
|
refchain_fini(interp);
|
||||||
REFCHAIN(interp) = NULL;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2501,42 +2533,6 @@ _Py_ResurrectReference(PyObject *op)
|
||||||
|
|
||||||
|
|
||||||
#ifdef Py_TRACE_REFS
|
#ifdef Py_TRACE_REFS
|
||||||
/* Make sure the ref is associated with the right interpreter.
|
|
||||||
* This only needs special attention for heap-allocated objects
|
|
||||||
* that have been immortalized, and only when the object might
|
|
||||||
* outlive the interpreter where it was created. That means the
|
|
||||||
* object was necessarily created using a global allocator
|
|
||||||
* (i.e. from the main interpreter). Thus in that specific case
|
|
||||||
* we move the object over to the main interpreter's refchain.
|
|
||||||
*
|
|
||||||
* This was added for the sake of the immortal interned strings,
|
|
||||||
* where legacy subinterpreters share the main interpreter's
|
|
||||||
* interned dict (and allocator), and therefore the strings can
|
|
||||||
* outlive the subinterpreter.
|
|
||||||
*
|
|
||||||
* It may make sense to fold this into _Py_SetImmortalUntracked(),
|
|
||||||
* but that requires further investigation. In the meantime, it is
|
|
||||||
* up to the caller to know if this is needed. There should be
|
|
||||||
* very few cases.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
_Py_NormalizeImmortalReference(PyObject *op)
|
|
||||||
{
|
|
||||||
assert(_Py_IsImmortal(op));
|
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
|
||||||
if (!_PyRefchain_IsTraced(interp, op)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PyInterpreterState *main_interp = _PyInterpreterState_Main();
|
|
||||||
if (interp != main_interp
|
|
||||||
&& interp->feature_flags & Py_RTFLAGS_USE_MAIN_OBMALLOC)
|
|
||||||
{
|
|
||||||
assert(!_PyRefchain_IsTraced(main_interp, op));
|
|
||||||
_PyRefchain_Remove(interp, op);
|
|
||||||
_PyRefchain_Trace(main_interp, op);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
_Py_ForgetReference(PyObject *op)
|
_Py_ForgetReference(PyObject *op)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15444,10 +15444,6 @@ _PyUnicode_InternStatic(PyInterpreterState *interp, PyObject **p)
|
||||||
assert(*p);
|
assert(*p);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Py_TRACE_REFS
|
|
||||||
extern void _Py_NormalizeImmortalReference(PyObject *);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
immortalize_interned(PyObject *s)
|
immortalize_interned(PyObject *s)
|
||||||
{
|
{
|
||||||
|
@ -15463,10 +15459,6 @@ immortalize_interned(PyObject *s)
|
||||||
#endif
|
#endif
|
||||||
_PyUnicode_STATE(s).interned = SSTATE_INTERNED_IMMORTAL;
|
_PyUnicode_STATE(s).interned = SSTATE_INTERNED_IMMORTAL;
|
||||||
_Py_SetImmortal(s);
|
_Py_SetImmortal(s);
|
||||||
#ifdef Py_TRACE_REFS
|
|
||||||
/* Make sure the ref is associated with the right interpreter. */
|
|
||||||
_Py_NormalizeImmortalReference(s);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static /* non-null */ PyObject*
|
static /* non-null */ PyObject*
|
||||||
|
|
|
@ -674,6 +674,13 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This could be done in init_interpreter() (in pystate.c) if it
|
||||||
|
// didn't depend on interp->feature_flags being set already.
|
||||||
|
status = _PyObject_InitState(interp);
|
||||||
|
if (_PyStatus_EXCEPTION(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
// initialize the interp->obmalloc state. This must be done after
|
// initialize the interp->obmalloc state. This must be done after
|
||||||
// the settings are loaded (so that feature_flags are set) but before
|
// the settings are loaded (so that feature_flags are set) but before
|
||||||
// any calls are made to obmalloc functions.
|
// any calls are made to obmalloc functions.
|
||||||
|
@ -2297,6 +2304,13 @@ new_interpreter(PyThreadState **tstate_p,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This could be done in init_interpreter() (in pystate.c) if it
|
||||||
|
// didn't depend on interp->feature_flags being set already.
|
||||||
|
status = _PyObject_InitState(interp);
|
||||||
|
if (_PyStatus_EXCEPTION(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
// initialize the interp->obmalloc state. This must be done after
|
// initialize the interp->obmalloc state. This must be done after
|
||||||
// the settings are loaded (so that feature_flags are set) but before
|
// the settings are loaded (so that feature_flags are set) but before
|
||||||
// any calls are made to obmalloc functions.
|
// any calls are made to obmalloc functions.
|
||||||
|
|
|
@ -629,10 +629,8 @@ init_interpreter(PyInterpreterState *interp,
|
||||||
assert(next != NULL || (interp == runtime->interpreters.main));
|
assert(next != NULL || (interp == runtime->interpreters.main));
|
||||||
interp->next = next;
|
interp->next = next;
|
||||||
|
|
||||||
PyStatus status = _PyObject_InitState(interp);
|
// We would call _PyObject_InitState() at this point
|
||||||
if (_PyStatus_EXCEPTION(status)) {
|
// if interp->feature_flags were alredy set.
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
_PyEval_InitState(interp);
|
_PyEval_InitState(interp);
|
||||||
_PyGC_InitState(&interp->gc);
|
_PyGC_InitState(&interp->gc);
|
||||||
|
|
Loading…
Reference in New Issue