77#include "pycore_pyerrors.h" // _Py_FatalErrorFormat()
88#include "pycore_pymem.h"
99#include "pycore_pystate.h" // _PyInterpreterState_GET
10+ #include "pycore_obmalloc_init.h"
1011
1112#include <stdlib.h> // malloc()
1213#include <stdbool.h>
@@ -967,6 +968,12 @@ static int running_on_valgrind = -1;
967968
968969typedef struct _obmalloc_state OMState ;
969970
971+ /* obmalloc state for main interpreter and shared by all interpreters without
972+ * their own obmalloc state. By not explicitly initalizing this structure, it
973+ * will be allocated in the BSS which is a small performance win. The radix
974+ * tree arrays are fairly large but are sparsely used. */
975+ static struct _obmalloc_state obmalloc_state_main ;
976+
970977static inline int
971978has_own_state (PyInterpreterState * interp )
972979{
@@ -979,10 +986,8 @@ static inline OMState *
979986get_state (void )
980987{
981988 PyInterpreterState * interp = _PyInterpreterState_GET ();
982- if (!has_own_state (interp )) {
983- interp = _PyInterpreterState_Main ();
984- }
985- return & interp -> obmalloc ;
989+ assert (interp -> obmalloc != NULL ); // otherwise not initialized or freed
990+ return interp -> obmalloc ;
986991}
987992
988993// These macros all rely on a local "state" variable.
@@ -1030,7 +1035,11 @@ _PyInterpreterState_GetAllocatedBlocks(PyInterpreterState *interp)
10301035 "the interpreter doesn't have its own allocator" );
10311036 }
10321037#endif
1033- OMState * state = & interp -> obmalloc ;
1038+ OMState * state = interp -> obmalloc ;
1039+
1040+ if (state == NULL ) {
1041+ return 0 ;
1042+ }
10341043
10351044 Py_ssize_t n = raw_allocated_blocks ;
10361045 /* add up allocated blocks for used pools */
@@ -1052,6 +1061,8 @@ _PyInterpreterState_GetAllocatedBlocks(PyInterpreterState *interp)
10521061 return n ;
10531062}
10541063
1064+ static void free_obmalloc_arenas (PyInterpreterState * interp );
1065+
10551066void
10561067_PyInterpreterState_FinalizeAllocatedBlocks (PyInterpreterState * interp )
10571068{
@@ -1060,10 +1071,20 @@ _PyInterpreterState_FinalizeAllocatedBlocks(PyInterpreterState *interp)
10601071 return ;
10611072 }
10621073#endif
1063- if (has_own_state (interp )) {
1074+ if (has_own_state (interp ) && interp -> obmalloc != NULL ) {
10641075 Py_ssize_t leaked = _PyInterpreterState_GetAllocatedBlocks (interp );
10651076 assert (has_own_state (interp ) || leaked == 0 );
10661077 interp -> runtime -> obmalloc .interpreter_leaks += leaked ;
1078+ if (interp -> obmalloc -> heap_allocated && leaked == 0 ) {
1079+ // free the obmalloc arenas and radix tree nodes. If leaked > 0
1080+ // then some of the memory allocated by obmalloc has not been
1081+ // freed. It might be safe to free the arenas in that case but
1082+ // it's possible that extension modules are still using that
1083+ // memory. So, it is safer to not free and to leak. Perhaps there
1084+ // should be warning when this happens. It should be possible to
1085+ // use a tool like "-fsanitize=address" to track down these leaks.
1086+ free_obmalloc_arenas (interp );
1087+ }
10671088 }
10681089}
10691090
@@ -2612,7 +2633,6 @@ _PyObject_DebugDumpAddress(const void *p)
26122633 _PyMem_DumpTraceback (fileno (stderr ), p );
26132634}
26142635
2615-
26162636static size_t
26172637printone (FILE * out , const char * msg , size_t value )
26182638{
@@ -2663,9 +2683,71 @@ _PyDebugAllocatorStats(FILE *out,
26632683 (void )printone (out , buf2 , num_blocks * sizeof_block );
26642684}
26652685
2686+ int _PyMem_init_obmalloc (PyInterpreterState * interp , _PyRuntimeState * runtime )
2687+ {
2688+ #ifdef WITH_PYMALLOC
2689+ /* Initialize obmalloc, but only for subinterpreters,
2690+ since the main interpreter is initialized statically. */
2691+ if (interp == & runtime -> _main_interpreter
2692+ || (interp -> feature_flags & Py_RTFLAGS_USE_MAIN_OBMALLOC )) {
2693+ interp -> obmalloc = & obmalloc_state_main ;
2694+ interp -> obmalloc -> heap_allocated = false;
2695+ } else {
2696+ interp -> obmalloc = PyMem_RawCalloc (1 , sizeof (struct _obmalloc_state ));
2697+ if (interp -> obmalloc == NULL ) {
2698+ return 0 ;
2699+ }
2700+ interp -> obmalloc -> heap_allocated = true;
2701+ }
2702+ if (!interp -> obmalloc -> initialized ) {
2703+ // initialize the obmalloc->pools structure. This must be done
2704+ // before the obmalloc alloc/free functions can be called.
2705+ poolp temp [OBMALLOC_USED_POOLS_SIZE ] =
2706+ _obmalloc_pools_INIT (interp -> obmalloc -> pools );
2707+ memcpy (& interp -> obmalloc -> pools .used , temp , sizeof (temp ));
2708+ interp -> obmalloc -> initialized = true;
2709+ }
2710+ #endif /* WITH_PYMALLOC */
2711+ return 1 ;
2712+ }
2713+
26662714
26672715#ifdef WITH_PYMALLOC
26682716
2717+ static void
2718+ free_obmalloc_arenas (PyInterpreterState * interp )
2719+ {
2720+ OMState * state = interp -> obmalloc ;
2721+ for (uint i = 0 ; i < maxarenas ; ++ i ) {
2722+ // free each obmalloc memory arena
2723+ struct arena_object * ao = & allarenas [i ];
2724+ _PyObject_Arena .free (_PyObject_Arena .ctx ,
2725+ (void * )ao -> address , ARENA_SIZE );
2726+ }
2727+ // free the array containing pointers to all arenas
2728+ PyMem_RawFree (allarenas );
2729+ #if WITH_PYMALLOC_RADIX_TREE
2730+ #ifdef USE_INTERIOR_NODES
2731+ // Free the middle and bottom nodes of the radix tree. These are allocated
2732+ // by arena_map_mark_used() but not freed when arenas are freed.
2733+ for (int i1 = 0 ; i1 < MAP_TOP_LENGTH ; i1 ++ ) {
2734+ arena_map_mid_t * mid = arena_map_root .ptrs [i1 ];
2735+ if (mid == NULL ) {
2736+ continue ;
2737+ }
2738+ for (int i2 = 0 ; i2 < MAP_MID_LENGTH ; i2 ++ ) {
2739+ arena_map_bot_t * bot = arena_map_root .ptrs [i1 ]-> ptrs [i2 ];
2740+ if (bot == NULL ) {
2741+ continue ;
2742+ }
2743+ PyMem_RawFree (bot );
2744+ }
2745+ PyMem_RawFree (mid );
2746+ }
2747+ #endif
2748+ #endif
2749+ }
2750+
26692751#ifdef Py_DEBUG
26702752/* Is target in the list? The list is traversed via the nextpool pointers.
26712753 * The list may be NULL-terminated, or circular. Return 1 if target is in
0 commit comments