X-Git-Url: http://research.m1stereo.tv/gitweb?a=blobdiff_plain;f=src%2Fframework%2Fmlt_cache.c;fp=src%2Fframework%2Fmlt_cache.c;h=0000000000000000000000000000000000000000;hb=17b5a97b9bbe00b442a58a5065498e67b497af09;hp=5a188719e92079dda5629e109ef8eedee3cb622b;hpb=f4963a6aa07644399b273b5d2b1f9299c9047414;p=melted diff --git a/src/framework/mlt_cache.c b/src/framework/mlt_cache.c deleted file mode 100644 index 5a18871..0000000 --- a/src/framework/mlt_cache.c +++ /dev/null @@ -1,451 +0,0 @@ -/** - * \file mlt_profile.c - * \brief least recently used cache - * \see mlt_profile_s - * - * Copyright (C) 2007-2009 Ushodaya Enterprises Limited - * \author Dan Dennedy - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "mlt_types.h" -#include "mlt_log.h" -#include "mlt_properties.h" -#include "mlt_cache.h" - -#include -#include - -/** the maximum number of data objects to cache per line */ -#define CACHE_SIZE (10) - -/** \brief Cache item class - * - * A cache item is a structure holding information about a data object including - * a reference count that is used to control its lifetime. When you get a - * a cache item from the cache, you hold a reference that prevents the data - * from being released when the cache is full and something new is added. - * When you close the cache item, the reference count is decremented. - * The data object is destroyed when all cache items are closed and the cache - * releases its reference. - */ - -typedef struct mlt_cache_item_s -{ - mlt_cache cache; /**< a reference to the cache to which this belongs */ - void *object; /**< a parent object to the cache data that uniquely identifies this cached item */ - void *data; /**< the opaque pointer to the cached data */ - int size; /**< the size of the cached data */ - int refcount; /**< a reference counter to control when destructor is called */ - mlt_destructor destructor; /**< a function to release or destroy the cached data */ -} mlt_cache_item_s; - -/** \brief Cache class - * - * This is a utility class for implementing a Least Recently Used (LRU) cache - * of data blobs indexed by the address of some other object (e.g., a service). - * Instead of sorting and manipulating linked lists, it tries to be simple and - * elegant by copying pointers between two arrays of fixed size to shuffle the - * order of elements. - * - * This class is useful if you have a service that wants to cache something - * somewhat large, but will not scale if there are many instances of the service. - * Of course, the service will need to know how to recreate the cached element - * if it gets flushed from the cache, - * - * The most obvious examples are the pixbuf and qimage producers that cache their - * respective objects representing a picture read from a file. If the picture - * is no longer in the cache, it can simply re-read it from file. However, a - * picture is often repeated over many frames and makes sense to cache instead - * of continually reading, parsing, and decoding. On the other hand, you might - * want to load hundreds of pictures as individual producers, which would use - * a lot of memory if every picture is held in memory! - */ - -struct mlt_cache_s -{ - int count; /**< the number of items currently in the cache */ - void* *current; /**< pointer to the current array of pointers */ - void* A[ CACHE_SIZE ]; - void* B[ CACHE_SIZE ]; - pthread_mutex_t mutex; /**< a mutex to prevent multi-threaded race conditions */ - mlt_properties active; /**< a list of cache items some of which may no longer - be in \p current but to which there are - outstanding references */ - mlt_properties garbage;/**< a list cache items pending release. A cache item - is copied to this list when it is updated but there - are outstanding references to the old data object. */ -}; - -/** Get the data pointer from the cache item. - * - * \public \memberof mlt_cache_s - * \param item a cache item - * \param[out] size the number of bytes pointed at, if supplied when putting the data into the cache - * \return the data pointer - */ - -void *mlt_cache_item_data( mlt_cache_item item, int *size ) -{ - if ( size && item ) - *size = item->size; - return item? item->data : NULL; -} - -/** Close a cache item given its parent object pointer. - * - * \private \memberof mlt_cache_s - * \param cache a cache - * \param object the object to which the data object belongs - * \param data the data object, which might be in the garbage list (optional) - */ - -static void cache_object_close( mlt_cache cache, void *object, void* data ) -{ - char key[19]; - - // Fetch the cache item from the active list by its owner's address - sprintf( key, "%p", object ); - pthread_mutex_lock( &cache->mutex ); - mlt_cache_item item = mlt_properties_get_data( cache->active, key, NULL ); - if ( item ) - { - mlt_log( NULL, MLT_LOG_DEBUG, "%s: item %p object %p data %p refcount %d\n", __FUNCTION__, - item, item->object, item->data, item->refcount ); - if ( item->destructor && --item->refcount <= 0 ) - { - // Destroy the data object - item->destructor( item->data ); - item->data = NULL; - item->destructor = NULL; - // Do not dispose of the cache item because it could likely be used - // again. - } - } - - // Fetch the cache item from the garbage collection by its data address - if ( data ) - { - sprintf( key, "%p", data ); - item = mlt_properties_get_data( cache->garbage, key, NULL ); - if ( item ) - { - mlt_log( NULL, MLT_LOG_DEBUG, "collecting garbage item %p object %p data %p refcount %d\n", - item, item->object, item->data, item->refcount ); - if ( item->destructor && --item->refcount <= 0 ) - { - item->destructor( item->data ); - item->data = NULL; - item->destructor = NULL; - // We do not need the garbage-collected cache item - mlt_properties_set_data( cache->garbage, key, NULL, 0, NULL, NULL ); - } - } - } - pthread_mutex_unlock( &cache->mutex ); -} - -/** Close a cache item. - * - * Release a reference and call the destructor on the data object when all - * references are released. - * - * \public \memberof mlt_cache_item_s - * \param item a cache item - */ - -void mlt_cache_item_close( mlt_cache_item item ) -{ - if ( item ) - cache_object_close( item->cache, item->object, item->data ); -} - -/** Create a new cache. - * - * \public \memberof mlt_cache_s - * \return a new cache or NULL if there was an error - */ - -mlt_cache mlt_cache_init() -{ - mlt_cache result = calloc( 1, sizeof( struct mlt_cache_s ) ); - if ( result ) - { - result->current = result->A; - pthread_mutex_init( &result->mutex, NULL ); - result->active = mlt_properties_new(); - result->garbage = mlt_properties_new(); - } - return result; -} - -/** Destroy a cache. - * - * \public \memberof mlt_cache_s - * \param cache the cache to detroy - */ - -void mlt_cache_close( mlt_cache cache ) -{ - if ( cache ) - { - while ( cache->count-- ) - { - void *object = cache->current[ cache->count ]; - mlt_log( NULL, MLT_LOG_DEBUG, "%s: %d = %p\n", __FUNCTION__, cache->count, object ); - cache_object_close( cache, object, NULL ); - } - mlt_properties_close( cache->active ); - mlt_properties_close( cache->garbage ); - pthread_mutex_destroy( &cache->mutex ); - free( cache ); - } -} - -/** Remove cache entries for an object. - * - * \public \memberof mlt_cache_s - * \param cache a cache - * \param object the object that owns the cached data - */ - -void mlt_cache_purge( mlt_cache cache, void *object ) -{ - pthread_mutex_lock( &cache->mutex ); - if ( cache && object ) - { - int i, j; - void **alt = cache->current == cache->A ? cache->B : cache->A; - - for ( i = 0, j = 0; i < cache->count; i++ ) - { - void *o = cache->current[ i ]; - - if ( o == object ) - { - pthread_mutex_unlock( &cache->mutex ); - cache_object_close( cache, o, NULL ); - pthread_mutex_lock( &cache->mutex ); - } - else - { - alt[ j++ ] = o; - } - } - cache->count = j; - cache->current = alt; - - // Remove the object's data from the active list regardless of refcount - char key[19]; - sprintf( key, "%p", object ); - mlt_cache_item item = mlt_properties_get_data( cache->active, key, NULL ); - if ( item && item->destructor ) - { - item->destructor( item->data ); - item->data = NULL; - item->destructor = NULL; - mlt_properties_set_data( cache->active, key, NULL, 0, NULL, NULL ); - } - - // Remove the object's items from the garbage collection regardless of refcount - i = mlt_properties_count( cache->garbage ); - while ( i-- ) - { - item = mlt_properties_get_data_at( cache->garbage, i, NULL ); - if ( object == item->object && item->destructor ) - { - sprintf( key, "%p", item->data ); - item->destructor( item->data ); - item->data = NULL; - item->destructor = NULL; - mlt_properties_set_data( cache->garbage, key, NULL, 0, NULL, NULL ); - } - } - } - pthread_mutex_unlock( &cache->mutex ); -} - -/** Shuffle the cache entries between the two arrays and return the cache entry for an object. - * - * \private \memberof mlt_cache_s - * \param cache a cache object - * \param object the object that owns the cached data - * \return a cache entry if there was a hit or NULL for a miss - */ - -static void** shuffle_get_hit( mlt_cache cache, void *object ) -{ - int i = cache->count; - int j = cache->count - 1; - void **hit = NULL; - void **alt = cache->current == cache->A ? cache->B : cache->A; - - if ( cache->count > 0 && cache->count < CACHE_SIZE ) - { - // first determine if we have a hit - while ( i-- && !hit ) - { - void **o = &cache->current[ i ]; - if ( *o == object ) - hit = o; - } - // if there was no hit, we will not be shuffling out an entry - // and are still filling the cache - if ( !hit ) - ++j; - // reset these - i = cache->count; - hit = NULL; - } - - // shuffle the existing entries to the alternate array - while ( i-- ) - { - void **o = &cache->current[ i ]; - - if ( !hit && *o == object ) - { - hit = o; - } - else if ( j > 0 ) - { - alt[ --j ] = *o; -// mlt_log( NULL, MLT_LOG_DEBUG, "%s: shuffle %d = %p\n", __FUNCTION__, j, alt[j] ); - } - } - return hit; -} - -/** Put a chunk of data in the cache. - * - * \public \memberof mlt_cache_s - * \param cache a cache object - * \param object the object to which this data belongs - * \param data an opaque pointer to the data to cache - * \param size the size of the data in bytes - * \param destructor a pointer to a function that can destroy or release a reference to the data. - */ - -void mlt_cache_put( mlt_cache cache, void *object, void* data, int size, mlt_destructor destructor ) -{ - pthread_mutex_lock( &cache->mutex ); - void **hit = shuffle_get_hit( cache, object ); - void **alt = cache->current == cache->A ? cache->B : cache->A; - - // add the object to the cache - if ( hit ) - { - // release the old data - pthread_mutex_unlock( &cache->mutex ); - cache_object_close( cache, *hit, NULL ); - pthread_mutex_lock( &cache->mutex ); - // the MRU end gets the updated data - hit = &alt[ cache->count - 1 ]; - } - else if ( cache->count < CACHE_SIZE ) - { - // more room in cache, add it to MRU end - hit = &alt[ cache->count++ ]; - } - else - { - // release the entry at the LRU end - pthread_mutex_unlock( &cache->mutex ); - cache_object_close( cache, cache->current[0], NULL ); - pthread_mutex_lock( &cache->mutex ); - - // The MRU end gets the new item - hit = &alt[ cache->count - 1 ]; - } - *hit = object; - mlt_log( NULL, MLT_LOG_DEBUG, "%s: put %d = %p, %p\n", __FUNCTION__, cache->count - 1, object, data ); - - // Fetch the cache item - char key[19]; - sprintf( key, "%p", object ); - mlt_cache_item item = mlt_properties_get_data( cache->active, key, NULL ); - if ( !item ) - { - item = calloc( 1, sizeof( mlt_cache_item_s ) ); - if ( item ) - mlt_properties_set_data( cache->active, key, item, 0, free, NULL ); - } - if ( item ) - { - // If updating the cache item but not all references are released - // copy the item to the garbage collection. - if ( item->refcount > 0 && item->data ) - { - mlt_cache_item orphan = calloc( 1, sizeof( mlt_cache_item_s ) ); - if ( orphan ) - { - mlt_log( NULL, MLT_LOG_DEBUG, "adding to garbage collection object %p data %p\n", item->object, item->data ); - *orphan = *item; - sprintf( key, "%p", orphan->data ); - // We store in the garbage collection by data address, not the owner's! - mlt_properties_set_data( cache->garbage, key, orphan, 0, free, NULL ); - } - } - - // Set/update the cache item - item->cache = cache; - item->object = object; - item->data = data; - item->size = size; - item->destructor = destructor; - item->refcount = 1; - } - - // swap the current array - cache->current = alt; - pthread_mutex_unlock( &cache->mutex ); -} - -/** Get a chunk of data from the cache. - * - * \public \memberof mlt_cache_s - * \param cache a cache object - * \param object the object for which you are trying to locate the data - * \return a mlt_cache_item if found or NULL if not found or has been flushed from the cache - */ - -mlt_cache_item mlt_cache_get( mlt_cache cache, void *object ) -{ - mlt_cache_item result = NULL; - pthread_mutex_lock( &cache->mutex ); - void **hit = shuffle_get_hit( cache, object ); - void **alt = cache->current == cache->A ? cache->B : cache->A; - - if ( hit ) - { - // copy the hit to the MRU end - alt[ cache->count - 1 ] = *hit; - hit = &alt[ cache->count - 1 ]; - - char key[19]; - sprintf( key, "%p", *hit ); - result = mlt_properties_get_data( cache->active, key, NULL ); - if ( result && result->data ) - result->refcount++; - mlt_log( NULL, MLT_LOG_DEBUG, "%s: get %d = %p, %p\n", __FUNCTION__, cache->count - 1, *hit, result->data ); - - // swap the current array - cache->current = alt; - } - pthread_mutex_unlock( &cache->mutex ); - - return result; -}