2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								/* ----------------------------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								Copyright  ( c )  2018 - 2020 ,  Microsoft  Research ,  Daan  Leijen 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								This  is  free  software ;  you  can  redistribute  it  and / or  modify  it  under  the 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								terms  of  the  MIT  license .  A  copy  of  the  license  can  be  found  in  the  file 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								" LICENSE "  at  the  root  of  this  distribution . 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# include  "mimalloc.h" 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# include  "mimalloc/internal.h" 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# include  "mimalloc/atomic.h" 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# include  <string.h>  // memset 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# include  <stdio.h> 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# define MI_PAGE_HUGE_ALIGN   (256*1024) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_segment_try_purge ( mi_segment_t *  segment ,  bool  force ,  mi_stats_t *  stats ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// -------------------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// commit mask
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// -------------------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  bool  mi_commit_mask_all_set ( const  mi_commit_mask_t *  commit ,  const  mi_commit_mask_t *  cm )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  for  ( size_t  i  =  0 ;  i  <  MI_COMMIT_MASK_FIELD_COUNT ;  i + + )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( ( commit - > mask [ i ]  &  cm - > mask [ i ] )  ! =  cm - > mask [ i ] )  return  false ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  bool  mi_commit_mask_any_set ( const  mi_commit_mask_t *  commit ,  const  mi_commit_mask_t *  cm )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  for  ( size_t  i  =  0 ;  i  <  MI_COMMIT_MASK_FIELD_COUNT ;  i + + )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( ( commit - > mask [ i ]  &  cm - > mask [ i ] )  ! =  0 )  return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  false ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_commit_mask_create_intersect ( const  mi_commit_mask_t *  commit ,  const  mi_commit_mask_t *  cm ,  mi_commit_mask_t *  res )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  for  ( size_t  i  =  0 ;  i  <  MI_COMMIT_MASK_FIELD_COUNT ;  i + + )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    res - > mask [ i ]  =  ( commit - > mask [ i ]  &  cm - > mask [ i ] ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_commit_mask_clear ( mi_commit_mask_t *  res ,  const  mi_commit_mask_t *  cm )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  for  ( size_t  i  =  0 ;  i  <  MI_COMMIT_MASK_FIELD_COUNT ;  i + + )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    res - > mask [ i ]  & =  ~ ( cm - > mask [ i ] ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_commit_mask_set ( mi_commit_mask_t *  res ,  const  mi_commit_mask_t *  cm )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  for  ( size_t  i  =  0 ;  i  <  MI_COMMIT_MASK_FIELD_COUNT ;  i + + )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    res - > mask [ i ]  | =  cm - > mask [ i ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_commit_mask_create ( size_t  bitidx ,  size_t  bitcount ,  mi_commit_mask_t *  cm )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( bitidx  <  MI_COMMIT_MASK_BITS ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( ( bitidx  +  bitcount )  < =  MI_COMMIT_MASK_BITS ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( bitcount  = =  MI_COMMIT_MASK_BITS )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( bitidx = = 0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_commit_mask_create_full ( cm ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  if  ( bitcount  = =  0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_commit_mask_create_empty ( cm ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_commit_mask_create_empty ( cm ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    size_t  i  =  bitidx  /  MI_COMMIT_MASK_FIELD_BITS ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    size_t  ofs  =  bitidx  %  MI_COMMIT_MASK_FIELD_BITS ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    while  ( bitcount  >  0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_assert_internal ( i  <  MI_COMMIT_MASK_FIELD_COUNT ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      size_t  avail  =  MI_COMMIT_MASK_FIELD_BITS  -  ofs ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      size_t  count  =  ( bitcount  >  avail  ?  avail  :  bitcount ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      size_t  mask  =  ( count  > =  MI_COMMIT_MASK_FIELD_BITS  ?  ~ ( ( size_t ) 0 )  :  ( ( ( size_t ) 1  < <  count )  -  1 )  < <  ofs ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      cm - > mask [ i ]  =  mask ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      bitcount  - =  count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      ofs  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      i + + ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								size_t  _mi_commit_mask_committed_size ( const  mi_commit_mask_t *  cm ,  size_t  total )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( ( total % MI_COMMIT_MASK_BITS ) = = 0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  count  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  for  ( size_t  i  =  0 ;  i  <  MI_COMMIT_MASK_FIELD_COUNT ;  i + + )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    size_t  mask  =  cm - > mask [ i ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( ~ mask  = =  0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      count  + =  MI_COMMIT_MASK_FIELD_BITS ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      for  ( ;  mask  ! =  0 ;  mask  > > =  1 )  {   // todo: use popcount
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ( ( mask & 1 ) ! = 0 )  count + + ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // we use total since for huge segments each commit bit may represent a larger size
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  ( ( total  /  MI_COMMIT_MASK_BITS )  *  count ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								size_t  _mi_commit_mask_next_run ( const  mi_commit_mask_t *  cm ,  size_t *  idx )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  i  =  ( * idx )  /  MI_COMMIT_MASK_FIELD_BITS ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  ofs  =  ( * idx )  %  MI_COMMIT_MASK_FIELD_BITS ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  mask  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // find first ones
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  while  ( i  <  MI_COMMIT_MASK_FIELD_COUNT )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mask  =  cm - > mask [ i ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mask  > > =  ofs ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( mask  ! =  0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      while  ( ( mask & 1 )  = =  0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        mask  > > =  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        ofs + + ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      break ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    i + + ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    ofs  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( i  > =  MI_COMMIT_MASK_FIELD_COUNT )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // not found
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    * idx  =  MI_COMMIT_MASK_BITS ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // found, count ones
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    size_t  count  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    * idx  =  ( i * MI_COMMIT_MASK_FIELD_BITS )  +  ofs ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    do  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_assert_internal ( ofs  <  MI_COMMIT_MASK_FIELD_BITS  & &  ( mask & 1 )  = =  1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      do  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        count + + ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        mask  > > =  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      }  while  ( ( mask & 1 )  = =  1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  ( ( ( ( * idx  +  count )  %  MI_COMMIT_MASK_FIELD_BITS )  = =  0 ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        i + + ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ( i  > =  MI_COMMIT_MASK_FIELD_COUNT )  break ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        mask  =  cm - > mask [ i ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        ofs  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    }  while  ( ( mask & 1 )  = =  1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( count  >  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* --------------------------------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  Segment  allocation 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  If  a   thread  ends ,  it  " abandons "  pages  with  used  blocks 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  and  there  is  an  abandoned  segment  list  whose  segments  can 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  be  reclaimed  by  still  running  threads ,  much  like  work - stealing . 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								   Slices 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  const  mi_slice_t *  mi_segment_slices_end ( const  mi_segment_t *  segment )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  & segment - > slices [ segment - > slice_entries ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  uint8_t *  mi_slice_start ( const  mi_slice_t *  slice )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment  =  _mi_ptr_segment ( slice ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice  > =  segment - > slices  & &  slice  <  mi_segment_slices_end ( segment ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  ( ( uint8_t * ) segment  +  ( ( slice  -  segment - > slices ) * MI_SEGMENT_SLICE_SIZE ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								   Bins 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// Use bit scan forward to quickly find the first zero bit if it is available
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  inline  size_t  mi_slice_bin8 ( size_t  slice_count )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( slice_count < = 1 )  return  slice_count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice_count  < =  MI_SLICES_PER_SEGMENT ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  slice_count - - ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  s  =  mi_bsr ( slice_count ) ;   // slice_count > 1
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( s  < =  2 )  return  slice_count  +  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  bin  =  ( ( s  < <  2 )  |  ( ( slice_count  > >  ( s  -  2 ) ) & 0x03 ) )  -  4 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  bin ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  inline  size_t  mi_slice_bin ( size_t  slice_count )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice_count * MI_SEGMENT_SLICE_SIZE  < =  MI_SEGMENT_SIZE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_slice_bin8 ( MI_SLICES_PER_SEGMENT )  < =  MI_SEGMENT_BIN_MAX ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  bin  =  mi_slice_bin8 ( slice_count ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( bin  < =  MI_SEGMENT_BIN_MAX ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  bin ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  inline  size_t  mi_slice_index ( const  mi_slice_t *  slice )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment  =  _mi_ptr_segment ( slice ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  ptrdiff_t  index  =  slice  -  segment - > slices ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( index  > =  0  & &  index  <  ( ptrdiff_t ) segment - > slice_entries ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  index ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								   Slice  span  queues 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_span_queue_push ( mi_span_queue_t *  sq ,  mi_slice_t *  slice )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // todo: or push to the end?
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice - > prev  = =  NULL  & &  slice - > next = = NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  slice - > prev  =  NULL ;  // paranoia
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  slice - > next  =  sq - > first ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  sq - > first  =  slice ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( slice - > next  ! =  NULL )  slice - > next - > prev  =  slice ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                     else  sq - > last  =  slice ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  slice - > xblock_size  =  0 ;  // free
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  mi_span_queue_t *  mi_span_queue_for ( size_t  slice_count ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  bin  =  mi_slice_bin ( slice_count ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_span_queue_t *  sq  =  & tld - > spans [ bin ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( sq - > slice_count  > =  slice_count ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  sq ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_span_queue_delete ( mi_span_queue_t *  sq ,  mi_slice_t *  slice )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice - > xblock_size = = 0  & &  slice - > slice_count > 0  & &  slice - > slice_offset = = 0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // should work too if the queue does not contain slice (which can happen during reclaim)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( slice - > prev  ! =  NULL )  slice - > prev - > next  =  slice - > next ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( slice  = =  sq - > first )  sq - > first  =  slice - > next ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( slice - > next  ! =  NULL )  slice - > next - > prev  =  slice - > prev ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( slice  = =  sq - > last )  sq - > last  =  slice - > prev ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  slice - > prev  =  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  slice - > next  =  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  slice - > xblock_size  =  1 ;  // no more free
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								 Invariant  checking 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  bool  mi_slice_is_used ( const  mi_slice_t *  slice )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  ( slice - > xblock_size  >  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# if (MI_DEBUG>=3) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  bool  mi_span_queue_contains ( mi_span_queue_t *  sq ,  mi_slice_t *  slice )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  for  ( mi_slice_t *  s  =  sq - > first ;  s  ! =  NULL ;  s  =  s - > next )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( s = = slice )  return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  false ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  bool  mi_segment_is_valid ( mi_segment_t *  segment ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment  ! =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( _mi_ptr_cookie ( segment )  = =  segment - > cookie ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > abandoned  < =  segment - > used ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > thread_id  = =  0  | |  segment - > thread_id  = =  _mi_thread_id ( ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_commit_mask_all_set ( & segment - > commit_mask ,  & segment - > purge_mask ) ) ;  // can only decommit committed blocks
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  //mi_assert_internal(segment->segment_info_size % MI_SEGMENT_SLICE_SIZE == 0);
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_slice_t *  slice  =  & segment - > slices [ 0 ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  mi_slice_t *  end  =  mi_segment_slices_end ( segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  used_count  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_span_queue_t *  sq ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  while ( slice  <  end )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( slice - > slice_count  >  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( slice - > slice_offset  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    size_t  index  =  mi_slice_index ( slice ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    size_t  maxindex  =  ( index  +  slice - > slice_count  > =  segment - > slice_entries  ?  segment - > slice_entries  :  index  +  slice - > slice_count )  -  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( mi_slice_is_used ( slice ) )  {  // a page in use, we need at least MAX_SLICE_OFFSET valid back offsets
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      used_count + + ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      for  ( size_t  i  =  0 ;  i  < =  MI_MAX_SLICE_OFFSET  & &  index  +  i  < =  maxindex ;  i + + )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        mi_assert_internal ( segment - > slices [ index  +  i ] . slice_offset  = =  i * sizeof ( mi_slice_t ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        mi_assert_internal ( i = = 0  | |  segment - > slices [ index  +  i ] . slice_count  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        mi_assert_internal ( i = = 0  | |  segment - > slices [ index  +  i ] . xblock_size  = =  1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // and the last entry as well (for coalescing)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      const  mi_slice_t *  last  =  slice  +  slice - > slice_count  -  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  ( last  >  slice  & &  last  <  mi_segment_slices_end ( segment ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        mi_assert_internal ( last - > slice_offset  = =  ( slice - > slice_count - 1 ) * sizeof ( mi_slice_t ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        mi_assert_internal ( last - > slice_count  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        mi_assert_internal ( last - > xblock_size  = =  1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else  {   // free range of slices; only last slice needs a valid back offset
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_slice_t *  last  =  & segment - > slices [ maxindex ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  ( segment - > kind  ! =  MI_SEGMENT_HUGE  | |  slice - > slice_count  < =  ( segment - > slice_entries  -  segment - > segment_info_slices ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        mi_assert_internal ( ( uint8_t * ) slice  = =  ( uint8_t * ) last  -  last - > slice_offset ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_assert_internal ( slice  = =  last  | |  last - > slice_count  = =  0  ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_assert_internal ( last - > xblock_size  = =  0  | |  ( segment - > kind = = MI_SEGMENT_HUGE  & &  last - > xblock_size = = 1 ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  ( segment - > kind  ! =  MI_SEGMENT_HUGE  & &  segment - > thread_id  ! =  0 )  {  // segment is not huge or abandoned
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        sq  =  mi_span_queue_for ( slice - > slice_count , tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        mi_assert_internal ( mi_span_queue_contains ( sq , slice ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    slice  =  & segment - > slices [ maxindex + 1 ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice  = =  end ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( used_count  = =  segment - > used  +  1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# endif 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								 Segment  size  calculations 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  size_t  mi_segment_info_size ( mi_segment_t *  segment )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  segment - > segment_info_slices  *  MI_SEGMENT_SLICE_SIZE ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  uint8_t *  _mi_segment_page_start_from_slice ( const  mi_segment_t *  segment ,  const  mi_slice_t *  slice ,  size_t  xblock_size ,  size_t *  page_size ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  ptrdiff_t  idx  =  slice  -  segment - > slices ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  psize  =  ( size_t ) slice - > slice_count  *  MI_SEGMENT_SLICE_SIZE ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // make the start not OS page aligned for smaller blocks to avoid page/cache effects
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // note: the offset must always be an xblock_size multiple since we assume small allocations
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // are aligned (see `mi_heap_malloc_aligned`).
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  start_offset  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( xblock_size  > =  MI_INTPTR_SIZE )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( xblock_size  < =  64 )  {  start_offset  =  3 * xblock_size ;  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else  if  ( xblock_size  < =  512 )  {  start_offset  =  xblock_size ;  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( page_size  ! =  NULL )  {  * page_size  =  psize  -  start_offset ;  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  ( uint8_t * ) segment  +  ( ( idx * MI_SEGMENT_SLICE_SIZE )  +  start_offset ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// Start of the page available memory; can be used on uninitialized pages
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								uint8_t *  _mi_segment_page_start ( const  mi_segment_t *  segment ,  const  mi_page_t *  page ,  size_t *  page_size ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  mi_slice_t *  slice  =  mi_page_to_slice ( ( mi_page_t * ) page ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  uint8_t *  p  =  _mi_segment_page_start_from_slice ( segment ,  slice ,  page - > xblock_size ,  page_size ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( page - > xblock_size  >  0  | |  _mi_ptr_page ( p )  = =  page ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( _mi_ptr_segment ( p )  = =  segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  p ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  size_t  mi_segment_calculate_slices ( size_t  required ,  size_t *  pre_size ,  size_t *  info_slices )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  page_size  =  _mi_os_page_size ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  isize      =  _mi_align_up ( sizeof ( mi_segment_t ) ,  page_size ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  guardsize  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( MI_SECURE > 0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // in secure mode, we set up a protected page in between the segment info
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // and the page data (and one at the end of the segment)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    guardsize  =  page_size ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( required  >  0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      required  =  _mi_align_up ( required ,  MI_SEGMENT_SLICE_SIZE )  +  page_size ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( pre_size  ! =  NULL )  * pre_size  =  isize ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  isize  =  _mi_align_up ( isize  +  guardsize ,  MI_SEGMENT_SLICE_SIZE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( info_slices  ! =  NULL )  * info_slices  =  isize  /  MI_SEGMENT_SLICE_SIZE ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  segment_size  =  ( required = = 0  ?  MI_SEGMENT_SIZE  :  _mi_align_up (  required  +  isize  +  guardsize ,  MI_SEGMENT_SLICE_SIZE )  ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment_size  %  MI_SEGMENT_SLICE_SIZE  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  ( segment_size  /  MI_SEGMENT_SLICE_SIZE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* ----------------------------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								Segment  caches 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								We  keep  a  small  segment  cache  per  thread  to  increase  local 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								reuse  and  avoid  setting / clearing  guard  pages  in  secure  mode . 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_segments_track_size ( long  segment_size ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( segment_size > = 0 )  _mi_stat_increase ( & tld - > stats - > segments , 1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                  else  _mi_stat_decrease ( & tld - > stats - > segments , 1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  tld - > count  + =  ( segment_size  > =  0  ?  1  :  - 1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( tld - > count  >  tld - > peak_count )  tld - > peak_count  =  tld - > count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  tld - > current_size  + =  segment_size ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( tld - > current_size  >  tld - > peak_size )  tld - > peak_size  =  tld - > current_size ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_segment_os_free ( mi_segment_t *  segment ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > thread_id  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  _mi_segment_map_freed_at ( segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segments_track_size ( - ( ( long ) mi_segment_size ( segment ) ) , tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( MI_SECURE > 0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // _mi_os_unprotect(segment, mi_segment_size(segment)); // ensure no more guard pages are set
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // unprotect the guard pages; we cannot just unprotect the whole segment size as part may be decommitted
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    size_t  os_pagesize  =  _mi_os_page_size ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    _mi_os_unprotect ( ( uint8_t * ) segment  +  mi_segment_info_size ( segment )  -  os_pagesize ,  os_pagesize ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    uint8_t *  end  =  ( uint8_t * ) segment  +  mi_segment_size ( segment )  -  os_pagesize ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    _mi_os_unprotect ( end ,  os_pagesize ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // purge delayed decommits now? (no, leave it to the arena)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // mi_segment_try_purge(segment,true,tld->stats);
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  size_t  size  =  mi_segment_size ( segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  size_t  csize  =  _mi_commit_mask_committed_size ( & segment - > commit_mask ,  size ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  _mi_abandoned_await_readers ( tld - > abandoned ) ;   // wait until safe to free
 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  _mi_arena_free ( segment ,  mi_segment_size ( segment ) ,  csize ,  segment - > memid ,  tld - > stats ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// called by threads that are terminating
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								void  _mi_segment_thread_collect ( mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  MI_UNUSED ( tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // nothing to do
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								   Commit / Decommit  ranges 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_segment_commit_mask ( mi_segment_t *  segment ,  bool  conservative ,  uint8_t *  p ,  size_t  size ,  uint8_t * *  start_p ,  size_t *  full_size ,  mi_commit_mask_t *  cm )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( _mi_ptr_segment ( p  +  1 )  = =  segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > kind  ! =  MI_SEGMENT_HUGE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_commit_mask_create_empty ( cm ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( size  = =  0  | |  size  >  MI_SEGMENT_SIZE  | |  segment - > kind  = =  MI_SEGMENT_HUGE )  return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  size_t  segstart  =  mi_segment_info_size ( segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  size_t  segsize  =  mi_segment_size ( segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( p  > =  ( uint8_t * ) segment  +  segsize )  return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  pstart  =  ( p  -  ( uint8_t * ) segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( pstart  +  size  < =  segsize ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  start ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  end ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( conservative )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // decommit conservative
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    start  =  _mi_align_up ( pstart ,  MI_COMMIT_SIZE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    end    =  _mi_align_down ( pstart  +  size ,  MI_COMMIT_SIZE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( start  > =  segstart ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( end  < =  segsize ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // commit liberal
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    start  =  _mi_align_down ( pstart ,  MI_MINIMAL_COMMIT_SIZE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    end    =  _mi_align_up ( pstart  +  size ,  MI_MINIMAL_COMMIT_SIZE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( pstart  > =  segstart  & &  start  <  segstart )  {   // note: the mask is also calculated for an initial commit of the info area
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    start  =  segstart ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( end  >  segsize )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    end  =  segsize ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( start  < =  pstart  & &  ( pstart  +  size )  < =  end ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( start  %  MI_COMMIT_SIZE = = 0  & &  end  %  MI_COMMIT_SIZE  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  * start_p    =  ( uint8_t * ) segment  +  start ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  * full_size  =  ( end  >  start  ?  end  -  start  :  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( * full_size  = =  0 )  return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  bitidx  =  start  /  MI_COMMIT_SIZE ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( bitidx  <  MI_COMMIT_MASK_BITS ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  bitcount  =  * full_size  /  MI_COMMIT_SIZE ;  // can be 0
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( bitidx  +  bitcount  >  MI_COMMIT_MASK_BITS )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    _mi_warning_message ( " commit mask overflow: idx=%zu count=%zu start=%zx end=%zx p=0x%p size=%zu fullsize=%zu \n " ,  bitidx ,  bitcount ,  start ,  end ,  p ,  size ,  * full_size ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( ( bitidx  +  bitcount )  < =  MI_COMMIT_MASK_BITS ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_commit_mask_create ( bitidx ,  bitcount ,  cm ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  bool  mi_segment_commit ( mi_segment_t *  segment ,  uint8_t *  p ,  size_t  size ,  mi_stats_t *  stats )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_commit_mask_all_set ( & segment - > commit_mask ,  & segment - > purge_mask ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // commit liberal
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  uint8_t *  start  =  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t    full_size  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_commit_mask_t  mask ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_commit_mask ( segment ,  false  /* conservative? */ ,  p ,  size ,  & start ,  & full_size ,  & mask ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( mi_commit_mask_is_empty ( & mask )  | |  full_size  = =  0 )  return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( ! mi_commit_mask_all_set ( & segment - > commit_mask ,  & mask ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // committing
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    bool  is_zero  =  false ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_commit_mask_t  cmask ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_commit_mask_create_intersect ( & segment - > commit_mask ,  & mask ,  & cmask ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    _mi_stat_decrease ( & _mi_stats_main . committed ,  _mi_commit_mask_committed_size ( & cmask ,  MI_SEGMENT_SIZE ) ) ;  // adjust for overlap
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( ! _mi_os_commit ( start ,  full_size ,  & is_zero ,  stats ) )  return  false ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_commit_mask_set ( & segment - > commit_mask ,  & mask ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // increase purge expiration when using part of delayed purges -- we assume more allocations are coming soon.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( mi_commit_mask_any_set ( & segment - > purge_mask ,  & mask ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    segment - > purge_expire  =  _mi_clock_now ( )  +  mi_option_get ( mi_option_purge_delay ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // always clear any delayed purges in our range (as they are either committed now)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_commit_mask_clear ( & segment - > purge_mask ,  & mask ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  bool  mi_segment_ensure_committed ( mi_segment_t *  segment ,  uint8_t *  p ,  size_t  size ,  mi_stats_t *  stats )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_commit_mask_all_set ( & segment - > commit_mask ,  & segment - > purge_mask ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // note: assumes commit_mask is always full for huge segments as otherwise the commit mask bits can overflow
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( mi_commit_mask_is_full ( & segment - > commit_mask )  & &  mi_commit_mask_is_empty ( & segment - > purge_mask ) )  return  true ;  // fully committed
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > kind  ! =  MI_SEGMENT_HUGE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  mi_segment_commit ( segment ,  p ,  size ,  stats ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  bool  mi_segment_purge ( mi_segment_t *  segment ,  uint8_t *  p ,  size_t  size ,  mi_stats_t *  stats )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_commit_mask_all_set ( & segment - > commit_mask ,  & segment - > purge_mask ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( ! segment - > allow_purge )  return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // purge conservative
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  uint8_t *  start  =  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t    full_size  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_commit_mask_t  mask ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_commit_mask ( segment ,  true  /* conservative? */ ,  p ,  size ,  & start ,  & full_size ,  & mask ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( mi_commit_mask_is_empty ( & mask )  | |  full_size = = 0 )  return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( mi_commit_mask_any_set ( & segment - > commit_mask ,  & mask ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // purging
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( ( void * ) start  ! =  ( void * ) segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( segment - > allow_decommit ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    const  bool  decommitted  =  _mi_os_purge ( start ,  full_size ,  stats ) ;   // reset or decommit
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( decommitted )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_commit_mask_t  cmask ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_commit_mask_create_intersect ( & segment - > commit_mask ,  & mask ,  & cmask ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      _mi_stat_increase ( & _mi_stats_main . committed ,  full_size  -  _mi_commit_mask_committed_size ( & cmask ,  MI_SEGMENT_SIZE ) ) ;  // adjust for double counting
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_commit_mask_clear ( & segment - > commit_mask ,  & mask ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // always clear any scheduled purges in our range
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_commit_mask_clear ( & segment - > purge_mask ,  & mask ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_segment_schedule_purge ( mi_segment_t *  segment ,  uint8_t *  p ,  size_t  size ,  mi_stats_t *  stats )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( ! segment - > allow_purge )  return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( mi_option_get ( mi_option_purge_delay )  = =  0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_segment_purge ( segment ,  p ,  size ,  stats ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // register for future purge in the purge mask
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    uint8_t *  start  =  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    size_t    full_size  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_commit_mask_t  mask ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_segment_commit_mask ( segment ,  true  /*conservative*/ ,  p ,  size ,  & start ,  & full_size ,  & mask ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( mi_commit_mask_is_empty ( & mask )  | |  full_size = = 0 )  return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // update delayed commit
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( segment - > purge_expire  >  0  | |  mi_commit_mask_is_empty ( & segment - > purge_mask ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_commit_mask_t  cmask ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_commit_mask_create_intersect ( & segment - > commit_mask ,  & mask ,  & cmask ) ;   // only purge what is committed; span_free may try to decommit more
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_commit_mask_set ( & segment - > purge_mask ,  & cmask ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_msecs_t  now  =  _mi_clock_now ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( segment - > purge_expire  = =  0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // no previous purgess, initialize now
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      segment - > purge_expire  =  now  +  mi_option_get ( mi_option_purge_delay ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else  if  ( segment - > purge_expire  < =  now )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // previous purge mask already expired
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  ( segment - > purge_expire  +  mi_option_get ( mi_option_purge_extend_delay )  < =  now )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        mi_segment_try_purge ( segment ,  true ,  stats ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        segment - > purge_expire  =  now  +  mi_option_get ( mi_option_purge_extend_delay ) ;  // (mi_option_get(mi_option_purge_delay) / 8); // wait a tiny bit longer in case there is a series of free's
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // previous purge mask is not yet expired, increase the expiration by a bit.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      segment - > purge_expire  + =  mi_option_get ( mi_option_purge_extend_delay ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_segment_try_purge ( mi_segment_t *  segment ,  bool  force ,  mi_stats_t *  stats )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( ! segment - > allow_purge  | |  mi_commit_mask_is_empty ( & segment - > purge_mask ) )  return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_msecs_t  now  =  _mi_clock_now ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( ! force  & &  now  <  segment - > purge_expire )  return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_commit_mask_t  mask  =  segment - > purge_mask ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > purge_expire  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_commit_mask_create_empty ( & segment - > purge_mask ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  idx ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_commit_mask_foreach ( & mask ,  idx ,  count )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // if found, decommit that sequence
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( count  >  0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      uint8_t *  p  =  ( uint8_t * ) segment  +  ( idx * MI_COMMIT_SIZE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      size_t  size  =  count  *  MI_COMMIT_SIZE ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_segment_purge ( segment ,  p ,  size ,  stats ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_commit_mask_foreach_end ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_commit_mask_is_empty ( & segment - > purge_mask ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								   Span  free 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  bool  mi_segment_is_abandoned ( mi_segment_t *  segment )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  ( segment - > thread_id  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// note: can be called on abandoned segments
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_segment_span_free ( mi_segment_t *  segment ,  size_t  slice_index ,  size_t  slice_count ,  bool  allow_purge ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice_index  <  segment - > slice_entries ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_span_queue_t *  sq  =  ( segment - > kind  = =  MI_SEGMENT_HUGE  | |  mi_segment_is_abandoned ( segment ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                          ?  NULL  :  mi_span_queue_for ( slice_count , tld ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( slice_count = = 0 )  slice_count  =  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice_index  +  slice_count  -  1  <  segment - > slice_entries ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // set first and last slice (the intermediates can be undetermined)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_slice_t *  slice  =  & segment - > slices [ slice_index ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  slice - > slice_count  =  ( uint32_t ) slice_count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice - > slice_count  = =  slice_count ) ;  // no overflow?
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  slice - > slice_offset  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( slice_count  >  1 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_slice_t *  last  =  & segment - > slices [ slice_index  +  slice_count  -  1 ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    last - > slice_count  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    last - > slice_offset  =  ( uint32_t ) ( sizeof ( mi_page_t ) * ( slice_count  -  1 ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    last - > xblock_size  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // perhaps decommit
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( allow_purge )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_segment_schedule_purge ( segment ,  mi_slice_start ( slice ) ,  slice_count  *  MI_SEGMENT_SLICE_SIZE ,  tld - > stats ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // and push it on the free page queue (if it was not a huge page)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( sq  ! =  NULL )  mi_span_queue_push (  sq ,  slice  ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								             else  slice - > xblock_size  =  0 ;  // mark huge page as free anyways
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/*
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// called from reclaim to add existing free spans
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_segment_span_add_free ( mi_slice_t *  slice ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment  =  _mi_ptr_segment ( slice ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice - > xblock_size = = 0  & &  slice - > slice_count > 0  & &  slice - > slice_offset = = 0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  slice_index  =  mi_slice_index ( slice ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_span_free ( segment , slice_index , slice - > slice_count , tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								*/ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_segment_span_remove_from_queue ( mi_slice_t *  slice ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice - > slice_count  >  0  & &  slice - > slice_offset = = 0  & &  slice - > xblock_size = = 0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( _mi_ptr_segment ( slice ) - > kind  ! =  MI_SEGMENT_HUGE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_span_queue_t *  sq  =  mi_span_queue_for ( slice - > slice_count ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_span_queue_delete ( sq ,  slice ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// note: can be called on abandoned segments
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  mi_slice_t *  mi_segment_span_free_coalesce ( mi_slice_t *  slice ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice  ! =  NULL  & &  slice - > slice_count  >  0  & &  slice - > slice_offset  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment  =  _mi_ptr_segment ( slice ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  bool  is_abandoned  =  mi_segment_is_abandoned ( segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // for huge pages, just mark as free but don't add to the queues
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( segment - > kind  = =  MI_SEGMENT_HUGE )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // issue #691: segment->used can be 0 if the huge page block was freed while abandoned (reclaim will get here in that case)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( ( segment - > used = = 0  & &  slice - > xblock_size = = 0 )  | |  segment - > used  = =  1 ) ;   // decreased right after this call in `mi_segment_page_clear`
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    slice - > xblock_size  =  0 ;   // mark as free anyways
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // we should mark the last slice `xblock_size=0` now to maintain invariants but we skip it to
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // avoid a possible cache miss (and the segment is about to be freed)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  slice ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // otherwise coalesce the span and add to the free span queues
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  slice_count  =  slice - > slice_count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_slice_t *  next  =  slice  +  slice - > slice_count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( next  < =  mi_segment_slices_end ( segment ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( next  <  mi_segment_slices_end ( segment )  & &  next - > xblock_size = = 0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // free next block -- remove it from free and merge
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( next - > slice_count  >  0  & &  next - > slice_offset = = 0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    slice_count  + =  next - > slice_count ;  // extend
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( ! is_abandoned )  {  mi_segment_span_remove_from_queue ( next ,  tld ) ;  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( slice  >  segment - > slices )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_slice_t *  prev  =  mi_slice_first ( slice  -  1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( prev  > =  segment - > slices ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( prev - > xblock_size = = 0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // free previous slice -- remove it from free and merge
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_assert_internal ( prev - > slice_count  >  0  & &  prev - > slice_offset = = 0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      slice_count  + =  prev - > slice_count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  ( ! is_abandoned )  {  mi_segment_span_remove_from_queue ( prev ,  tld ) ;  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      slice  =  prev ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // and add the new free page
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_span_free ( segment ,  mi_slice_index ( slice ) ,  slice_count ,  true ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  slice ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								   Page  allocation 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// Note: may still return NULL if committing the memory failed
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  mi_page_t *  mi_segment_span_allocate ( mi_segment_t *  segment ,  size_t  slice_index ,  size_t  slice_count ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice_index  <  segment - > slice_entries ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_slice_t *  const  slice  =  & segment - > slices [ slice_index ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice - > xblock_size = = 0  | |  slice - > xblock_size = = 1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // commit before changing the slice data
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( ! mi_segment_ensure_committed ( segment ,  _mi_segment_page_start_from_slice ( segment ,  slice ,  0 ,  NULL ) ,  slice_count  *  MI_SEGMENT_SLICE_SIZE ,  tld - > stats ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  NULL ;   // commit failed!
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // convert the slices to a page
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  slice - > slice_offset  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  slice - > slice_count  =  ( uint32_t ) slice_count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice - > slice_count  = =  slice_count ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  size_t  bsize  =  slice_count  *  MI_SEGMENT_SLICE_SIZE ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  slice - > xblock_size  =  ( uint32_t ) ( bsize  > =  MI_HUGE_BLOCK_SIZE  ?  MI_HUGE_BLOCK_SIZE  :  bsize ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_page_t *   page  =  mi_slice_to_page ( slice ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_page_block_size ( page )  = =  bsize ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // set slice back pointers for the first MI_MAX_SLICE_OFFSET entries
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  extra  =  slice_count - 1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( extra  >  MI_MAX_SLICE_OFFSET )  extra  =  MI_MAX_SLICE_OFFSET ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-08-28 19:41:04 +08:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  if  ( slice_index  +  extra  > =  segment - > slice_entries )  extra  =  segment - > slice_entries  -  slice_index  -  1 ;   // huge objects may have more slices than available entries in the segment->slices
 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_slice_t *  slice_next  =  slice  +  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  for  ( size_t  i  =  1 ;  i  < =  extra ;  i + + ,  slice_next + + )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    slice_next - > slice_offset  =  ( uint32_t ) ( sizeof ( mi_slice_t ) * i ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    slice_next - > slice_count  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    slice_next - > xblock_size  =  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // and also for the last one (if not set already) (the last one is needed for coalescing and for large alignments)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // note: the cast is needed for ubsan since the index can be larger than MI_SLICES_PER_SEGMENT for huge allocations (see #543)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_slice_t *  last  =  slice  +  slice_count  -  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_slice_t *  end  =  ( mi_slice_t * ) mi_segment_slices_end ( segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( last  >  end )  last  =  end ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( last  >  slice )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    last - > slice_offset  =  ( uint32_t ) ( sizeof ( mi_slice_t )  *  ( last  -  slice ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    last - > slice_count  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    last - > xblock_size  =  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // and initialize the page
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  page - > is_committed  =  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > used + + ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  page ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_segment_slice_split ( mi_segment_t *  segment ,  mi_slice_t *  slice ,  size_t  slice_count ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( _mi_ptr_segment ( slice )  = =  segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice - > slice_count  > =  slice_count ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice - > xblock_size  >  0 ) ;  // no more in free queue
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( slice - > slice_count  < =  slice_count )  return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > kind  ! =  MI_SEGMENT_HUGE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  next_index  =  mi_slice_index ( slice )  +  slice_count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  next_count  =  slice - > slice_count  -  slice_count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_span_free ( segment ,  next_index ,  next_count ,  false  /* don't purge left-over part */ ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  slice - > slice_count  =  ( uint32_t ) slice_count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  mi_page_t *  mi_segments_page_find_and_allocate ( size_t  slice_count ,  mi_arena_id_t  req_arena_id ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice_count * MI_SEGMENT_SLICE_SIZE  < =  MI_LARGE_OBJ_SIZE_MAX ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // search from best fit up
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_span_queue_t *  sq  =  mi_span_queue_for ( slice_count ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( slice_count  = =  0 )  slice_count  =  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  while  ( sq  < =  & tld - > spans [ MI_SEGMENT_BIN_MAX ] )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    for  ( mi_slice_t *  slice  =  sq - > first ;  slice  ! =  NULL ;  slice  =  slice - > next )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  ( slice - > slice_count  > =  slice_count )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        // found one
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        mi_segment_t *  segment  =  _mi_ptr_segment ( slice ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ( _mi_arena_memid_is_suitable ( segment - > memid ,  req_arena_id ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          // found a suitable page span
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          mi_span_queue_delete ( sq ,  slice ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          if  ( slice - > slice_count  >  slice_count )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            mi_segment_slice_split ( segment ,  slice ,  slice_count ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          mi_assert_internal ( slice  ! =  NULL  & &  slice - > slice_count  = =  slice_count  & &  slice - > xblock_size  >  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          mi_page_t *  page  =  mi_segment_span_allocate ( segment ,  mi_slice_index ( slice ) ,  slice - > slice_count ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          if  ( page  = =  NULL )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            // commit failed; return NULL but first restore the slice
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            mi_segment_span_free_coalesce ( slice ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          return  page ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    sq + + ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // could not find a page..
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								   Segment  allocation 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  mi_segment_t *  mi_segment_os_alloc (  size_t  required ,  size_t  page_alignment ,  bool  eager_delayed ,  mi_arena_id_t  req_arena_id , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                                          size_t *  psegment_slices ,  size_t *  ppre_size ,  size_t *  pinfo_slices , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                                          bool  commit ,  mi_segments_tld_t *  tld ,  mi_os_tld_t *  os_tld ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_memid_t  memid ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  bool    allow_large  =  ( ! eager_delayed  & &  ( MI_SECURE  = =  0 ) ) ;  // only allow large OS pages once we are no longer lazy
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  align_offset  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  alignment  =  MI_SEGMENT_ALIGN ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( page_alignment  >  0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // mi_assert_internal(huge_page != NULL);
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( page_alignment  > =  MI_SEGMENT_ALIGN ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    alignment  =  page_alignment ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    const  size_t  info_size  =  ( * pinfo_slices )  *  MI_SEGMENT_SLICE_SIZE ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    align_offset  =  _mi_align_up (  info_size ,  MI_SEGMENT_ALIGN  ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    const  size_t  extra  =  align_offset  -  info_size ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // recalculate due to potential guard pages
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    * psegment_slices  =  mi_segment_calculate_slices ( required  +  extra ,  ppre_size ,  pinfo_slices ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-04-16 22:34:12 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // mi_page_t.slice_count type is uint32_t
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( * psegment_slices  >  ( size_t ) UINT32_MAX )  return  NULL ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  size_t  segment_size  =  ( * psegment_slices )  *  MI_SEGMENT_SLICE_SIZE ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment  =  ( mi_segment_t * ) _mi_arena_alloc_aligned ( segment_size ,  alignment ,  align_offset ,  commit ,  allow_large ,  req_arena_id ,  & memid ,  os_tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( segment  = =  NULL )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  NULL ;   // failed to allocate
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // ensure metadata part of the segment is committed
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_commit_mask_t  commit_mask ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( memid . initially_committed )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_commit_mask_create_full ( & commit_mask ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // at least commit the info slices
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    const  size_t  commit_needed  =  _mi_divide_up ( ( * pinfo_slices ) * MI_SEGMENT_SLICE_SIZE ,  MI_COMMIT_SIZE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( commit_needed > 0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_commit_mask_create ( 0 ,  commit_needed ,  & commit_mask ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( commit_needed * MI_COMMIT_SIZE  > =  ( * pinfo_slices ) * MI_SEGMENT_SLICE_SIZE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( ! _mi_os_commit ( segment ,  commit_needed * MI_COMMIT_SIZE ,  NULL ,  tld - > stats ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      _mi_arena_free ( segment , segment_size , 0 , memid , tld - > stats ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      return  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment  ! =  NULL  & &  ( uintptr_t ) segment  %  MI_SEGMENT_SIZE  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > memid  =  memid ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > allow_decommit  =  ! memid . is_pinned ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > allow_purge  =  segment - > allow_decommit  & &  ( mi_option_get ( mi_option_purge_delay )  > =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > segment_size  =  segment_size ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > commit_mask  =  commit_mask ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > purge_expire  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_commit_mask_create_empty ( & segment - > purge_mask ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_atomic_store_ptr_release ( mi_segment_t ,  & segment - > abandoned_next ,  NULL ) ;   // tsan
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segments_track_size ( ( long ) ( segment_size ) ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  _mi_segment_map_allocated_at ( segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  segment ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// Allocate a segment from the OS aligned to `MI_SEGMENT_SIZE` .
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  mi_segment_t *  mi_segment_alloc ( size_t  required ,  size_t  page_alignment ,  mi_arena_id_t  req_arena_id ,  mi_segments_tld_t *  tld ,  mi_os_tld_t *  os_tld ,  mi_page_t * *  huge_page ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( ( required = = 0  & &  huge_page = = NULL )  | |  ( required > 0  & &  huge_page  ! =  NULL ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // calculate needed sizes first
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  info_slices ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  pre_size ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  segment_slices  =  mi_segment_calculate_slices ( required ,  & pre_size ,  & info_slices ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-04-16 22:34:12 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  // mi_page_t.slice_count type is uint32_t
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( segment_slices  >  ( size_t ) UINT32_MAX )  return  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  // Commit eagerly only if not the first N lazy segments (to reduce impact of many threads that allocate just a little)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  bool  eager_delay  =  ( // !_mi_os_has_overcommit() &&             // never delay on overcommit systems
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                            _mi_current_thread_count ( )  >  1  & &        // do not delay for the first N threads
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                            tld - > count  <  ( size_t ) mi_option_get ( mi_option_eager_commit_delay ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  bool  eager  =  ! eager_delay  & &  mi_option_is_enabled ( mi_option_eager_commit ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  bool  commit  =  eager  | |  ( required  >  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // Allocate the segment from the OS
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment  =  mi_segment_os_alloc ( required ,  page_alignment ,  eager_delay ,  req_arena_id , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                                              & segment_slices ,  & pre_size ,  & info_slices ,  commit ,  tld ,  os_tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( segment  = =  NULL )  return  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // zero the segment info? -- not always needed as it may be zero initialized from the OS
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( ! segment - > memid . initially_zero )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    ptrdiff_t  ofs     =  offsetof ( mi_segment_t ,  next ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    size_t     prefix  =  offsetof ( mi_segment_t ,  slices )  -  ofs ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    size_t     zsize   =  prefix  +  ( sizeof ( mi_slice_t )  *  ( segment_slices  +  1 ) ) ;  // one more
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    _mi_memzero ( ( uint8_t * ) segment  +  ofs ,  zsize ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // initialize the rest of the segment info
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  size_t  slice_entries  =  ( segment_slices  >  MI_SLICES_PER_SEGMENT  ?  MI_SLICES_PER_SEGMENT  :  segment_slices ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > segment_slices  =  segment_slices ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > segment_info_slices  =  info_slices ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > thread_id  =  _mi_thread_id ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > cookie  =  _mi_ptr_cookie ( segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > slice_entries  =  slice_entries ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > kind  =  ( required  = =  0  ?  MI_SEGMENT_NORMAL  :  MI_SEGMENT_HUGE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // _mi_memzero(segment->slices, sizeof(mi_slice_t)*(info_slices+1));
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  _mi_stat_increase ( & tld - > stats - > page_committed ,  mi_segment_info_size ( segment ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // set up guard pages
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  guard_slices  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( MI_SECURE > 0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // in secure mode, we set up a protected page in between the segment info
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // and the page data, and at the end of the segment.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    size_t  os_pagesize  =  _mi_os_page_size ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( mi_segment_info_size ( segment )  -  os_pagesize  > =  pre_size ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    _mi_os_protect ( ( uint8_t * ) segment  +  mi_segment_info_size ( segment )  -  os_pagesize ,  os_pagesize ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    uint8_t *  end  =  ( uint8_t * ) segment  +  mi_segment_size ( segment )  -  os_pagesize ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_segment_ensure_committed ( segment ,  end ,  os_pagesize ,  tld - > stats ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    _mi_os_protect ( end ,  os_pagesize ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( slice_entries  = =  segment_slices )  segment - > slice_entries - - ;  // don't use the last slice :-(
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    guard_slices  =  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // reserve first slices for segment info
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_page_t *  page0  =  mi_segment_span_allocate ( segment ,  0 ,  info_slices ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( page0 ! = NULL ) ;  if  ( page0 = = NULL )  return  NULL ;  // cannot fail as we always commit in advance
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > used  = =  1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > used  =  0 ;  // don't count our internal slices towards usage
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // initialize initial free pages
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( segment - > kind  = =  MI_SEGMENT_NORMAL )  {  // not a huge page
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( huge_page = = NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_segment_span_free ( segment ,  info_slices ,  segment - > slice_entries  -  info_slices ,  false  /* don't purge */ ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( huge_page ! = NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( mi_commit_mask_is_empty ( & segment - > purge_mask ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( mi_commit_mask_is_full ( & segment - > commit_mask ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    * huge_page  =  mi_segment_span_allocate ( segment ,  info_slices ,  segment_slices  -  info_slices  -  guard_slices ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( * huge_page  ! =  NULL ) ;  // cannot fail as we commit in advance
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_expensive ( mi_segment_is_valid ( segment , tld ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  segment ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_segment_free ( mi_segment_t *  segment ,  bool  force ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  MI_UNUSED ( force ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment  ! =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > next  = =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > used  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // Remove the free pages
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_slice_t *  slice  =  & segment - > slices [ 0 ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  mi_slice_t *  end  =  mi_segment_slices_end ( segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  # if MI_DEBUG>1 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  page_count  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  # endif 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  while  ( slice  <  end )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( slice - > slice_count  >  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( slice - > slice_offset  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( mi_slice_index ( slice ) = = 0  | |  slice - > xblock_size  = =  0 ) ;  // no more used pages ..
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( slice - > xblock_size  = =  0  & &  segment - > kind  ! =  MI_SEGMENT_HUGE )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_segment_span_remove_from_queue ( slice ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # if MI_DEBUG>1 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    page_count + + ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # endif 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    slice  =  slice  +  slice - > slice_count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( page_count  = =  2 ) ;  // first page is allocated by the segment itself
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // stats
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  _mi_stat_decrease ( & tld - > stats - > page_committed ,  mi_segment_info_size ( segment ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // return it to the OS
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_os_free ( segment ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								   Page  Free 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_segment_abandon ( mi_segment_t *  segment ,  mi_segments_tld_t *  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// note: can be called on abandoned pages
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  mi_slice_t *  mi_segment_page_clear ( mi_page_t *  page ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( page - > xblock_size  >  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_page_all_free ( page ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment  =  _mi_ptr_segment ( page ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > used  >  0 ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-03-06 09:42:11 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								# ifdef Py_GIL_DISABLED 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( page - > qsbr_goal  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( page - > qsbr_node . next  = =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# endif 
 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  inuse  =  page - > capacity  *  mi_page_block_size ( page ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  _mi_stat_decrease ( & tld - > stats - > page_committed ,  inuse ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  _mi_stat_decrease ( & tld - > stats - > pages ,  1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // reset the page memory to reduce memory pressure?
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( segment - > allow_decommit  & &  mi_option_is_enabled ( mi_option_deprecated_page_reset ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    size_t  psize ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    uint8_t *  start  =  _mi_page_start ( segment ,  page ,  & psize ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    _mi_os_reset ( start ,  psize ,  tld - > stats ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // zero the page data, but not the segment fields
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  page - > is_zero_init  =  false ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  ptrdiff_t  ofs  =  offsetof ( mi_page_t ,  capacity ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  _mi_memzero ( ( uint8_t * ) page  +  ofs ,  sizeof ( * page )  -  ofs ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  page - > xblock_size  =  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // and free it
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_slice_t *  slice  =  mi_segment_span_free_coalesce ( mi_page_to_slice ( page ) ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > used - - ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // cannot assert segment valid as it is called during reclaim
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // mi_assert_expensive(mi_segment_is_valid(segment, tld));
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  slice ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								void  _mi_segment_page_free ( mi_page_t *  page ,  bool  force ,  mi_segments_tld_t *  tld ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert ( page  ! =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment  =  _mi_page_segment ( page ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_expensive ( mi_segment_is_valid ( segment , tld ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // mark it as free now
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_page_clear ( page ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_expensive ( mi_segment_is_valid ( segment ,  tld ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( segment - > used  = =  0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // no more used pages; remove from the free list and free the segment
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_segment_free ( segment ,  force ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  if  ( segment - > used  = =  segment - > abandoned )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // only abandoned pages; remove from free list and abandon
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_segment_abandon ( segment , tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								Abandonment 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								When  threads  terminate ,  they  can  leave  segments  with 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								live  blocks  ( reachable  through  other  threads ) .  Such  segments 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								are  " abandoned "  and  will  be  reclaimed  by  other  threads  to 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								reuse  their  pages  and / or  free  them  eventually 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								We  maintain  a  global  list  of  abandoned  segments  that  are 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								reclaimed  on  demand .  Since  this  is  shared  among  threads 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								the  implementation  needs  to  avoid  the  A - B - A  problem  on 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								popping  abandoned  segments :  < https : //en.wikipedia.org/wiki/ABA_problem>
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								We  use  tagged  pointers  to  avoid  accidentally  identifying 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								reused  segments ,  much  like  stamped  references  in  Java . 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								Secondly ,  we  maintain  a  reader  counter  to  avoid  resetting 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								or  decommitting  segments  that  have  a  pending  read  operation . 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								Note :  the  current  implementation  is  one  possible  design ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								another  way  might  be  to  keep  track  of  abandoned  segments 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								in  the  arenas / segment_cache ' s .  This  would  have  the  advantage  of  keeping 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								all  concurrent  code  in  one  place  and  not  needing  to  deal 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								with  ABA  issues .  The  drawback  is  that  it  is  unclear  how  to 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								scan  abandoned  segments  efficiently  in  that  case  as  they 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								would  be  spread  among  all  other  segments  in  the  arenas . 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// Use the bottom 20-bits (on 64-bit) of the aligned segment pointers
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// to put in a tag that increments on update to avoid the A-B-A problem.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# define MI_TAGGED_MASK   MI_SEGMENT_MASK 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  mi_segment_t *  mi_tagged_segment_ptr ( mi_tagged_segment_t  ts )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  ( mi_segment_t * ) ( ts  &  ~ MI_TAGGED_MASK ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  mi_tagged_segment_t  mi_tagged_segment ( mi_segment_t *  segment ,  mi_tagged_segment_t  ts )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( ( ( uintptr_t ) segment  &  MI_TAGGED_MASK )  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  uintptr_t  tag  =  ( ( ts  &  MI_TAGGED_MASK )  +  1 )  &  MI_TAGGED_MASK ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  ( ( uintptr_t ) segment  |  tag ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								mi_abandoned_pool_t  _mi_abandoned_default ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// Push on the visited list
 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								static  void  mi_abandoned_visited_push ( mi_abandoned_pool_t  * pool ,  mi_segment_t *  segment )  { 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > thread_id  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_atomic_load_ptr_relaxed ( mi_segment_t , & segment - > abandoned_next )  = =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > next  = =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > used  >  0 ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  mi_segment_t *  anext  =  mi_atomic_load_ptr_relaxed ( mi_segment_t ,  & pool - > abandoned_visited ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  do  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_atomic_store_ptr_release ( mi_segment_t ,  & segment - > abandoned_next ,  anext ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  }  while  ( ! mi_atomic_cas_ptr_weak_release ( mi_segment_t ,  & pool - > abandoned_visited ,  & anext ,  segment ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_atomic_increment_relaxed ( & pool - > abandoned_visited_count ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// Move the visited list to the abandoned list.
 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								static  bool  mi_abandoned_visited_revisit ( mi_abandoned_pool_t  * pool ) 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // quick check if the visited list is empty
 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  if  ( mi_atomic_load_ptr_relaxed ( mi_segment_t ,  & pool - > abandoned_visited )  = =  NULL )  return  false ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // grab the whole visited list
 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  mi_segment_t *  first  =  mi_atomic_exchange_ptr_acq_rel ( mi_segment_t ,  & pool - > abandoned_visited ,  NULL ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  if  ( first  = =  NULL )  return  false ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // first try to swap directly if the abandoned list happens to be NULL
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_tagged_segment_t  afirst ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  mi_tagged_segment_t  ts  =  mi_atomic_load_relaxed ( & pool - > abandoned ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  if  ( mi_tagged_segment_ptr ( ts ) = = NULL )  { 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    size_t  count  =  mi_atomic_load_relaxed ( & pool - > abandoned_visited_count ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								    afirst  =  mi_tagged_segment ( first ,  ts ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( mi_atomic_cas_strong_acq_rel ( & pool - > abandoned ,  & ts ,  afirst ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_atomic_add_relaxed ( & pool - > abandoned_count ,  count ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_atomic_sub_relaxed ( & pool - > abandoned_visited_count ,  count ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								      return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // find the last element of the visited list: O(n)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  last  =  first ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  next ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  while  ( ( next  =  mi_atomic_load_ptr_relaxed ( mi_segment_t ,  & last - > abandoned_next ) )  ! =  NULL )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    last  =  next ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // and atomically prepend to the abandoned list
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // (no need to increase the readers as we don't access the abandoned segments)
 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  mi_tagged_segment_t  anext  =  mi_atomic_load_relaxed ( & pool - > abandoned ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  size_t  count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  do  { 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    count  =  mi_atomic_load_relaxed ( & pool - > abandoned_visited_count ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								    mi_atomic_store_ptr_release ( mi_segment_t ,  & last - > abandoned_next ,  mi_tagged_segment_ptr ( anext ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    afirst  =  mi_tagged_segment ( first ,  anext ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  }  while  ( ! mi_atomic_cas_weak_release ( & pool - > abandoned ,  & anext ,  afirst ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_atomic_add_relaxed ( & pool - > abandoned_count ,  count ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_atomic_sub_relaxed ( & pool - > abandoned_visited_count ,  count ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// Push on the abandoned list.
 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								static  void  mi_abandoned_push ( mi_abandoned_pool_t *  pool ,  mi_segment_t *  segment )  { 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > thread_id  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_atomic_load_ptr_relaxed ( mi_segment_t ,  & segment - > abandoned_next )  = =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > next  = =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > used  >  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_tagged_segment_t  next ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  mi_tagged_segment_t  ts  =  mi_atomic_load_relaxed ( & pool - > abandoned ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  do  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_atomic_store_ptr_release ( mi_segment_t ,  & segment - > abandoned_next ,  mi_tagged_segment_ptr ( ts ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    next  =  mi_tagged_segment ( segment ,  ts ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  }  while  ( ! mi_atomic_cas_weak_release ( & pool - > abandoned ,  & ts ,  next ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_atomic_increment_relaxed ( & pool - > abandoned_count ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// Wait until there are no more pending reads on segments that used to be in the abandoned list
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// called for example from `arena.c` before decommitting
 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								void  _mi_abandoned_await_readers ( mi_abandoned_pool_t *  pool )  { 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  size_t  n ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  do  { 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    n  =  mi_atomic_load_acquire ( & pool - > abandoned_readers ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								    if  ( n  ! =  0 )  mi_atomic_yield ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  }  while  ( n  ! =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// Pop from the abandoned list
 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								static  mi_segment_t *  mi_abandoned_pop ( mi_abandoned_pool_t *  pool )  { 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // Check efficiently if it is empty (or if the visited list needs to be moved)
 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  mi_tagged_segment_t  ts  =  mi_atomic_load_relaxed ( & pool - > abandoned ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  segment  =  mi_tagged_segment_ptr ( ts ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  mi_likely ( segment  = =  NULL )  { 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  mi_likely ( ! mi_abandoned_visited_revisit ( pool ) )  {  // try to swap in the visited list on NULL
 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								      return  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // Do a pop. We use a reader count to prevent
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // a segment to be decommitted while a read is still pending,
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // and a tagged pointer to prevent A-B-A link corruption.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // (this is called from `region.c:_mi_mem_free` for example)
 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  mi_atomic_increment_relaxed ( & pool - > abandoned_readers ) ;   // ensure no segment gets decommitted
 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  mi_tagged_segment_t  next  =  0 ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  ts  =  mi_atomic_load_acquire ( & pool - > abandoned ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  do  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    segment  =  mi_tagged_segment_ptr ( ts ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( segment  ! =  NULL )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_segment_t *  anext  =  mi_atomic_load_ptr_relaxed ( mi_segment_t ,  & segment - > abandoned_next ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      next  =  mi_tagged_segment ( anext ,  ts ) ;  // note: reads the segment's `abandoned_next` field so should not be decommitted
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  }  while  ( segment  ! =  NULL  & &  ! mi_atomic_cas_weak_acq_rel ( & pool - > abandoned ,  & ts ,  next ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_atomic_decrement_relaxed ( & pool - > abandoned_readers ) ;   // release reader lock
 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  if  ( segment  ! =  NULL )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_atomic_store_ptr_release ( mi_segment_t ,  & segment - > abandoned_next ,  NULL ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    mi_atomic_decrement_relaxed ( & pool - > abandoned_count ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  segment ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								   Abandon  segment / page 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  void  mi_segment_abandon ( mi_segment_t *  segment ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > used  = =  segment - > abandoned ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > used  >  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_atomic_load_ptr_relaxed ( mi_segment_t ,  & segment - > abandoned_next )  = =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > abandoned_visits  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_expensive ( mi_segment_is_valid ( segment , tld ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // remove the free pages from the free page queues
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_slice_t *  slice  =  & segment - > slices [ 0 ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  mi_slice_t *  end  =  mi_segment_slices_end ( segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  while  ( slice  <  end )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( slice - > slice_count  >  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( slice - > slice_offset  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( slice - > xblock_size  = =  0 )  {  // a free page
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_segment_span_remove_from_queue ( slice , tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      slice - > xblock_size  =  0 ;  // but keep it free
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    slice  =  slice  +  slice - > slice_count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // perform delayed decommits (forcing is much slower on mstress)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_try_purge ( segment ,  mi_option_is_enabled ( mi_option_abandoned_page_purge )  /* force? */ ,  tld - > stats ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // all pages in the segment are abandoned; add it to the abandoned list
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  _mi_stat_increase ( & tld - > stats - > segments_abandoned ,  1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segments_track_size ( - ( ( long ) mi_segment_size ( segment ) ) ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > thread_id  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_atomic_store_ptr_release ( mi_segment_t ,  & segment - > abandoned_next ,  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > abandoned_visits  =  1 ;    // from 0 to 1 to signify it is abandoned
 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  mi_abandoned_push ( tld - > abandoned ,  segment ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								void  _mi_segment_page_abandon ( mi_page_t *  page ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert ( page  ! =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_page_thread_free_flag ( page ) = = MI_NEVER_DELAYED_FREE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_page_heap ( page )  = =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment  =  _mi_page_segment ( page ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_expensive ( mi_segment_is_valid ( segment , tld ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > abandoned + + ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  _mi_stat_increase ( & tld - > stats - > pages_abandoned ,  1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > abandoned  < =  segment - > used ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( segment - > used  = =  segment - > abandoned )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // all pages are abandoned, abandon the entire segment
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_segment_abandon ( segment ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  Reclaim  abandoned  pages 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  mi_slice_t *  mi_slices_start_iterate ( mi_segment_t *  segment ,  const  mi_slice_t * *  end )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_slice_t *  slice  =  & segment - > slices [ 0 ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  * end  =  mi_segment_slices_end ( segment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slice - > slice_count > 0  & &  slice - > xblock_size > 0 ) ;  // segment allocated page
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  slice  =  slice  +  slice - > slice_count ;  // skip the first segment allocated page
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  slice ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// Possibly free pages and check if free space is available
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  bool  mi_segment_check_free ( mi_segment_t *  segment ,  size_t  slices_needed ,  size_t  block_size ,  mi_segments_tld_t *  tld ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( block_size  <  MI_HUGE_BLOCK_SIZE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_segment_is_abandoned ( segment ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  bool  has_page  =  false ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // for all slices
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  mi_slice_t *  end ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_slice_t *  slice  =  mi_slices_start_iterate ( segment ,  & end ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  while  ( slice  <  end )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( slice - > slice_count  >  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( slice - > slice_offset  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( mi_slice_is_used ( slice ) )  {  // used page
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // ensure used count is up to date and collect potential concurrent frees
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_page_t *  const  page  =  mi_slice_to_page ( slice ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      _mi_page_free_collect ( page ,  false ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-03-06 09:42:11 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      if  ( mi_page_all_free ( page )  & &  _PyMem_mi_page_is_safe_to_free ( page ) )  { 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								        // if this page is all free now, free it without adding to any queues (yet)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        mi_assert_internal ( page - > next  = =  NULL  & &  page - > prev = = NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        _mi_stat_decrease ( & tld - > stats - > pages_abandoned ,  1 ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-03-06 09:42:11 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								# ifdef Py_GIL_DISABLED 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        page - > qsbr_goal  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# endif 
 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								        segment - > abandoned - - ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        slice  =  mi_segment_page_clear ( page ,  tld ) ;  // re-assign slice due to coalesce!
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        mi_assert_internal ( ! mi_slice_is_used ( slice ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ( slice - > slice_count  > =  slices_needed )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          has_page  =  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ( page - > xblock_size  = =  block_size  & &  mi_page_has_any_available ( page ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          // a page has available free blocks of the right size
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          has_page  =  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // empty span
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  ( slice - > slice_count  > =  slices_needed )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        has_page  =  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    slice  =  slice  +  slice - > slice_count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  has_page ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-01-05 15:08:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								static  mi_heap_t *  mi_heap_by_tag ( mi_heap_t *  heap ,  uint8_t  tag )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( heap - > tag  = =  tag )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  heap ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  for  ( mi_heap_t  * curr  =  heap - > tld - > heaps ;  curr  ! =  NULL ;  curr  =  curr - > next )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( curr - > tag  = =  tag )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      return  curr ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								// Reclaim an abandoned segment; returns NULL if the segment was freed
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// set `right_page_reclaimed` to `true` if it reclaimed a page of the right `block_size` that was not full.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  mi_segment_t *  mi_segment_reclaim ( mi_segment_t *  segment ,  mi_heap_t *  heap ,  size_t  requested_block_size ,  bool *  right_page_reclaimed ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_atomic_load_ptr_relaxed ( mi_segment_t ,  & segment - > abandoned_next )  = =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_expensive ( mi_segment_is_valid ( segment ,  tld ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( right_page_reclaimed  ! =  NULL )  {  * right_page_reclaimed  =  false ;  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > thread_id  =  _mi_thread_id ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > abandoned_visits  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segments_track_size ( ( long ) mi_segment_size ( segment ) ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > next  = =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  _mi_stat_decrease ( & tld - > stats - > segments_abandoned ,  1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // for all slices
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  mi_slice_t *  end ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_slice_t *  slice  =  mi_slices_start_iterate ( segment ,  & end ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  while  ( slice  <  end )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( slice - > slice_count  >  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( slice - > slice_offset  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( mi_slice_is_used ( slice ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // in use: reclaim the page in our heap
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_page_t *  page  =  mi_slice_to_page ( slice ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-05 15:08:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      mi_heap_t *  target_heap  =  mi_heap_by_tag ( heap ,  page - > tag ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								      mi_assert_internal ( page - > is_committed ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_assert_internal ( mi_page_thread_free_flag ( page ) = = MI_NEVER_DELAYED_FREE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_assert_internal ( mi_page_heap ( page )  = =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_assert_internal ( page - > next  = =  NULL  & &  page - > prev = = NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      _mi_stat_decrease ( & tld - > stats - > pages_abandoned ,  1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      segment - > abandoned - - ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // set the heap again and allow delayed free again
 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-05 15:08:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      mi_page_set_heap ( page ,  target_heap ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								      _mi_page_use_delayed_free ( page ,  MI_USE_DELAYED_FREE ,  true ) ;  // override never (after heap is set)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      _mi_page_free_collect ( page ,  false ) ;  // ensure used count is up to date
 
							 
						 
					
						
							
								
									
										
										
										
											2024-03-06 09:42:11 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      if  ( mi_page_all_free ( page )  & &  _PyMem_mi_page_is_safe_to_free ( page ) )  { 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								        // if everything free by now, free the page
 
							 
						 
					
						
							
								
									
										
										
										
											2024-03-06 09:42:11 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								# ifdef Py_GIL_DISABLED 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        page - > qsbr_goal  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# endif 
 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								        slice  =  mi_segment_page_clear ( page ,  tld ) ;    // set slice again due to coalesceing
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        // otherwise reclaim it into the heap
 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-05 15:08:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        _mi_page_reclaim ( target_heap ,  page ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ( requested_block_size  = =  page - > xblock_size  & &  mi_page_has_any_available ( page )  & & 
							 
						 
					
						
							
								
									
										
										
										
											2024-03-06 09:42:11 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								            requested_block_size  < =  MI_MEDIUM_OBJ_SIZE_MAX  & &  heap  = =  target_heap )  { 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								          if  ( right_page_reclaimed  ! =  NULL )  {  * right_page_reclaimed  =  true ;  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // the span is free, add it to our page queues
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      slice  =  mi_segment_span_free_coalesce ( slice ,  tld ) ;  // set slice again due to coalesceing
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( slice - > slice_count > 0  & &  slice - > slice_offset = = 0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    slice  =  slice  +  slice - > slice_count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert ( segment - > abandoned  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( segment - > used  = =  0 )  {   // due to page_clear
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( right_page_reclaimed  = =  NULL  | |  ! ( * right_page_reclaimed ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_segment_free ( segment ,  false ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  segment ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								void  _mi_abandoned_reclaim_all ( mi_heap_t *  heap ,  mi_segments_tld_t *  tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  while  ( ( segment  =  mi_abandoned_pop ( tld - > abandoned ) )  ! =  NULL )  { 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								    mi_segment_reclaim ( segment ,  heap ,  0 ,  NULL ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  mi_segment_t *  mi_segment_try_reclaim ( mi_heap_t *  heap ,  size_t  needed_slices ,  size_t  block_size ,  bool *  reclaimed ,  mi_segments_tld_t *  tld ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  * reclaimed  =  false ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  long  max_tries  =  mi_option_get_clamp ( mi_option_max_segment_reclaim ,  8 ,  1024 ) ;      // limit the work to bound allocation times
 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  while  ( ( max_tries - -  >  0 )  & &  ( ( segment  =  mi_abandoned_pop ( tld - > abandoned ) )  ! =  NULL ) )  { 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								    segment - > abandoned_visits + + ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // todo: an arena exclusive heap will potentially visit many abandoned unsuitable segments
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // and push them into the visited list and use many tries. Perhaps we can skip non-suitable ones in a better way?
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    bool  is_suitable  =  _mi_heap_memid_is_suitable ( heap ,  segment - > memid ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    bool  has_page  =  mi_segment_check_free ( segment , needed_slices , block_size , tld ) ;  // try to free up pages (due to concurrent frees)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( segment - > used  = =  0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // free the segment (by forced reclaim) to make it available to other threads.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // note1: we prefer to free a segment as that might lead to reclaiming another
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // segment that is still partially used.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // note2: we could in principle optimize this by skipping reclaim and directly
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // freeing but that would violate some invariants temporarily)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_segment_reclaim ( segment ,  heap ,  0 ,  NULL ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else  if  ( has_page  & &  is_suitable )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // found a large enough free span, or a page of the right block_size with free space
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // we return the result of reclaim (which is usually `segment`) as it might free
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // the segment due to concurrent frees (in which case `NULL` is returned).
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      return  mi_segment_reclaim ( segment ,  heap ,  block_size ,  reclaimed ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else  if  ( segment - > abandoned_visits  >  3  & &  is_suitable )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // always reclaim on 3rd visit to limit the abandoned queue length.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_segment_reclaim ( segment ,  heap ,  0 ,  NULL ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // otherwise, push on the visited list so it gets not looked at too quickly again
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_segment_try_purge ( segment ,  true  /* force? */ ,  tld - > stats ) ;  // force purge if needed as we may not visit soon again
 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      mi_abandoned_visited_push ( tld - > abandoned ,  segment ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								void  _mi_abandoned_collect ( mi_heap_t *  heap ,  bool  force ,  mi_segments_tld_t *  tld ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  mi_abandoned_pool_t *  pool  =  tld - > abandoned ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  int  max_tries  =  ( force  ?  16 * 1024  :  1024 ) ;  // limit latency
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( force )  { 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    mi_abandoned_visited_revisit ( pool ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  while  ( ( max_tries - -  >  0 )  & &  ( ( segment  =  mi_abandoned_pop ( pool ) )  ! =  NULL ) )  { 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								    mi_segment_check_free ( segment , 0 , 0 , tld ) ;  // try to free up pages (due to concurrent frees)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( segment - > used  = =  0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // free the segment (by forced reclaim) to make it available to other threads.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // note: we could in principle optimize this by skipping reclaim and directly
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // freeing but that would violate some invariants temporarily)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_segment_reclaim ( segment ,  heap ,  0 ,  NULL ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // otherwise, purge if needed and push on the visited list
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // note: forced purge can be expensive if many threads are destroyed/created as in mstress.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_segment_try_purge ( segment ,  force ,  tld - > stats ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-04 17:21:40 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      mi_abandoned_visited_push ( pool ,  segment ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-10-30 08:43:11 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								   Reclaim  or  allocate 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  mi_segment_t *  mi_segment_reclaim_or_alloc ( mi_heap_t *  heap ,  size_t  needed_slices ,  size_t  block_size ,  mi_segments_tld_t *  tld ,  mi_os_tld_t *  os_tld ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( block_size  <  MI_HUGE_BLOCK_SIZE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( block_size  < =  MI_LARGE_OBJ_SIZE_MAX ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // 1. try to reclaim an abandoned segment
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  bool  reclaimed ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment  =  mi_segment_try_reclaim ( heap ,  needed_slices ,  block_size ,  & reclaimed ,  tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( reclaimed )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // reclaimed the right page right into the heap
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( segment  ! =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  NULL ;  // pretend out-of-memory as the page will be in the page queue of the heap with available blocks
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  if  ( segment  ! =  NULL )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // reclaimed a segment with a large enough empty span in it
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  segment ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // 2. otherwise allocate a fresh segment
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  mi_segment_alloc ( 0 ,  0 ,  heap - > arena_id ,  tld ,  os_tld ,  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								   Page  allocation 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  mi_page_t *  mi_segments_page_alloc ( mi_heap_t *  heap ,  mi_page_kind_t  page_kind ,  size_t  required ,  size_t  block_size ,  mi_segments_tld_t *  tld ,  mi_os_tld_t *  os_tld ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( required  < =  MI_LARGE_OBJ_SIZE_MAX  & &  page_kind  < =  MI_PAGE_LARGE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // find a free page
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  page_size  =  _mi_align_up ( required ,  ( required  >  MI_MEDIUM_PAGE_SIZE  ?  MI_MEDIUM_PAGE_SIZE  :  MI_SEGMENT_SLICE_SIZE ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  slices_needed  =  page_size  /  MI_SEGMENT_SLICE_SIZE ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( slices_needed  *  MI_SEGMENT_SLICE_SIZE  = =  page_size ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_page_t *  page  =  mi_segments_page_find_and_allocate ( slices_needed ,  heap - > arena_id ,  tld ) ;  //(required <= MI_SMALL_SIZE_MAX ? 0 : slices_needed), tld);
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( page = = NULL )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    // no free page, allocate a new segment and try again
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( mi_segment_reclaim_or_alloc ( heap ,  slices_needed ,  block_size ,  tld ,  os_tld )  = =  NULL )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // OOM or reclaimed a good page in the heap
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      return  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      // otherwise try again
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      return  mi_segments_page_alloc ( heap ,  page_kind ,  required ,  block_size ,  tld ,  os_tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( page  ! =  NULL  & &  page - > slice_count * MI_SEGMENT_SLICE_SIZE  = =  page_size ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( _mi_ptr_segment ( page ) - > thread_id  = =  _mi_thread_id ( ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_try_purge ( _mi_ptr_segment ( page ) ,  false ,  tld - > stats ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  page ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								   Huge  page  allocation 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  mi_page_t *  mi_segment_huge_page_alloc ( size_t  size ,  size_t  page_alignment ,  mi_arena_id_t  req_arena_id ,  mi_segments_tld_t *  tld ,  mi_os_tld_t *  os_tld ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_page_t *  page  =  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment  =  mi_segment_alloc ( size , page_alignment , req_arena_id , tld , os_tld , & page ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( segment  = =  NULL  | |  page = = NULL )  return  NULL ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > used = = 1 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_page_block_size ( page )  > =  size ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  # if MI_HUGE_PAGE_ABANDON 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment - > thread_id  =  0 ;  // huge segments are immediately abandoned
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  # endif 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // for huge pages we initialize the xblock_size as we may
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // overallocate to accommodate large alignments.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  psize ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  uint8_t *  start  =  _mi_segment_page_start ( segment ,  page ,  & psize ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  page - > xblock_size  =  ( psize  >  MI_HUGE_BLOCK_SIZE  ?  MI_HUGE_BLOCK_SIZE  :  ( uint32_t ) psize ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // decommit the part of the prefix of a page that will not be used; this can be quite large (close to MI_SEGMENT_SIZE)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( page_alignment  >  0  & &  segment - > allow_decommit )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    uint8_t *  aligned_p  =  ( uint8_t * ) _mi_align_up ( ( uintptr_t ) start ,  page_alignment ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( _mi_is_aligned ( aligned_p ,  page_alignment ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( psize  -  ( aligned_p  -  start )  > =  size ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    uint8_t *  decommit_start  =  start  +  sizeof ( mi_block_t ) ;               // for the free list
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    ptrdiff_t  decommit_size  =  aligned_p  -  decommit_start ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    _mi_os_reset ( decommit_start ,  decommit_size ,  & _mi_stats_main ) ;    // note: cannot use segment_decommit on huge segments
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  page ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# if MI_HUGE_PAGE_ABANDON 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// free huge block from another thread
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								void  _mi_segment_huge_page_free ( mi_segment_t *  segment ,  mi_page_t *  page ,  mi_block_t *  block )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // huge page segments are always abandoned and can be freed immediately by any thread
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > kind = = MI_SEGMENT_HUGE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment  = =  _mi_page_segment ( page ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( mi_atomic_load_relaxed ( & segment - > thread_id ) = = 0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // claim it and free
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_heap_t *  heap  =  mi_heap_get_default ( ) ;  // issue #221; don't use the internal get_default_heap as we need to ensure the thread is initialized.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // paranoia: if this it the last reference, the cas should always succeed
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  size_t  expected_tid  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( mi_atomic_cas_strong_acq_rel ( & segment - > thread_id ,  & expected_tid ,  heap - > thread_id ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_block_set_next ( page ,  block ,  page - > free ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    page - > free  =  block ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    page - > used - - ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    page - > is_zero  =  false ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert ( page - > used  = =  0 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_tld_t *  tld  =  heap - > tld ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    _mi_segment_page_free ( page ,  true ,  & tld - > segments ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# if (MI_DEBUG!=0) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( false ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# endif 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# else 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// reset memory of a huge block from another thread
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								void  _mi_segment_huge_page_reset ( mi_segment_t *  segment ,  mi_page_t *  page ,  mi_block_t *  block )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  MI_UNUSED ( page ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment - > kind  = =  MI_SEGMENT_HUGE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( segment  = =  _mi_page_segment ( page ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( page - > used  = =  1 ) ;  // this is called just before the free
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( page - > free  = =  NULL ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( segment - > allow_decommit )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    size_t  csize  =  mi_usable_size ( block ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( csize  >  sizeof ( mi_block_t ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      csize  =  csize  -  sizeof ( mi_block_t ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      uint8_t *  p  =  ( uint8_t * ) block  +  sizeof ( mi_block_t ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      _mi_os_reset ( p ,  csize ,  & _mi_stats_main ) ;   // note: cannot use segment_decommit on huge segments
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# endif 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								   Page  allocation  and  free 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								mi_page_t *  _mi_segment_page_alloc ( mi_heap_t *  heap ,  size_t  block_size ,  size_t  page_alignment ,  mi_segments_tld_t *  tld ,  mi_os_tld_t *  os_tld )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_page_t *  page ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  mi_unlikely ( page_alignment  >  MI_ALIGNMENT_MAX )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( _mi_is_power_of_two ( page_alignment ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mi_assert_internal ( page_alignment  > =  MI_SEGMENT_SIZE ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( page_alignment  <  MI_SEGMENT_SIZE )  {  page_alignment  =  MI_SEGMENT_SIZE ;  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    page  =  mi_segment_huge_page_alloc ( block_size , page_alignment , heap - > arena_id , tld , os_tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  if  ( block_size  < =  MI_SMALL_OBJ_SIZE_MAX )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    page  =  mi_segments_page_alloc ( heap , MI_PAGE_SMALL , block_size , block_size , tld , os_tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  if  ( block_size  < =  MI_MEDIUM_OBJ_SIZE_MAX )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    page  =  mi_segments_page_alloc ( heap , MI_PAGE_MEDIUM , MI_MEDIUM_PAGE_SIZE , block_size , tld ,  os_tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  if  ( block_size  < =  MI_LARGE_OBJ_SIZE_MAX )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    page  =  mi_segments_page_alloc ( heap , MI_PAGE_LARGE , block_size , block_size , tld ,  os_tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    page  =  mi_segment_huge_page_alloc ( block_size , page_alignment , heap - > arena_id , tld , os_tld ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_internal ( page  = =  NULL  | |  _mi_heap_memid_is_suitable ( heap ,  _mi_page_segment ( page ) - > memid ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_assert_expensive ( page  = =  NULL  | |  mi_segment_is_valid ( _mi_page_segment ( page ) , tld ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  page ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-22 16:10:21 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								/* -----------------------------------------------------------
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								   Visit  blocks  in  abandoned  segments 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  bool  mi_segment_visit_page ( mi_segment_t *  segment ,  mi_page_t *  page ,  bool  visit_blocks ,  mi_block_visit_fun *  visitor ,  void *  arg ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_heap_area_t  area ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  _mi_heap_area_init ( & area ,  page ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( ! visitor ( NULL ,  & area ,  NULL ,  area . block_size ,  arg ) )  return  false ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( visit_blocks )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  _mi_heap_area_visit_blocks ( & area ,  page ,  visitor ,  arg ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  bool  mi_segment_visit_pages ( mi_segment_t *  segment ,  uint8_t  page_tag ,  bool  visit_blocks ,  mi_block_visit_fun *  visitor ,  void *  arg )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  const  mi_slice_t *  end ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_slice_t *  slice  =  mi_slices_start_iterate ( segment ,  & end ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  while  ( slice  <  end )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( mi_slice_is_used ( slice ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      mi_page_t *  const  page  =  mi_slice_to_page ( slice ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  ( page - > tag  = =  page_tag )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ( ! mi_segment_visit_page ( segment ,  page ,  visit_blocks ,  visitor ,  arg ) )  return  false ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    slice  =  slice  +  slice - > slice_count ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								// Visit all blocks in a abandoned segments
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								bool  _mi_abandoned_pool_visit_blocks ( mi_abandoned_pool_t *  pool ,  uint8_t  page_tag ,  bool  visit_blocks ,  mi_block_visit_fun *  visitor ,  void *  arg )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  // Note: this is not safe in any other thread is abandoning or claiming segments from the pool
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  mi_segment_t *  segment  =  mi_tagged_segment_ptr ( pool - > abandoned ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  while  ( segment  ! =  NULL )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( ! mi_segment_visit_pages ( segment ,  page_tag ,  visit_blocks ,  visitor ,  arg ) )  return  false ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    segment  =  segment - > abandoned_next ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  segment  =  pool - > abandoned_visited ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  while  ( segment  ! =  NULL )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( ! mi_segment_visit_pages ( segment ,  page_tag ,  visit_blocks ,  visitor ,  arg ) )  return  false ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    segment  =  segment - > abandoned_next ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								}