@@ -538,12 +538,16 @@ tracemalloc_alloc(int use_calloc, void *ctx, size_t nelem, size_t elsize)
538538 return NULL ;
539539
540540 TABLES_LOCK ();
541- if (ADD_TRACE (ptr , nelem * elsize ) < 0 ) {
542- /* Failed to allocate a trace for the new memory block */
543- TABLES_UNLOCK ();
544- alloc -> free (alloc -> ctx , ptr );
545- return NULL ;
541+
542+ if (tracemalloc_config .tracing ) {
543+ if (ADD_TRACE (ptr , nelem * elsize ) < 0 ) {
544+ /* Failed to allocate a trace for the new memory block */
545+ alloc -> free (alloc -> ctx , ptr );
546+ ptr = NULL ;
547+ }
546548 }
549+ // else: gh-128679: tracemalloc.stop() was called by another thread
550+
547551 TABLES_UNLOCK ();
548552 return ptr ;
549553}
@@ -559,11 +563,15 @@ tracemalloc_realloc(void *ctx, void *ptr, size_t new_size)
559563 if (ptr2 == NULL )
560564 return NULL ;
561565
566+ TABLES_LOCK ();
567+ if (!tracemalloc_config .tracing ) {
568+ // gh-128679: tracemalloc.stop() was called by another thread
569+ goto done ;
570+ }
571+
562572 if (ptr != NULL ) {
563573 /* an existing memory block has been resized */
564574
565- TABLES_LOCK ();
566-
567575 /* tracemalloc_add_trace() updates the trace if there is already
568576 a trace at address ptr2 */
569577 if (ptr2 != ptr ) {
@@ -582,20 +590,19 @@ tracemalloc_realloc(void *ctx, void *ptr, size_t new_size)
582590 allocating memory. */
583591 Py_FatalError ("tracemalloc_realloc() failed to allocate a trace" );
584592 }
585- TABLES_UNLOCK ();
586593 }
587594 else {
588595 /* new allocation */
589596
590- TABLES_LOCK ();
591597 if (ADD_TRACE (ptr2 , new_size ) < 0 ) {
592598 /* Failed to allocate a trace for the new memory block */
593- TABLES_UNLOCK ();
594599 alloc -> free (alloc -> ctx , ptr2 );
595- return NULL ;
600+ ptr2 = NULL ;
596601 }
597- TABLES_UNLOCK ();
598602 }
603+
604+ done :
605+ TABLES_UNLOCK ();
599606 return ptr2 ;
600607}
601608
@@ -614,7 +621,12 @@ tracemalloc_free(void *ctx, void *ptr)
614621 alloc -> free (alloc -> ctx , ptr );
615622
616623 TABLES_LOCK ();
617- REMOVE_TRACE (ptr );
624+
625+ if (tracemalloc_config .tracing ) {
626+ REMOVE_TRACE (ptr );
627+ }
628+ // else: gh-128679: tracemalloc.stop() was called by another thread
629+
618630 TABLES_UNLOCK ();
619631}
620632
@@ -779,17 +791,15 @@ tracemalloc_clear_filename(void *value)
779791
780792/* reentrant flag must be set to call this function and GIL must be held */
781793static void
782- tracemalloc_clear_traces (void )
794+ tracemalloc_clear_traces_unlocked (void )
783795{
784796 /* The GIL protects variables against concurrent access */
785797 assert (PyGILState_Check ());
786798
787- TABLES_LOCK ();
788799 _Py_hashtable_clear (tracemalloc_traces );
789800 _Py_hashtable_clear (tracemalloc_domains );
790801 tracemalloc_traced_memory = 0 ;
791802 tracemalloc_peak_traced_memory = 0 ;
792- TABLES_UNLOCK ();
793803
794804 _Py_hashtable_clear (tracemalloc_tracebacks );
795805
@@ -963,6 +973,10 @@ _PyTraceMalloc_Stop(void)
963973 if (!tracemalloc_config .tracing )
964974 return ;
965975
976+ // Lock to synchronize with tracemalloc_free() which checks
977+ // 'tracing' while holding the lock.
978+ TABLES_LOCK ();
979+
966980 /* stop tracing Python memory allocations */
967981 tracemalloc_config .tracing = 0 ;
968982
@@ -973,11 +987,13 @@ _PyTraceMalloc_Stop(void)
973987 PyMem_SetAllocator (PYMEM_DOMAIN_MEM , & allocators .mem );
974988 PyMem_SetAllocator (PYMEM_DOMAIN_OBJ , & allocators .obj );
975989
976- tracemalloc_clear_traces ();
990+ tracemalloc_clear_traces_unlocked ();
977991
978992 /* release memory */
979993 raw_free (tracemalloc_traceback );
980994 tracemalloc_traceback = NULL ;
995+
996+ TABLES_UNLOCK ();
981997}
982998
983999
@@ -1307,20 +1323,19 @@ int
13071323PyTraceMalloc_Track (unsigned int domain , uintptr_t ptr ,
13081324 size_t size )
13091325{
1310- int res ;
1311- PyGILState_STATE gil_state ;
1326+ PyGILState_STATE gil_state = PyGILState_Ensure () ;
1327+ TABLES_LOCK () ;
13121328
1313- if (!tracemalloc_config .tracing ) {
1314- /* tracemalloc is not tracing: do nothing */
1315- return -2 ;
1329+ int res ;
1330+ if (tracemalloc_config .tracing ) {
1331+ res = tracemalloc_add_trace (domain , ptr , size );
1332+ }
1333+ else {
1334+ // gh-128679: tracemalloc.stop() was called by another thread
1335+ res = -2 ;
13161336 }
13171337
1318- gil_state = PyGILState_Ensure ();
1319-
1320- TABLES_LOCK ();
1321- res = tracemalloc_add_trace (domain , ptr , size );
13221338 TABLES_UNLOCK ();
1323-
13241339 PyGILState_Release (gil_state );
13251340 return res ;
13261341}
@@ -1329,16 +1344,20 @@ PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr,
13291344int
13301345PyTraceMalloc_Untrack (unsigned int domain , uintptr_t ptr )
13311346{
1332- if (!tracemalloc_config .tracing ) {
1347+ TABLES_LOCK ();
1348+
1349+ int result ;
1350+ if (tracemalloc_config .tracing ) {
1351+ tracemalloc_remove_trace (domain , ptr );
1352+ result = 0 ;
1353+ }
1354+ else {
13331355 /* tracemalloc is not tracing: do nothing */
1334- return -2 ;
1356+ result = -2 ;
13351357 }
13361358
1337- TABLES_LOCK ();
1338- tracemalloc_remove_trace (domain , ptr );
13391359 TABLES_UNLOCK ();
1340-
1341- return 0 ;
1360+ return result ;
13421361}
13431362
13441363
@@ -1376,16 +1395,21 @@ _PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event, void* Py_UNUSED(ig
13761395 int res = -1 ;
13771396
13781397 TABLES_LOCK ();
1379- trace_t * trace = _Py_hashtable_get (tracemalloc_traces , TO_PTR (ptr ));
1380- if (trace != NULL ) {
1381- /* update the traceback of the memory block */
1382- traceback_t * traceback = traceback_new ();
1383- if (traceback != NULL ) {
1384- trace -> traceback = traceback ;
1385- res = 0 ;
1398+
1399+ if (tracemalloc_config .tracing ) {
1400+ trace_t * trace = _Py_hashtable_get (tracemalloc_traces , TO_PTR (ptr ));
1401+ if (trace != NULL ) {
1402+ /* update the traceback of the memory block */
1403+ traceback_t * traceback = traceback_new ();
1404+ if (traceback != NULL ) {
1405+ trace -> traceback = traceback ;
1406+ res = 0 ;
1407+ }
13861408 }
1409+ /* else: cannot track the object, its memory block size is unknown */
13871410 }
1388- /* else: cannot track the object, its memory block size is unknown */
1411+ // else: gh-128679: tracemalloc.stop() was called by another thread
1412+
13891413 TABLES_UNLOCK ();
13901414
13911415 return res ;
@@ -1418,7 +1442,9 @@ _PyTraceMalloc_ClearTraces(void)
14181442 return ;
14191443 }
14201444 set_reentrant (1 );
1421- tracemalloc_clear_traces ();
1445+ TABLES_LOCK ();
1446+ tracemalloc_clear_traces_unlocked ();
1447+ TABLES_UNLOCK ();
14221448 set_reentrant (0 );
14231449}
14241450
0 commit comments