@@ -32,9 +32,10 @@ static int _PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event,
3232#define allocators _PyRuntime.tracemalloc.allocators
3333
3434
35- /* This lock is needed because tracemalloc_free() is called without
36- the GIL held from PyMem_RawFree(). It cannot acquire the lock because it
37- would introduce a deadlock in _PyThreadState_DeleteCurrent(). */
35+ /* This lock protects the trace tables. It is acquired by threads which may
36+ not have an attached thread state, such as tracemalloc_free() called from
37+ PyMem_RawFree(): tracing never acquires the GIL nor attaches a thread
38+ state. */
3839#define tables_lock _PyRuntime.tracemalloc.tables_lock
3940#define TABLES_LOCK () PyMutex_LockFlags(&tables_lock, _Py_LOCK_DONT_DETACH)
4041#define TABLES_UNLOCK () PyMutex_Unlock(&tables_lock)
@@ -303,11 +304,8 @@ traceback_hash(traceback_t *traceback)
303304
304305
305306static void
306- traceback_get_frames (traceback_t * traceback )
307+ traceback_get_frames (traceback_t * traceback , PyThreadState * tstate )
307308{
308- PyThreadState * tstate = _PyThreadState_GET ();
309- assert (tstate != NULL );
310-
311309 _PyInterpreterFrame * pyframe = _PyThreadState_GetFrame (tstate );
312310 while (pyframe ) {
313311 if (traceback -> nframe < tracemalloc_config .max_nframe ) {
@@ -329,13 +327,19 @@ traceback_new(void)
329327 traceback_t * traceback ;
330328 _Py_hashtable_entry_t * entry ;
331329
332- _Py_AssertHoldsTstate ();
330+ // A thread with no attached thread state cannot capture a Python
331+ // traceback and must not use Python objects, such as the interned
332+ // filenames: record the trace with the "<unknown>" traceback instead.
333+ PyThreadState * tstate = _PyThreadState_GET ();
334+ if (tstate == NULL ) {
335+ return tracemalloc_empty_traceback ;
336+ }
333337
334338 /* get frames */
335339 traceback = tracemalloc_traceback ;
336340 traceback -> nframe = 0 ;
337341 traceback -> total_nframe = 0 ;
338- traceback_get_frames (traceback );
342+ traceback_get_frames (traceback , tstate );
339343 if (traceback -> nframe == 0 ) {
340344 return tracemalloc_empty_traceback ;
341345 }
@@ -497,21 +501,17 @@ tracemalloc_add_trace_unlocked(unsigned int domain, uintptr_t ptr,
497501
498502
499503static void *
500- tracemalloc_alloc (int need_gil , int use_calloc ,
501- void * ctx , size_t nelem , size_t elsize )
504+ tracemalloc_alloc (int use_calloc , void * ctx , size_t nelem , size_t elsize )
502505{
503506 assert (elsize == 0 || nelem <= SIZE_MAX / elsize );
504507
505508 int reentrant = get_reentrant ();
506509
507510 // Ignore reentrant call.
508511 //
509- // For example, PyObjet_Malloc () calls
512+ // For example, PyObject_Malloc () calls
510513 // PyMem_Malloc() for allocations larger than 512 bytes: don't trace the
511514 // same memory allocation twice.
512- //
513- // If reentrant calls are not ignored, PyGILState_Ensure() can call
514- // PyMem_RawMalloc() which would call PyGILState_Ensure() again in a loop.
515515 if (!reentrant ) {
516516 set_reentrant (1 );
517517 }
@@ -532,10 +532,6 @@ tracemalloc_alloc(int need_gil, int use_calloc,
532532 goto done ;
533533 }
534534
535- PyGILState_STATE gil_state ;
536- if (need_gil ) {
537- gil_state = PyGILState_Ensure ();
538- }
539535 TABLES_LOCK ();
540536
541537 if (tracemalloc_config .tracing ) {
@@ -548,9 +544,6 @@ tracemalloc_alloc(int need_gil, int use_calloc,
548544 // else: gh-128679: tracemalloc.stop() was called by another thread
549545
550546 TABLES_UNLOCK ();
551- if (need_gil ) {
552- PyGILState_Release (gil_state );
553- }
554547
555548done :
556549 if (!reentrant ) {
@@ -561,7 +554,7 @@ tracemalloc_alloc(int need_gil, int use_calloc,
561554
562555
563556static void *
564- tracemalloc_realloc (int need_gil , void * ctx , void * ptr , size_t new_size )
557+ tracemalloc_realloc (void * ctx , void * ptr , size_t new_size )
565558{
566559 int reentrant = get_reentrant ();
567560
@@ -582,10 +575,6 @@ tracemalloc_realloc(int need_gil, void *ctx, void *ptr, size_t new_size)
582575 goto done ;
583576 }
584577
585- PyGILState_STATE gil_state ;
586- if (need_gil ) {
587- gil_state = PyGILState_Ensure ();
588- }
589578 TABLES_LOCK ();
590579
591580 if (!tracemalloc_config .tracing ) {
@@ -610,8 +599,8 @@ tracemalloc_realloc(int need_gil, void *ctx, void *ptr, size_t new_size)
610599 // This case is very unlikely: a hash entry has just been released,
611600 // so the hash table should have at least one free entry.
612601 //
613- // The GIL and the table lock ensures that only one thread is
614- // allocating memory .
602+ // The table lock ensures that no other thread touched the trace
603+ // tables in the meantime .
615604 Py_FatalError ("tracemalloc_realloc() failed to allocate a trace" );
616605 }
617606 }
@@ -627,9 +616,6 @@ tracemalloc_realloc(int need_gil, void *ctx, void *ptr, size_t new_size)
627616
628617unlock :
629618 TABLES_UNLOCK ();
630- if (need_gil ) {
631- PyGILState_Release (gil_state );
632- }
633619
634620done :
635621 if (!reentrant ) {
@@ -665,44 +651,16 @@ tracemalloc_free(void *ctx, void *ptr)
665651
666652
667653static void *
668- tracemalloc_malloc_gil (void * ctx , size_t size )
669- {
670- return tracemalloc_alloc (0 , 0 , ctx , 1 , size );
671- }
672-
673-
674- static void *
675- tracemalloc_calloc_gil (void * ctx , size_t nelem , size_t elsize )
654+ tracemalloc_malloc (void * ctx , size_t size )
676655{
677- return tracemalloc_alloc (0 , 1 , ctx , nelem , elsize );
656+ return tracemalloc_alloc (0 , ctx , 1 , size );
678657}
679658
680659
681660static void *
682- tracemalloc_realloc_gil (void * ctx , void * ptr , size_t new_size )
661+ tracemalloc_calloc (void * ctx , size_t nelem , size_t elsize )
683662{
684- return tracemalloc_realloc (0 , ctx , ptr , new_size );
685- }
686-
687-
688- static void *
689- tracemalloc_raw_malloc (void * ctx , size_t size )
690- {
691- return tracemalloc_alloc (1 , 0 , ctx , 1 , size );
692- }
693-
694-
695- static void *
696- tracemalloc_raw_calloc (void * ctx , size_t nelem , size_t elsize )
697- {
698- return tracemalloc_alloc (1 , 1 , ctx , nelem , elsize );
699- }
700-
701-
702- static void *
703- tracemalloc_raw_realloc (void * ctx , void * ptr , size_t new_size )
704- {
705- return tracemalloc_realloc (1 , ctx , ptr , new_size );
663+ return tracemalloc_alloc (1 , ctx , nelem , elsize );
706664}
707665
708666
@@ -717,7 +675,8 @@ tracemalloc_clear_filename(void *value)
717675static void
718676tracemalloc_clear_traces_unlocked (void )
719677{
720- // Clearing tracemalloc_filenames requires the GIL to call Py_DECREF()
678+ // Clearing tracemalloc_filenames requires an attached thread state to
679+ // call Py_DECREF()
721680 _Py_AssertHoldsTstate ();
722681
723682 set_reentrant (1 );
@@ -829,20 +788,15 @@ _PyTraceMalloc_Start(int max_nframe)
829788 }
830789
831790 PyMemAllocatorEx alloc ;
832- alloc .malloc = tracemalloc_raw_malloc ;
833- alloc .calloc = tracemalloc_raw_calloc ;
834- alloc .realloc = tracemalloc_raw_realloc ;
791+ alloc .malloc = tracemalloc_malloc ;
792+ alloc .calloc = tracemalloc_calloc ;
793+ alloc .realloc = tracemalloc_realloc ;
835794 alloc .free = tracemalloc_free ;
836795
837796 alloc .ctx = & allocators .raw ;
838797 PyMem_GetAllocator (PYMEM_DOMAIN_RAW , & allocators .raw );
839798 PyMem_SetAllocator (PYMEM_DOMAIN_RAW , & alloc );
840799
841- alloc .malloc = tracemalloc_malloc_gil ;
842- alloc .calloc = tracemalloc_calloc_gil ;
843- alloc .realloc = tracemalloc_realloc_gil ;
844- alloc .free = tracemalloc_free ;
845-
846800 alloc .ctx = & allocators .mem ;
847801 PyMem_GetAllocator (PYMEM_DOMAIN_MEM , & allocators .mem );
848802 PyMem_SetAllocator (PYMEM_DOMAIN_MEM , & alloc );
@@ -1221,7 +1175,6 @@ PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr,
12211175 /* tracemalloc is not tracing: do nothing */
12221176 return -2 ;
12231177 }
1224- PyGILState_STATE gil_state = PyGILState_Ensure ();
12251178 TABLES_LOCK ();
12261179
12271180 int result ;
@@ -1234,7 +1187,6 @@ PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr,
12341187 }
12351188
12361189 TABLES_UNLOCK ();
1237- PyGILState_Release (gil_state );
12381190 return result ;
12391191}
12401192
0 commit comments