2006-02-07 00:25:02 +10:00
/** \file halloc.c
2006-02-07 04:11:01 +10:00
A hierarchical memory allocation system . Works just like talloc
used in Samba , except that an arbitrary block allocated with
malloc ( ) can be registered to be freed by halloc_free .
2006-02-07 00:25:02 +10:00
*/
# include "config.h"
2006-02-28 23:17:16 +10:00
2006-02-07 00:25:02 +10:00
# include <stdlib.h>
# include <unistd.h>
2006-02-28 23:17:16 +10:00
# include "fallback.h"
2006-02-07 00:25:02 +10:00
# include "util.h"
2006-02-28 23:17:16 +10:00
2006-02-07 00:25:02 +10:00
# include "common.h"
# include "halloc.h"
2006-06-20 10:50:10 +10:00
/**
Extra size to allocate whenever doing a halloc , in order to fill uyp smaller halloc calls
*/
2006-02-12 23:18:46 +10:00
# define HALLOC_BLOCK_SIZE 128
2006-06-20 10:50:10 +10:00
/**
2006-09-30 22:19:17 +10:00
Maximum size of trailing halloc space to refuse to discard . This is
set to be larger on 64 - bit platforms , since we don ' t want to get
' stuck ' with an unusably small slice of memory , and what is
unusably small often depends on pointer size .
2006-06-20 10:50:10 +10:00
*/
2006-09-30 22:19:17 +10:00
# define HALLOC_SCRAP_SIZE (4*sizeof(void *))
2006-02-11 10:13:17 +10:00
2006-02-12 23:18:46 +10:00
# ifdef HALLOC_DEBUG
2006-06-20 10:50:10 +10:00
/**
Debug statistic parameter
*/
2006-02-11 10:13:17 +10:00
static int child_count = 0 ;
2006-06-20 10:50:10 +10:00
/**
Debug statistic parameter
*/
2006-02-11 10:13:17 +10:00
static int child_size = 0 ;
2006-06-20 10:50:10 +10:00
/**
Debug statistic parameter
*/
2006-02-11 10:13:17 +10:00
static int alloc_count = 0 ;
2006-06-20 10:50:10 +10:00
/**
Debug statistic parameter
*/
2006-02-11 10:13:17 +10:00
static int alloc_spill = 0 ;
2006-06-20 10:50:10 +10:00
/**
Debug statistic parameter
*/
2006-02-11 10:13:17 +10:00
static pid_t pid = 0 ;
2006-06-20 10:50:10 +10:00
/**
Debug statistic parameter
*/
2006-02-11 10:13:17 +10:00
static int parent_count = 0 ;
2006-02-12 23:18:46 +10:00
# endif
2006-02-11 10:13:17 +10:00
2006-06-20 10:50:10 +10:00
/**
The main datastructure for a main halloc context
*/
2006-02-07 00:25:02 +10:00
typedef struct halloc
{
2006-06-20 10:50:10 +10:00
/**
List of all addresses and functions to call on them
*/
2006-02-07 00:25:02 +10:00
array_list_t children ;
2006-06-20 10:50:10 +10:00
/**
Memory scratch area used to fullfil smaller memory allocations
*/
2006-02-11 10:13:17 +10:00
void * scratch ;
2006-06-20 10:50:10 +10:00
/**
Amount of free space in the scratch area
*/
2006-09-30 22:19:17 +10:00
ssize_t scratch_free ;
2006-02-07 00:25:02 +10:00
}
halloc_t ;
2006-09-30 22:19:17 +10:00
static void * align_ptr ( void * in )
{
unsigned long step = maxi ( sizeof ( double ) , sizeof ( void * ) ) ;
unsigned long inc = step - 1 ;
unsigned long long_in = ( long ) in ;
unsigned long long_out = ( ( long_in + inc ) / step ) * step ;
return ( void * ) long_out ;
}
static size_t align_sz ( size_t in )
{
size_t step = maxi ( sizeof ( double ) , sizeof ( void * ) ) ;
size_t inc = step - 1 ;
return ( ( in + inc ) / step ) * step ;
}
2006-06-20 10:50:10 +10:00
/**
Get the offset of the halloc structure before a data block
*/
2006-02-07 00:25:02 +10:00
static halloc_t * halloc_from_data ( void * data )
{
2006-09-30 22:19:17 +10:00
return ( halloc_t * ) ( ( ( char * ) data ) - align_sz ( sizeof ( halloc_t ) ) ) ;
2006-02-07 00:25:02 +10:00
}
2006-06-20 10:50:10 +10:00
/**
A function that does nothing
*/
2006-02-11 10:13:17 +10:00
static void late_free ( void * data )
{
}
2006-02-12 23:18:46 +10:00
# ifdef HALLOC_DEBUG
2006-06-20 10:50:10 +10:00
/**
Debug function , called at exit when in debug mode . Prints usage
statistics , like number of allocations and number of internal calls
to malloc .
*/
2006-09-30 22:19:17 +10:00
static void halloc_report ( )
2006-02-11 10:13:17 +10:00
{
if ( getpid ( ) = = pid )
{
debug ( 1 , L " %d parents, %d children with average child size of %.2f bytes caused %d allocs, average spill of %.2f bytes " ,
parent_count , child_count , ( double ) child_size / child_count ,
parent_count + alloc_count , ( double ) alloc_spill / ( parent_count + alloc_count ) ) ;
}
}
2006-02-12 23:18:46 +10:00
# endif
2006-02-07 00:25:02 +10:00
2006-09-30 22:19:17 +10:00
2006-02-07 00:25:02 +10:00
void * halloc ( void * context , size_t size )
2006-02-10 01:50:20 +10:00
{
2006-02-07 04:11:01 +10:00
halloc_t * me , * parent ;
2006-02-07 00:25:02 +10:00
if ( context )
2006-02-11 10:13:17 +10:00
{
void * res ;
2006-09-30 22:19:17 +10:00
void * aligned ;
2006-02-11 10:13:17 +10:00
2006-02-12 23:18:46 +10:00
# ifdef HALLOC_DEBUG
2006-02-11 10:13:17 +10:00
if ( ! child_count )
{
pid = getpid ( ) ;
2006-09-30 22:19:17 +10:00
atexit ( & halloc_report ) ;
2006-02-11 10:13:17 +10:00
}
child_count + + ;
child_size + = size ;
2006-02-12 23:18:46 +10:00
# endif
2006-02-07 04:11:01 +10:00
parent = halloc_from_data ( context ) ;
2006-09-30 22:19:17 +10:00
/*
Align memory address
*/
aligned = align_ptr ( parent - > scratch ) ;
parent - > scratch_free - = ( aligned - parent - > scratch ) ;
if ( parent - > scratch_free < 0 )
parent - > scratch_free = 0 ;
parent - > scratch = aligned ;
2006-02-11 10:13:17 +10:00
if ( size < = parent - > scratch_free )
{
res = parent - > scratch ;
parent - > scratch_free - = size ;
2006-03-29 10:14:50 +10:00
parent - > scratch = ( ( char * ) parent - > scratch ) + size ;
2006-02-11 10:13:17 +10:00
}
else
{
2006-02-12 23:18:46 +10:00
# ifdef HALLOC_DEBUG
2006-02-11 10:13:17 +10:00
alloc_count + + ;
2006-02-12 23:18:46 +10:00
# endif
2006-02-11 10:13:17 +10:00
if ( parent - > scratch_free < HALLOC_SCRAP_SIZE )
{
2006-02-12 23:18:46 +10:00
# ifdef HALLOC_DEBUG
2006-02-11 10:13:17 +10:00
alloc_spill + = parent - > scratch_free ;
2006-02-12 23:18:46 +10:00
# endif
2006-02-11 10:13:17 +10:00
res = calloc ( 1 , size + HALLOC_BLOCK_SIZE ) ;
2007-01-09 12:51:02 +10:00
if ( ! res )
DIE_MEM ( ) ;
2006-03-29 10:14:50 +10:00
parent - > scratch = ( char * ) res + size ;
2006-02-11 10:13:17 +10:00
parent - > scratch_free = HALLOC_BLOCK_SIZE ;
}
else
{
res = calloc ( 1 , size ) ;
2007-01-09 12:51:02 +10:00
if ( ! res )
DIE_MEM ( ) ;
2006-02-11 10:13:17 +10:00
}
al_push ( & parent - > children , & late_free ) ;
al_push ( & parent - > children , res ) ;
}
return res ;
2006-02-10 01:50:20 +10:00
2006-02-11 10:13:17 +10:00
}
else
{
2006-09-30 22:19:17 +10:00
me = ( halloc_t * ) calloc ( 1 , align_sz ( sizeof ( halloc_t ) ) + align_sz ( size ) + HALLOC_BLOCK_SIZE ) ;
2006-02-11 10:13:17 +10:00
if ( ! me )
2007-01-09 12:51:02 +10:00
DIE_MEM ( ) ;
2006-02-12 23:18:46 +10:00
# ifdef HALLOC_DEBUG
2006-02-11 10:13:17 +10:00
parent_count + + ;
2006-02-12 23:18:46 +10:00
# endif
2006-09-30 22:19:17 +10:00
me - > scratch = ( ( char * ) me ) + align_sz ( sizeof ( halloc_t ) ) + align_sz ( size ) ;
2006-02-11 10:13:17 +10:00
me - > scratch_free = HALLOC_BLOCK_SIZE ;
al_init ( & me - > children ) ;
2006-09-30 22:19:17 +10:00
return ( ( char * ) me ) + align_sz ( sizeof ( halloc_t ) ) ;
2006-02-11 10:13:17 +10:00
}
2006-02-07 00:25:02 +10:00
}
2006-02-10 01:50:20 +10:00
void halloc_register_function ( void * context , void ( * func ) ( void * ) , void * data )
2006-02-07 00:25:02 +10:00
{
halloc_t * me ;
if ( ! context )
2006-02-10 01:50:20 +10:00
return ;
2006-02-07 00:25:02 +10:00
me = halloc_from_data ( context ) ;
2006-02-10 01:50:20 +10:00
al_push ( & me - > children , func ) ;
2006-02-07 04:11:01 +10:00
al_push ( & me - > children , data ) ;
2006-02-07 00:25:02 +10:00
}
2006-02-07 04:11:01 +10:00
void halloc_free ( void * context )
2006-02-07 00:25:02 +10:00
{
2006-02-07 04:11:01 +10:00
halloc_t * me ;
2006-02-10 01:50:20 +10:00
int i ;
2006-02-07 04:11:01 +10:00
if ( ! context )
return ;
2006-02-11 10:13:17 +10:00
2006-02-10 01:50:20 +10:00
2006-02-07 04:11:01 +10:00
me = halloc_from_data ( context ) ;
2006-02-11 10:13:17 +10:00
2006-02-12 23:18:46 +10:00
# ifdef HALLOC_DEBUG
2006-02-11 10:13:17 +10:00
alloc_spill + = me - > scratch_free ;
2006-02-12 23:18:46 +10:00
# endif
2006-02-11 10:13:17 +10:00
for ( i = 0 ; i < al_get_count ( & me - > children ) ; i + = 2 )
{
void ( * func ) ( void * ) = ( void ( * ) ( void * ) ) al_get ( & me - > children , i ) ;
void * data = ( void * ) al_get ( & me - > children , i + 1 ) ;
if ( func ! = & late_free )
func ( data ) ;
}
2006-02-10 01:50:20 +10:00
for ( i = 0 ; i < al_get_count ( & me - > children ) ; i + = 2 )
{
void ( * func ) ( void * ) = ( void ( * ) ( void * ) ) al_get ( & me - > children , i ) ;
void * data = ( void * ) al_get ( & me - > children , i + 1 ) ;
2006-02-11 10:13:17 +10:00
if ( func = = & late_free )
free ( data ) ;
2006-02-10 01:50:20 +10:00
}
2006-02-07 04:11:01 +10:00
al_destroy ( & me - > children ) ;
free ( me ) ;
2006-02-07 00:25:02 +10:00
}