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.
|
||||
|
||||
|
||||
.. 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()
|
||||
|
||||
.. index::
|
||||
|
|
|
@ -702,7 +702,7 @@ Debug options
|
|||
Effects:
|
||||
|
||||
* Define the ``Py_TRACE_REFS`` macro.
|
||||
* Add :func:`!sys.getobjects` function.
|
||||
* Add :func:`sys.getobjects` function.
|
||||
* Add :envvar:`PYTHONDUMPREFS` environment variable.
|
||||
|
||||
The :envvar:`PYTHONDUMPREFS` environment variable can be used to dump
|
||||
|
|
|
@ -416,6 +416,15 @@ symtable
|
|||
|
||||
(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
|
||||
-----------
|
||||
|
||||
|
|
|
@ -171,6 +171,48 @@ _PyDebug_PrintTotalRefs(void) {
|
|||
#define REFCHAIN(interp) interp->object_state.refchain
|
||||
#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
|
||||
_PyRefchain_IsTraced(PyInterpreterState *interp, PyObject *obj)
|
||||
{
|
||||
|
@ -2191,16 +2233,7 @@ PyStatus
|
|||
_PyObject_InitState(PyInterpreterState *interp)
|
||||
{
|
||||
#ifdef Py_TRACE_REFS
|
||||
_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) {
|
||||
if (refchain_init(interp) < 0) {
|
||||
return _PyStatus_NO_MEMORY();
|
||||
}
|
||||
#endif
|
||||
|
@ -2211,8 +2244,7 @@ void
|
|||
_PyObject_FiniState(PyInterpreterState *interp)
|
||||
{
|
||||
#ifdef Py_TRACE_REFS
|
||||
_Py_hashtable_destroy(REFCHAIN(interp));
|
||||
REFCHAIN(interp) = NULL;
|
||||
refchain_fini(interp);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -2501,42 +2533,6 @@ _Py_ResurrectReference(PyObject *op)
|
|||
|
||||
|
||||
#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
|
||||
_Py_ForgetReference(PyObject *op)
|
||||
{
|
||||
|
|
|
@ -15444,10 +15444,6 @@ _PyUnicode_InternStatic(PyInterpreterState *interp, PyObject **p)
|
|||
assert(*p);
|
||||
}
|
||||
|
||||
#ifdef Py_TRACE_REFS
|
||||
extern void _Py_NormalizeImmortalReference(PyObject *);
|
||||
#endif
|
||||
|
||||
static void
|
||||
immortalize_interned(PyObject *s)
|
||||
{
|
||||
|
@ -15463,10 +15459,6 @@ immortalize_interned(PyObject *s)
|
|||
#endif
|
||||
_PyUnicode_STATE(s).interned = SSTATE_INTERNED_IMMORTAL;
|
||||
_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*
|
||||
|
|
|
@ -674,6 +674,13 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
|
|||
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
|
||||
// the settings are loaded (so that feature_flags are set) but before
|
||||
// any calls are made to obmalloc functions.
|
||||
|
@ -2297,6 +2304,13 @@ new_interpreter(PyThreadState **tstate_p,
|
|||
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
|
||||
// the settings are loaded (so that feature_flags are set) but before
|
||||
// any calls are made to obmalloc functions.
|
||||
|
|
|
@ -629,10 +629,8 @@ init_interpreter(PyInterpreterState *interp,
|
|||
assert(next != NULL || (interp == runtime->interpreters.main));
|
||||
interp->next = next;
|
||||
|
||||
PyStatus status = _PyObject_InitState(interp);
|
||||
if (_PyStatus_EXCEPTION(status)) {
|
||||
return status;
|
||||
}
|
||||
// We would call _PyObject_InitState() at this point
|
||||
// if interp->feature_flags were alredy set.
|
||||
|
||||
_PyEval_InitState(interp);
|
||||
_PyGC_InitState(&interp->gc);
|
||||
|
|
Loading…
Reference in New Issue