@@ -538,12 +538,16 @@ tracemalloc_alloc(int use_calloc, void *ctx, size_t nelem, size_t elsize)
538
538
return NULL ;
539
539
540
540
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
+ }
546
548
}
549
+ // else: gh-128679: tracemalloc.stop() was called by another thread
550
+
547
551
TABLES_UNLOCK ();
548
552
return ptr ;
549
553
}
@@ -559,11 +563,15 @@ tracemalloc_realloc(void *ctx, void *ptr, size_t new_size)
559
563
if (ptr2 == NULL )
560
564
return NULL ;
561
565
566
+ TABLES_LOCK ();
567
+ if (!tracemalloc_config .tracing ) {
568
+ // gh-128679: tracemalloc.stop() was called by another thread
569
+ goto done ;
570
+ }
571
+
562
572
if (ptr != NULL ) {
563
573
/* an existing memory block has been resized */
564
574
565
- TABLES_LOCK ();
566
-
567
575
/* tracemalloc_add_trace() updates the trace if there is already
568
576
a trace at address ptr2 */
569
577
if (ptr2 != ptr ) {
@@ -576,45 +584,46 @@ tracemalloc_realloc(void *ctx, void *ptr, size_t new_size)
576
584
memory block and so removed bytes.
577
585
578
586
This case is very unlikely: a hash entry has just been
579
- released, so the hash table should have at least one free entry.
587
+ released, so the hash table should have at least one free
588
+ entry.
580
589
581
590
The GIL and the table lock ensures that only one thread is
582
591
allocating memory. */
583
592
Py_FatalError ("tracemalloc_realloc() failed to allocate a trace" );
584
593
}
585
- TABLES_UNLOCK ();
586
594
}
587
595
else {
588
596
/* new allocation */
589
597
590
- TABLES_LOCK ();
591
598
if (ADD_TRACE (ptr2 , new_size ) < 0 ) {
592
599
/* Failed to allocate a trace for the new memory block */
593
- TABLES_UNLOCK ();
594
600
alloc -> free (alloc -> ctx , ptr2 );
595
- return NULL ;
601
+ ptr2 = NULL ;
596
602
}
597
- TABLES_UNLOCK ();
598
603
}
604
+
605
+ done :
606
+ TABLES_UNLOCK ();
599
607
return ptr2 ;
600
608
}
601
609
602
610
603
611
static void
604
612
tracemalloc_free (void * ctx , void * ptr )
605
613
{
606
- PyMemAllocatorEx * alloc = (PyMemAllocatorEx * )ctx ;
607
-
608
614
if (ptr == NULL )
609
615
return ;
610
616
611
- /* GIL cannot be locked in PyMem_RawFree() because it would introduce
612
- a deadlock in _PyThreadState_DeleteCurrent(). */
613
-
617
+ PyMemAllocatorEx * alloc = (PyMemAllocatorEx * )ctx ;
614
618
alloc -> free (alloc -> ctx , ptr );
615
619
616
620
TABLES_LOCK ();
617
- REMOVE_TRACE (ptr );
621
+
622
+ if (tracemalloc_config .tracing ) {
623
+ REMOVE_TRACE (ptr );
624
+ }
625
+ // else: gh-128679: tracemalloc.stop() was called by another thread
626
+
618
627
TABLES_UNLOCK ();
619
628
}
620
629
@@ -779,17 +788,15 @@ tracemalloc_clear_filename(void *value)
779
788
780
789
/* reentrant flag must be set to call this function and GIL must be held */
781
790
static void
782
- tracemalloc_clear_traces (void )
791
+ tracemalloc_clear_traces_unlocked (void )
783
792
{
784
793
/* The GIL protects variables against concurrent access */
785
794
assert (PyGILState_Check ());
786
795
787
- TABLES_LOCK ();
788
796
_Py_hashtable_clear (tracemalloc_traces );
789
797
_Py_hashtable_clear (tracemalloc_domains );
790
798
tracemalloc_traced_memory = 0 ;
791
799
tracemalloc_peak_traced_memory = 0 ;
792
- TABLES_UNLOCK ();
793
800
794
801
_Py_hashtable_clear (tracemalloc_tracebacks );
795
802
@@ -963,6 +970,10 @@ _PyTraceMalloc_Stop(void)
963
970
if (!tracemalloc_config .tracing )
964
971
return ;
965
972
973
+ // Lock to synchronize with tracemalloc_free() which checks
974
+ // 'tracing' while holding the lock.
975
+ TABLES_LOCK ();
976
+
966
977
/* stop tracing Python memory allocations */
967
978
tracemalloc_config .tracing = 0 ;
968
979
@@ -973,11 +984,13 @@ _PyTraceMalloc_Stop(void)
973
984
PyMem_SetAllocator (PYMEM_DOMAIN_MEM , & allocators .mem );
974
985
PyMem_SetAllocator (PYMEM_DOMAIN_OBJ , & allocators .obj );
975
986
976
- tracemalloc_clear_traces ();
987
+ tracemalloc_clear_traces_unlocked ();
977
988
978
989
/* release memory */
979
990
raw_free (tracemalloc_traceback );
980
991
tracemalloc_traceback = NULL ;
992
+
993
+ TABLES_UNLOCK ();
981
994
}
982
995
983
996
@@ -1307,20 +1320,19 @@ int
1307
1320
PyTraceMalloc_Track (unsigned int domain , uintptr_t ptr ,
1308
1321
size_t size )
1309
1322
{
1310
- int res ;
1311
- PyGILState_STATE gil_state ;
1323
+ PyGILState_STATE gil_state = PyGILState_Ensure () ;
1324
+ TABLES_LOCK () ;
1312
1325
1313
- if (!tracemalloc_config .tracing ) {
1314
- /* tracemalloc is not tracing: do nothing */
1315
- return -2 ;
1326
+ int res ;
1327
+ if (tracemalloc_config .tracing ) {
1328
+ res = tracemalloc_add_trace (domain , ptr , size );
1329
+ }
1330
+ else {
1331
+ // gh-128679: tracemalloc.stop() was called by another thread
1332
+ res = -2 ;
1316
1333
}
1317
1334
1318
- gil_state = PyGILState_Ensure ();
1319
-
1320
- TABLES_LOCK ();
1321
- res = tracemalloc_add_trace (domain , ptr , size );
1322
1335
TABLES_UNLOCK ();
1323
-
1324
1336
PyGILState_Release (gil_state );
1325
1337
return res ;
1326
1338
}
@@ -1329,16 +1341,20 @@ PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr,
1329
1341
int
1330
1342
PyTraceMalloc_Untrack (unsigned int domain , uintptr_t ptr )
1331
1343
{
1332
- if (!tracemalloc_config .tracing ) {
1344
+ TABLES_LOCK ();
1345
+
1346
+ int result ;
1347
+ if (tracemalloc_config .tracing ) {
1348
+ tracemalloc_remove_trace (domain , ptr );
1349
+ result = 0 ;
1350
+ }
1351
+ else {
1333
1352
/* tracemalloc is not tracing: do nothing */
1334
- return -2 ;
1353
+ result = -2 ;
1335
1354
}
1336
1355
1337
- TABLES_LOCK ();
1338
- tracemalloc_remove_trace (domain , ptr );
1339
1356
TABLES_UNLOCK ();
1340
-
1341
- return 0 ;
1357
+ return result ;
1342
1358
}
1343
1359
1344
1360
@@ -1376,16 +1392,21 @@ _PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event, void* Py_UNUSED(ig
1376
1392
int res = -1 ;
1377
1393
1378
1394
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 ;
1395
+
1396
+ if (tracemalloc_config .tracing ) {
1397
+ trace_t * trace = _Py_hashtable_get (tracemalloc_traces , TO_PTR (ptr ));
1398
+ if (trace != NULL ) {
1399
+ /* update the traceback of the memory block */
1400
+ traceback_t * traceback = traceback_new ();
1401
+ if (traceback != NULL ) {
1402
+ trace -> traceback = traceback ;
1403
+ res = 0 ;
1404
+ }
1386
1405
}
1406
+ /* else: cannot track the object, its memory block size is unknown */
1387
1407
}
1388
- /* else: cannot track the object, its memory block size is unknown */
1408
+ // else: gh-128679: tracemalloc.stop() was called by another thread
1409
+
1389
1410
TABLES_UNLOCK ();
1390
1411
1391
1412
return res ;
@@ -1418,7 +1439,9 @@ _PyTraceMalloc_ClearTraces(void)
1418
1439
return ;
1419
1440
}
1420
1441
set_reentrant (1 );
1421
- tracemalloc_clear_traces ();
1442
+ TABLES_LOCK ();
1443
+ tracemalloc_clear_traces_unlocked ();
1444
+ TABLES_UNLOCK ();
1422
1445
set_reentrant (0 );
1423
1446
}
1424
1447
0 commit comments