8000 [3.13] gh-125286: Share the Main Refchain With Legacy Interpreters (g… · miss-islington/cpython@909f70e · GitHub
[go: up one dir, main page]

Skip to content

Commit 909f70e

Browse files
ericsnowcurrentlyencukou
authored andcommitted
[3.13] pythongh-125286: Share the Main Refchain With Legacy Interpreters (pythongh-125709)
They used to be shared, before 3.12. Returning to sharing them resolves a failure on Py_TRACE_REFS builds.
1 parent a5b6574 commit 909f70e

File tree

7 files changed

+101
-61
lines changed

7 files changed

+101
-61
lines changed

Doc/library/sys.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,35 @@ always available.
920920
It is not guaranteed to exist in all implementations of Python.
921921

922922

923+
.. function:: getobjects(limit[, type])
924+
925+
This function only exists if CPython was built using the
926+
specialized configure option :option:`--with-trace-refs`.
927+
It is intended only for debugging garbage-collection issues.
928+
929+
Return a list of up to *limit* dynamically allocated Python objects.
930+
If *type* is given, only objects of that exact type (not subtypes)
931+
are included.
932+
933+
Objects from the list are not safe to use.
934+
Specifically, the result will include objects from all interpreters that
935+
share their object allocator state (that is, ones created with
936+
:c:member:`PyInterpreterConfig.use_main_obmalloc` set to 1
937+
or using :c:func:`Py_NewInterpreter`, and the
938+
:ref:`main interpreter <sub-interpreter-support>`).
939+
Mixing objects from different interpreters may lead to crashes
940+
or other unexpected behavior.
941+
942+
.. impl-detail::
943+
944+
This function should be used for specialized purposes only.
945+
It is not guaranteed to exist in all implementations of Python.
946+
947+
.. versionchanged:: next
948+
949+
The result may include objects from other interpreters.
950+
951+
923952
.. function:: getprofile()
924953

925954
.. index::

Doc/using/configure.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,7 @@ Debug options
708708
Effects:
709709

710710
* Define the ``Py_TRACE_REFS`` macro.
711-
* Add :func:`!sys.getobjects` function.
711+
* Add :func:`sys.getobjects` function.
712712
* Add :envvar:`PYTHONDUMPREFS` environment variable.
713713

714714
The :envvar:`PYTHONDUMPREFS` environment variable can be used to dump

Doc/whatsnew/3.13.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2706,3 +2706,14 @@ Regression Test Changes
27062706
option. If used, it specifies a module that should be imported early
27072707
in the lifecycle of the interpreter, before ``site.py`` is executed.
27082708
(Contributed by Łukasz Langa in :gh:`110769`.)
2709+
2710+
2711+
Notable changes in 3.13.1
2712+
=========================
2713+
2714+
sys
2715+
---
2716+
2717+
* The previously undocumented special function :func:`sys.getobjects`,
2718+
which only exists in specialized builds of Python, may now return objects
2719+
from other interpreters than the one it's called in.

Objects/object.c

Lines changed: 44 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,48 @@ _PyDebug_PrintTotalRefs(void) {
169169
#define REFCHAIN(interp) interp->object_state.refchain
170170
#define REFCHAIN_VALUE ((void*)(uintptr_t)1)
171171

172+
static inline int
173+
has_own_refchain(PyInterpreterState *interp)
174+
{
175+
if (interp->feature_flags & Py_RTFLAGS_USE_MAIN_OBMALLOC) {
176+
return (_Py_IsMainInterpreter(interp)
177+
|| _PyInterpreterState_Main() == NULL);
178+
}
179+
return 1;
180+
}
181+
182+
static int
183+
refchain_init(PyInterpreterState *interp)
184+
{
185+
if (!has_own_refchain(interp)) {
186+
// Legacy subinterpreters share a refchain with the main interpreter.
187+
REFCHAIN(interp) = REFCHAIN(_PyInterpreterState_Main());
188+
return 0;
189+
}
190+
_Py_hashtable_allocator_t alloc = {
191+
// Don't use default PyMem_Malloc() and PyMem_Free() which
192+
// require the caller to hold the GIL.
193+
.malloc = PyMem_RawMalloc,
194+
.free = PyMem_RawFree,
195+
};
196+
REFCHAIN(interp) = _Py_hashtable_new_full(
197+
_Py_hashtable_hash_ptr, _Py_hashtable_compare_direct,
198+
NULL, NULL, &alloc);
199+
if (REFCHAIN(interp) == NULL) {
200+
return -1;
201+
}
202+
return 0;
203+
}
204+
205+
static void
206+
refchain_fini(PyInterpreterState *interp)
207+
{
208+
if (has_own_refchain(interp) && REFCHAIN(interp) != NULL) {
209+
_Py_hashtable_destroy(REFCHAIN(interp));
210+
}
211+
REFCHAIN(interp) = NULL;
212+
}
213+
172214
bool
173215
_PyRefchain_IsTraced(PyInterpreterState *interp, PyObject *obj)
174216
{
@@ -2171,16 +2213,7 @@ PyStatus
21712213
_PyObject_InitState(PyInterpreterState *interp)
21722214
{
21732215
#ifdef Py_TRACE_REFS
2174-
_Py_hashtable_allocator_t alloc = {
2175-
// Don't use default PyMem_Malloc() and PyMem_Free() which
2176-
// require the caller to hold the GIL.
2177-
.malloc = PyMem_RawMalloc,
2178-
.free = PyMem_RawFree,
2179-
};
2180-
REFCHAIN(interp) = _Py_hashtable_new_full(
2181-
_Py_hashtable_hash_ptr, _Py_hashtable_compare_direct,
2182-
NULL, NULL, &alloc);
2183-
if (REFCHAIN(interp) == NULL) {
2216+
if (refchain_init(interp) < 0) {
21842217
return _PyStatus_NO_MEMORY();
21852218
}
21862219
#endif
@@ -2191,8 +2224,7 @@ void
21912224
_PyObject_FiniState(PyInterpreterState *interp)
21922225
{
21932226
#ifdef Py_TRACE_REFS
2194-
_Py_hashtable_destroy(REFCHAIN(interp));
2195-
REFCHAIN(interp) = NULL;
2227+
refchain_fini(interp);
21962228
#endif
21972229
}
21982230

@@ -2481,42 +2513,6 @@ _Py_ResurrectReference(PyObject *op)
24812513

24822514

24832515
#ifdef Py_TRACE_REFS
2484-
/* Make sure the ref is associated with the right interpreter.
2485-
* This only needs special attention for heap-allocated objects
2486-
* that have been immortalized, and only when the object might
2487-
* outlive the interpreter where it was created. That means the
2488-
* object was necessarily created using a global allocator
2489-
* (i.e. from the main interpreter). Thus in that specific case
2490-
* we move the object over to the main interpreter's refchain.
2491-
*
2492-
* This was added for the sake of the immortal interned strings,
2493-
* where legacy subinterpreters share the main interpreter's
2494-
* interned dict (and allocator), and therefore the strings can
2495-
* outlive the subinterpreter.
2496-
*
2497-
* It may make sense to fold this into _Py_SetImmortalUntracked(),
2498-
* but that requires further investigation. In the meantime, it is
2499-
* up to the caller to know if this is needed. There should be
2500-
* very few cases.
2501-
*/
2502-
void
2503-
_Py_NormalizeImmortalReference(PyObject *op)
2504-
{
2505-
assert(_Py_IsImmortal(op));
2506-
PyInterpreterState *interp = _PyInterpreterState_GET();
2507-
if (!_PyRefchain_IsTraced(interp, op)) {
2508-
return;
2509-
}
2510-
PyInterpreterState *main_interp = _PyInterpreterState_Main();
2511-
if (interp != main_interp
2512-
&& interp->feature_flags & Py_RTFLAGS_USE_MAIN_OBMALLOC)
2513-
{
2514-
assert(!_PyRefchain_IsTraced(main_interp, op));
2515-
_PyRefchain_Remove(interp, op);
2516-
_PyRefchain_Trace(main_interp, op);
2517-
}
2518-
}
2519-
25202516
void
25212517
_Py_ForgetReference(PyObject *op)
25222518
{

Objects/unicodeobject.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15125,10 +15125,6 @@ _PyUnicode_InternStatic(PyInterpreterState *interp, PyObject **p)
1512515125
assert(*p);
1512615126
}
1512715127

15128-
#ifdef Py_TRACE_REFS
15129-
extern void _Py_NormalizeImmortalReference(PyObject *);
15130-
#endif
15131-
1513215128
static void
1513315129
immortalize_interned(PyObject *s)
1513415130
{
@@ -15144,10 +15140,6 @@ immortalize_interned(PyObject *s)
1514415140
#endif
1514515141
_PyUnicode_STATE(s).interned = SSTATE_INTERNED_IMMORTAL;
1514615142
_Py_SetImmortal(s);
15147-
#ifdef Py_TRACE_REFS
15148-
/* Make sure the ref is associated with the right interpreter. */
15149-
_Py_NormalizeImmortalReference(s);
15150-
#endif
1515115143
}
1515215144

1515315145
static /* non-null */ PyObject*

Python/pylifecycle.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,13 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
670670
return status;
671671
}
672672

673+
// This could be done in init_interpreter() (in pystate.c) if it
674+
// didn't depend on interp->feature_flags being set already.
675+
status = _PyObject_InitState(interp);
676+
if (_PyStatus_EXCEPTION(status)) {
677+
return status;
678+
}
679+
673680
// initialize the interp->obmalloc state. This must be done after
674681
// the settings are loaded (so that feature_flags are set) but before
675682
// any calls are made to obmalloc functions.
@@ -2290,6 +2297,13 @@ new_interpreter(PyThreadState **tstate_p,
22902297
goto error;
22912298
}
22922299

2300+
// This could be done in init_interpreter() (in pystate.c) if it
2301+
// didn't depend on interp->feature_flags being set already.
2302+
status = _PyObject_InitState(interp);
2303+
if (_PyStatus_EXCEPTION(status)) {
2304+
return status;
2305+
}
2306+
22932307
// initialize the interp->obmalloc state. This must be done after
22942308
// the settings are loaded (so that feature_flags are set) but before
22952309
// any calls are made to obmalloc functions.

Python/pystate.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -632,10 +632,8 @@ init_interpreter(PyInterpreterState *interp,
632632
assert(next != NULL || (interp == runtime->interpreters.main));
633633
interp->next = next;
634634

635-
PyStatus status = _PyObject_InitState(interp);
636-
if (_PyStatus_EXCEPTION(status)) {
637-
return status;
638-
}
635+
// We would call _PyObject_InitState() at this point
636+
// if interp->feature_flags were alredy set.
639637

640638
_PyEval_InitState(interp);
641639
_PyGC_InitState(&interp->gc);

0 commit comments

Comments
 (0)
0