You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mlt/src/framework/mlt_pool.c

356 lines
7.0 KiB

/*
* mlt_pool.c -- memory pooling functionality
* Copyright (C) 2003-2004 Ushodaya Enterprises Limited
* Author: Charles Yates <charles.yates@pandora.be>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mlt_properties.h"
#include "mlt_deque.h"
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
// Not nice - memalign is defined here apparently?
#ifdef linux
#include <malloc.h>
#endif
/** Singleton repositories
*/
static mlt_properties pools = NULL;
/** Private pooling structure.
*/
typedef struct mlt_pool_s
{
pthread_mutex_t lock;
mlt_deque stack;
int size;
int count;
}
*mlt_pool;
typedef struct mlt_release_s
{
mlt_pool pool;
int references;
}
*mlt_release;
/** Create a pool.
*/
static mlt_pool pool_init( int size )
{
// Create the pool
mlt_pool this = calloc( 1, sizeof( struct mlt_pool_s ) );
// Initialise it
if ( this != NULL )
{
// Initialise the mutex
pthread_mutex_init( &this->lock, NULL );
// Create the stack
this->stack = mlt_deque_init( );
// Assign the size
this->size = size;
}
// Return it
return this;
}
/** Get an item from the pool.
*/
static void *pool_fetch( mlt_pool this )
{
// We will generate a release object
void *ptr = NULL;
// Sanity check
if ( this != NULL )
{
// Lock the pool
pthread_mutex_lock( &this->lock );
// Check if the stack is empty
if ( mlt_deque_count( this->stack ) != 0 )
{
// Pop the top of the stack
ptr = mlt_deque_pop_back( this->stack );
// Assign the reference
( ( mlt_release )ptr )->references = 1;
}
else
{
// We need to generate a release item
#ifdef linux
mlt_release release = memalign( 16, this->size );
#else
mlt_release release = malloc( this->size );
#endif
// Initialise it
if ( release != NULL )
{
// Increment the number of items allocated to this pool
this->count ++;
// Assign the pool
release->pool = this;
// Assign the reference
release->references = 1;
// Determine the ptr
ptr = ( void * )release + sizeof( struct mlt_release_s );
}
}
// Unlock the pool
pthread_mutex_unlock( &this->lock );
}
// Return the generated release object
return ptr;
}
/** Return an item to the pool.
*/
static void pool_return( void *ptr )
{
// Sanity checks
if ( ptr != NULL )
{
// Get the release pointer
mlt_release that = ptr - sizeof( struct mlt_release_s );
// Get the pool
mlt_pool this = that->pool;
if ( this != NULL )
{
// Lock the pool
pthread_mutex_lock( &this->lock );
// Push the that back back on to the stack
mlt_deque_push_back( this->stack, ptr );
// Unlock the pool
pthread_mutex_unlock( &this->lock );
// Ensure that we don't clean up
ptr = NULL;
}
}
// Tidy up - this will only occur if the returned item is incorrect
if ( ptr != NULL )
{
// Free the release itself
free( ptr - sizeof( struct mlt_release_s ) );
}
}
/** Destroy a pool.
*/
static void pool_close( mlt_pool this )
{
if ( this != NULL )
{
// We need to free up all items in the pool
void *release = NULL;
// Iterate through the stack until depleted
while ( ( release = mlt_deque_pop_back( this->stack ) ) != NULL )
{
// We'll free this item now
free( release - sizeof( struct mlt_release_s ) );
}
// We can now close the stack
mlt_deque_close( this->stack );
// Destroy the mutex
pthread_mutex_destroy( &this->lock );
// Close the pool
free( this );
}
}
/** Initialise the pool.
*/
void mlt_pool_init( )
{
// Loop variable used to create the pools
int i = 0;
// Create the pools
pools = mlt_properties_new( );
// Create the pools
for ( i = 8; i < 31; i ++ )
{
// Each properties item needs a name
char name[ 32 ];
// Construct a pool
mlt_pool pool = pool_init( 1 << i );
// Generate a name
sprintf( name, "%d", i );
// Register with properties
mlt_properties_set_data( pools, name, pool, 0, ( mlt_destructor )pool_close, NULL );
}
}
/** Allocate size bytes from the pool.
*/
void *mlt_pool_alloc( int size )
{
// This will be used to obtain the pool to use
mlt_pool pool = NULL;
// Determines the index of the pool to use
int index = 8;
// Minimum size pooled is 256 bytes
size = size + sizeof( mlt_release );
while ( ( 1 << index ) < size )
index ++;
// Now get the pool at the index
pool = mlt_properties_get_data_at( pools, index - 8, NULL );
// Now get the real item
return pool_fetch( pool );
}
/** Allocate size bytes from the pool.
*/
void *mlt_pool_realloc( void *ptr, int size )
{
// Result to return
void *result = NULL;
// Check if we actually have an address
if ( ptr != NULL )
{
// Get the release pointer
mlt_release that = ptr - sizeof( struct mlt_release_s );
// If the current pool this ptr belongs to is big enough
if ( size > that->pool->size - sizeof( struct mlt_release_s ) )
{
// Allocate
result = mlt_pool_alloc( size );
// Copy
memcpy( result, ptr, that->pool->size - sizeof( struct mlt_release_s ) );
// Release
mlt_pool_release( ptr );
}
else
{
// Nothing to do
result = ptr;
}
}
else
{
// Simply allocate
result = mlt_pool_alloc( size );
}
return result;
}
/** Purge unused items in the pool.
*/
void mlt_pool_purge( )
{
int i = 0;
// For each pool
for ( i = 0; i < mlt_properties_count( pools ); i ++ )
{
// Get the pool
mlt_pool this = mlt_properties_get_data_at( pools, i, NULL );
// Pointer to unused memory
void *release = NULL;
// Lock the pool
pthread_mutex_lock( &this->lock );
// We'll free all unused items now
while ( ( release = mlt_deque_pop_back( this->stack ) ) != NULL )
free( release - sizeof( struct mlt_release_s ) );
// Unlock the pool
pthread_mutex_unlock( &this->lock );
}
}
/** Release the allocated memory.
*/
void mlt_pool_release( void *release )
{
// Return to the pool
pool_return( release );
}
/** Close the pool.
*/
void mlt_pool_close( )
{
#ifdef _MLT_POOL_CHECKS_
// Stats dump on close
int i = 0;
fprintf( stderr, "Usage:\n\n" );
for ( i = 0; i < mlt_properties_count( pools ); i ++ )
{
mlt_pool pool = mlt_properties_get_data_at( pools, i, NULL );
if ( pool->count )
fprintf( stderr, "%d: allocated %d returned %d %c\n", pool->size, pool->count, mlt_deque_count( pool->stack ),
pool->count != mlt_deque_count( pool->stack ) ? '*' : ' ' );
}
#endif
// Close the properties
mlt_properties_close( pools );
}