3 * \brief least recently used cache
6 * Copyright (C) 2007-2009 Ushodaya Enterprises Limited
7 * \author Dan Dennedy <dan@dennedy.org>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "mlt_types.h"
26 #include "mlt_properties.h"
27 #include "mlt_cache.h"
32 /** the maximum number of data objects to cache per line */
33 #define CACHE_SIZE (10)
35 /** \brief Cache item class
37 * A cache item is a structure holding information about a data object including
38 * a reference count that is used to control its lifetime. When you get a
39 * a cache item from the cache, you hold a reference that prevents the data
40 * from being released when the cache is full and something new is added.
41 * When you close the cache item, the reference count is decremented.
42 * The data object is destroyed when all cache items are closed and the cache
43 * releases its reference.
46 typedef struct mlt_cache_item_s
48 mlt_cache cache
; /**< a reference to the cache to which this belongs */
49 void *object
; /**< a parent object to the cache data that uniquely identifies this cached item */
50 void *data
; /**< the opaque pointer to the cached data */
51 int size
; /**< the size of the cached data */
52 int refcount
; /**< a reference counter to control when destructor is called */
53 mlt_destructor destructor
; /**< a function to release or destroy the cached data */
56 /** \brief Cache class
58 * This is a utility class for implementing a Least Recently Used (LRU) cache
59 * of data blobs indexed by the address of some other object (e.g., a service).
60 * Instead of sorting and manipulating linked lists, it tries to be simple and
61 * elegant by copying pointers between two arrays of fixed size to shuffle the
64 * This class is useful if you have a service that wants to cache something
65 * somewhat large, but will not scale if there are many instances of the service.
66 * Of course, the service will need to know how to recreate the cached element
67 * if it gets flushed from the cache,
69 * The most obvious examples are the pixbuf and qimage producers that cache their
70 * respective objects representing a picture read from a file. If the picture
71 * is no longer in the cache, it can simply re-read it from file. However, a
72 * picture is often repeated over many frames and makes sense to cache instead
73 * of continually reading, parsing, and decoding. On the other hand, you might
74 * want to load hundreds of pictures as individual producers, which would use
75 * a lot of memory if every picture is held in memory!
80 int count
; /**< the number of items currently in the cache */
81 void* *current
; /**< pointer to the current array of pointers */
82 void* A
[ CACHE_SIZE
];
83 void* B
[ CACHE_SIZE
];
84 pthread_mutex_t mutex
; /**< a mutex to prevent multi-threaded race conditions */
85 mlt_properties active
; /**< a list of cache items some of which may no longer
86 be in \p current but to which there are
87 outstanding references */
88 mlt_properties garbage
;/**< a list cache items pending release. A cache item
89 is copied to this list when it is updated but there
90 are outstanding references to the old data object. */
93 /** Get the data pointer from the cache item.
95 * \public \memberof mlt_cache_s
96 * \param item a cache item
97 * \param[out] size the number of bytes pointed at, if supplied when putting the data into the cache
98 * \return the data pointer
101 void *mlt_cache_item_data( mlt_cache_item item
, int *size
)
105 return item? item
->data
: NULL
;
108 /** Close a cache item given its parent object pointer.
110 * \private \memberof mlt_cache_s
111 * \param cache a cache
112 * \param object the object to which the data object belongs
113 * \param data the data object, which might be in the garbage list (optional)
116 static void cache_object_close( mlt_cache cache
, void *object
, void* data
)
120 // Fetch the cache item from the active list by its owner's address
121 sprintf( key
, "%p", object
);
122 pthread_mutex_lock( &cache
->mutex
);
123 mlt_cache_item item
= mlt_properties_get_data( cache
->active
, key
, NULL
);
126 mlt_log( NULL
, MLT_LOG_DEBUG
, "%s: item %p object %p data %p refcount %d\n", __FUNCTION__
,
127 item
, item
->object
, item
->data
, item
->refcount
);
128 if ( item
->destructor
&& --item
->refcount
<= 0 )
130 // Destroy the data object
131 item
->destructor( item
->data
);
133 item
->destructor
= NULL
;
134 // Do not dispose of the cache item because it could likely be used
139 // Fetch the cache item from the garbage collection by its data address
142 sprintf( key
, "%p", data
);
143 item
= mlt_properties_get_data( cache
->garbage
, key
, NULL
);
146 mlt_log( NULL
, MLT_LOG_DEBUG
, "collecting garbage item %p object %p data %p refcount %d\n",
147 item
, item
->object
, item
->data
, item
->refcount
);
148 if ( item
->destructor
&& --item
->refcount
<= 0 )
150 item
->destructor( item
->data
);
152 item
->destructor
= NULL
;
153 // We do not need the garbage-collected cache item
154 mlt_properties_set_data( cache
->garbage
, key
, NULL
, 0, NULL
, NULL
);
158 pthread_mutex_unlock( &cache
->mutex
);
161 /** Close a cache item.
163 * Release a reference and call the destructor on the data object when all
164 * references are released.
166 * \public \memberof mlt_cache_item_s
167 * \param item a cache item
170 void mlt_cache_item_close( mlt_cache_item item
)
173 cache_object_close( item
->cache
, item
->object
, item
->data
);
176 /** Create a new cache.
178 * \public \memberof mlt_cache_s
179 * \return a new cache or NULL if there was an error
182 mlt_cache
mlt_cache_init()
184 mlt_cache result
= calloc( 1, sizeof( struct mlt_cache_s
) );
187 result
->current
= result
->A
;
188 pthread_mutex_init( &result
->mutex
, NULL
);
189 result
->active
= mlt_properties_new();
190 result
->garbage
= mlt_properties_new();
197 * \public \memberof mlt_cache_s
198 * \param cache the cache to detroy
201 void mlt_cache_close( mlt_cache cache
)
205 while ( cache
->count
-- )
207 void *object
= cache
->current
[ cache
->count
];
208 mlt_log( NULL
, MLT_LOG_DEBUG
, "%s: %d = %p\n", __FUNCTION__
, cache
->count
, object
);
209 cache_object_close( cache
, object
, NULL
);
211 mlt_properties_close( cache
->active
);
212 mlt_properties_close( cache
->garbage
);
213 pthread_mutex_destroy( &cache
->mutex
);
218 /** Remove cache entries for an object.
220 * \public \memberof mlt_cache_s
221 * \param cache a cache
222 * \param object the object that owns the cached data
225 void mlt_cache_purge( mlt_cache cache
, void *object
)
227 pthread_mutex_lock( &cache
->mutex
);
228 if ( cache
&& object
)
231 void **alt
= cache
->current
== cache
->A ? cache
->B
: cache
->A
;
233 for ( i
= 0, j
= 0; i
< cache
->count
; i
++ )
235 void *o
= cache
->current
[ i
];
239 pthread_mutex_unlock( &cache
->mutex
);
240 cache_object_close( cache
, o
, NULL
);
241 pthread_mutex_lock( &cache
->mutex
);
249 cache
->current
= alt
;
251 // Remove the object's data from the active list regardless of refcount
253 sprintf( key
, "%p", object
);
254 mlt_cache_item item
= mlt_properties_get_data( cache
->active
, key
, NULL
);
255 if ( item
&& item
->destructor
)
257 item
->destructor( item
->data
);
259 item
->destructor
= NULL
;
260 mlt_properties_set_data( cache
->active
, key
, NULL
, 0, NULL
, NULL
);
263 // Remove the object's items from the garbage collection regardless of refcount
264 i
= mlt_properties_count( cache
->garbage
);
267 item
= mlt_properties_get_data_at( cache
->garbage
, i
, NULL
);
268 if ( object
== item
->object
&& item
->destructor
)
270 sprintf( key
, "%p", item
->data
);
271 item
->destructor( item
->data
);
273 item
->destructor
= NULL
;
274 mlt_properties_set_data( cache
->garbage
, key
, NULL
, 0, NULL
, NULL
);
278 pthread_mutex_unlock( &cache
->mutex
);
281 /** Shuffle the cache entries between the two arrays and return the cache entry for an object.
283 * \private \memberof mlt_cache_s
284 * \param cache a cache object
285 * \param object the object that owns the cached data
286 * \return a cache entry if there was a hit or NULL for a miss
289 static void** shuffle_get_hit( mlt_cache cache
, void *object
)
291 int i
= cache
->count
;
292 int j
= cache
->count
- 1;
294 void **alt
= cache
->current
== cache
->A ? cache
->B
: cache
->A
;
296 if ( cache
->count
> 0 && cache
->count
< CACHE_SIZE
)
298 // first determine if we have a hit
299 while ( i
-- && !hit
)
301 void **o
= &cache
->current
[ i
];
305 // if there was no hit, we will not be shuffling out an entry
306 // and are still filling the cache
314 // shuffle the existing entries to the alternate array
317 void **o
= &cache
->current
[ i
];
319 if ( !hit
&& *o
== object
)
326 // mlt_log( NULL, MLT_LOG_DEBUG, "%s: shuffle %d = %p\n", __FUNCTION__, j, alt[j] );
332 /** Put a chunk of data in the cache.
334 * \public \memberof mlt_cache_s
335 * \param cache a cache object
336 * \param object the object to which this data belongs
337 * \param data an opaque pointer to the data to cache
338 * \param size the size of the data in bytes
339 * \param destructor a pointer to a function that can destroy or release a reference to the data.
342 void mlt_cache_put( mlt_cache cache
, void *object
, void* data
, int size
, mlt_destructor destructor
)
344 pthread_mutex_lock( &cache
->mutex
);
345 void **hit
= shuffle_get_hit( cache
, object
);
346 void **alt
= cache
->current
== cache
->A ? cache
->B
: cache
->A
;
348 // add the object to the cache
351 // release the old data
352 pthread_mutex_unlock( &cache
->mutex
);
353 cache_object_close( cache
, *hit
, NULL
);
354 pthread_mutex_lock( &cache
->mutex
);
355 // the MRU end gets the updated data
356 hit
= &alt
[ cache
->count
- 1 ];
358 else if ( cache
->count
< CACHE_SIZE
)
360 // more room in cache, add it to MRU end
361 hit
= &alt
[ cache
->count
++ ];
365 // release the entry at the LRU end
366 pthread_mutex_unlock( &cache
->mutex
);
367 cache_object_close( cache
, cache
->current
[0], NULL
);
368 pthread_mutex_lock( &cache
->mutex
);
370 // The MRU end gets the new item
371 hit
= &alt
[ cache
->count
- 1 ];
374 mlt_log( NULL
, MLT_LOG_DEBUG
, "%s: put %d = %p, %p\n", __FUNCTION__
, cache
->count
- 1, object
, data
);
376 // Fetch the cache item
378 sprintf( key
, "%p", object
);
379 mlt_cache_item item
= mlt_properties_get_data( cache
->active
, key
, NULL
);
382 item
= calloc( 1, sizeof( mlt_cache_item_s
) );
384 mlt_properties_set_data( cache
->active
, key
, item
, 0, free
, NULL
);
388 // If updating the cache item but not all references are released
389 // copy the item to the garbage collection.
390 if ( item
->refcount
> 0 && item
->data
)
392 mlt_cache_item orphan
= calloc( 1, sizeof( mlt_cache_item_s
) );
395 mlt_log( NULL
, MLT_LOG_DEBUG
, "adding to garbage collection object %p data %p\n", item
->object
, item
->data
);
397 sprintf( key
, "%p", orphan
->data
);
398 // We store in the garbage collection by data address, not the owner's!
399 mlt_properties_set_data( cache
->garbage
, key
, orphan
, 0, free
, NULL
);
403 // Set/update the cache item
405 item
->object
= object
;
408 item
->destructor
= destructor
;
412 // swap the current array
413 cache
->current
= alt
;
414 pthread_mutex_unlock( &cache
->mutex
);
417 /** Get a chunk of data from the cache.
419 * \public \memberof mlt_cache_s
420 * \param cache a cache object
421 * \param object the object for which you are trying to locate the data
422 * \return a mlt_cache_item if found or NULL if not found or has been flushed from the cache
425 mlt_cache_item
mlt_cache_get( mlt_cache cache
, void *object
)
427 mlt_cache_item result
= NULL
;
428 pthread_mutex_lock( &cache
->mutex
);
429 void **hit
= shuffle_get_hit( cache
, object
);
430 void **alt
= cache
->current
== cache
->A ? cache
->B
: cache
->A
;
434 // copy the hit to the MRU end
435 alt
[ cache
->count
- 1 ] = *hit
;
436 hit
= &alt
[ cache
->count
- 1 ];
439 sprintf( key
, "%p", *hit
);
440 result
= mlt_properties_get_data( cache
->active
, key
, NULL
);
441 if ( result
&& result
->data
)
443 mlt_log( NULL
, MLT_LOG_DEBUG
, "%s: get %d = %p, %p\n", __FUNCTION__
, cache
->count
- 1, *hit
, result
->data
);
445 // swap the current array
446 cache
->current
= alt
;
448 pthread_mutex_unlock( &cache
->mutex
);