gh-128679: Redesign tracemalloc locking (#128888)

* Use TABLES_LOCK() to protect 'tracemalloc_config.tracing'.
* Hold TABLES_LOCK() longer while accessing tables.
* tracemalloc_realloc() and tracemalloc_free() no longer
  remove the trace on reentrant call.
* _PyTraceMalloc_Stop() unregisters _PyTraceMalloc_TraceRef().
* _PyTraceMalloc_GetTraces() sets the reentrant flag.
* tracemalloc_clear_traces_unlocked() sets the reentrant flag.
This commit is contained in:
Victor Stinner 2025-01-15 21:22:44 +01:00 committed by GitHub
parent 080f444a58
commit 36c5e3bcc2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 302 additions and 364 deletions

View file

@ -299,12 +299,6 @@ Py_ssize_t _Py_ExplicitMergeRefcount(PyObject *op, Py_ssize_t extra);
extern int _PyType_CheckConsistency(PyTypeObject *type); extern int _PyType_CheckConsistency(PyTypeObject *type);
extern int _PyDict_CheckConsistency(PyObject *mp, int check_content); extern int _PyDict_CheckConsistency(PyObject *mp, int check_content);
/* Update the Python traceback of an object. This function must be called
when a memory block is reused from a free list.
Internal function called by _Py_NewReference(). */
extern int _PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event, void*);
// Fast inlined version of PyType_HasFeature() // Fast inlined version of PyType_HasFeature()
static inline int static inline int
_PyType_HasFeature(PyTypeObject *type, unsigned long feature) { _PyType_HasFeature(PyTypeObject *type, unsigned long feature) {

View file

@ -25,7 +25,7 @@ struct _PyTraceMalloc_Config {
} initialized; } initialized;
/* Is tracemalloc tracing memory allocations? /* Is tracemalloc tracing memory allocations?
Variable protected by the GIL */ Variable protected by the TABLES_LOCK(). */
int tracing; int tracing;
/* limit of the number of frames in a traceback, 1 by default. /* limit of the number of frames in a traceback, 1 by default.
@ -85,14 +85,14 @@ struct _tracemalloc_runtime_state {
size_t peak_traced_memory; size_t peak_traced_memory;
/* Hash table used as a set to intern filenames: /* Hash table used as a set to intern filenames:
PyObject* => PyObject*. PyObject* => PyObject*.
Protected by the GIL */ Protected by the TABLES_LOCK(). */
_Py_hashtable_t *filenames; _Py_hashtable_t *filenames;
/* Buffer to store a new traceback in traceback_new(). /* Buffer to store a new traceback in traceback_new().
Protected by the GIL. */ Protected by the TABLES_LOCK(). */
struct tracemalloc_traceback *traceback; struct tracemalloc_traceback *traceback;
/* Hash table used as a set to intern tracebacks: /* Hash table used as a set to intern tracebacks:
traceback_t* => traceback_t* traceback_t* => traceback_t*
Protected by the GIL */ Protected by the TABLES_LOCK(). */
_Py_hashtable_t *tracebacks; _Py_hashtable_t *tracebacks;
/* pointer (void*) => trace (trace_t*). /* pointer (void*) => trace (trace_t*).
Protected by TABLES_LOCK(). */ Protected by TABLES_LOCK(). */
@ -144,7 +144,7 @@ extern PyObject* _PyTraceMalloc_GetTraces(void);
extern PyObject* _PyTraceMalloc_GetObjectTraceback(PyObject *obj); extern PyObject* _PyTraceMalloc_GetObjectTraceback(PyObject *obj);
/* Initialize tracemalloc */ /* Initialize tracemalloc */
extern int _PyTraceMalloc_Init(void); extern PyStatus _PyTraceMalloc_Init(void);
/* Start tracemalloc */ /* Start tracemalloc */
extern int _PyTraceMalloc_Start(int max_nframe); extern int _PyTraceMalloc_Start(int max_nframe);

View file

@ -215,18 +215,14 @@ static struct PyModuleDef module_def = {
PyMODINIT_FUNC PyMODINIT_FUNC
PyInit__tracemalloc(void) PyInit__tracemalloc(void)
{ {
PyObject *m; PyObject *mod = PyModule_Create(&module_def);
m = PyModule_Create(&module_def); if (mod == NULL) {
if (m == NULL)
return NULL;
#ifdef Py_GIL_DISABLED
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
#endif
if (_PyTraceMalloc_Init() < 0) {
Py_DECREF(m);
return NULL; return NULL;
} }
return m; #ifdef Py_GIL_DISABLED
PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED);
#endif
return mod;
} }

View file

@ -707,7 +707,12 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
// 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.
if (_PyMem_init_obmalloc(interp) < 0) { if (_PyMem_init_obmalloc(interp) < 0) {
return _PyStatus_NO_MEMORY(); return _PyStatus_NO_MEMORY();
}
status = _PyTraceMalloc_Init();
if (_PyStatus_EXCEPTION(status)) {
return status;
} }
PyThreadState *tstate = _PyThreadState_New(interp, PyThreadState *tstate = _PyThreadState_New(interp,

File diff suppressed because it is too large Load diff