464 lines
12 KiB
C
464 lines
12 KiB
C
/*******************************************************************
|
|
*
|
|
* ttcache.c 1.1
|
|
*
|
|
* Generic object cache
|
|
*
|
|
* Copyright 1996-1999 by
|
|
* David Turner, Robert Wilhelm, and Werner Lemberg.
|
|
*
|
|
* This file is part of the FreeType project, and may only be used
|
|
* modified and distributed under the terms of the FreeType project
|
|
* license, LICENSE.TXT. By continuing to use, modify, or distribute
|
|
* this file you indicate that you have read the license and
|
|
* understand and accept it fully.
|
|
*
|
|
* Changes between 1.1 and 1.0:
|
|
*
|
|
* - introduced the refresher and finalizer in the cache class
|
|
* definition/implementation.
|
|
*
|
|
******************************************************************/
|
|
|
|
#include "ttengine.h"
|
|
#include "ttmemory.h"
|
|
#include "ttcache.h"
|
|
#include "ttobjs.h"
|
|
#include "ttdebug.h"
|
|
|
|
/* required by the tracing mode */
|
|
#undef TT_COMPONENT
|
|
#define TT_COMPONENT trace_cache
|
|
|
|
#define ZERO_List( list ) list = NULL
|
|
|
|
/* The macro FREE_Elements aliases the current engine instance's */
|
|
/* free list_elements recycle list. */
|
|
#define FREE_Elements ( engine->list_free_elements )
|
|
|
|
/* Redefinition of LOCK and UNLOCK macros for New_Element and Done_Element */
|
|
/* Note: The macros are redefined below for the cache functions */
|
|
|
|
#undef LOCK
|
|
#define LOCK() MUTEX_Lock ( engine->lock )
|
|
|
|
#undef UNLOCK
|
|
#define UNLOCK() MUTEX_Release( engine->lock )
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : Element_New
|
|
*
|
|
* Description : Gets a new (either fresh or recycled) list
|
|
* element. The element is unlisted.
|
|
*
|
|
* Input : None
|
|
*
|
|
* Output : List element address. NULL if out of memory.
|
|
*
|
|
******************************************************************/
|
|
|
|
static
|
|
PList_Element Element_New( PEngine_Instance engine )
|
|
{
|
|
PList_Element element;
|
|
|
|
|
|
LOCK();
|
|
if ( FREE_Elements )
|
|
{
|
|
element = (PList_Element)FREE_Elements;
|
|
FREE_Elements = element->next;
|
|
}
|
|
else
|
|
{
|
|
if ( !MEM_Alloc( element, sizeof ( TList_Element ) ) )
|
|
{
|
|
element->next = NULL;
|
|
element->data = NULL;
|
|
}
|
|
}
|
|
|
|
/* Note: in case of failure, Alloc sets the pointer to NULL */
|
|
UNLOCK();
|
|
|
|
return element;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : Element_Done
|
|
*
|
|
* Description : Recycles an unlinked list element.
|
|
*
|
|
* Input : The list element to recycle. It _must_ be unlisted.
|
|
*
|
|
* Output : none.
|
|
*
|
|
* Note : This function doesn't check the element.
|
|
*
|
|
******************************************************************/
|
|
|
|
static
|
|
void Element_Done( PEngine_Instance engine,
|
|
PList_Element element )
|
|
{
|
|
LOCK();
|
|
/* Simply add the list element to the recycle list */
|
|
element->next = (PList_Element)FREE_Elements;
|
|
FREE_Elements = element;
|
|
UNLOCK();
|
|
}
|
|
|
|
|
|
/* Redefinition of LOCK and UNLOCK macros for the cache functions */
|
|
/* Note: The macros are defined above for the list element functions */
|
|
|
|
#undef LOCK
|
|
#define LOCK() MUTEX_Lock( *cache->lock )
|
|
|
|
#undef UNLOCK
|
|
#define UNLOCK() MUTEX_Release( *cache->lock )
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : Cache_Create
|
|
*
|
|
* Description : Creates a new cache that will be used to list
|
|
* and recycle several objects of the same class.
|
|
*
|
|
* Input : clazz a pointer to the cache's class. This is
|
|
* a simple structure that describes the
|
|
* the cache's object types and recycling
|
|
* limits.
|
|
*
|
|
* cache address of cache to create
|
|
*
|
|
* lock address of the mutex to use for this
|
|
* cache. The mutex will be used to protect
|
|
* the cache's lists. Use NULL for unprotected
|
|
* cache.
|
|
*
|
|
* Output : Error code.
|
|
*
|
|
******************************************************************/
|
|
|
|
LOCAL_FUNC
|
|
TT_Error Cache_Create( PEngine_Instance engine,
|
|
PCache_Class clazz,
|
|
TCache* cache,
|
|
TMutex* lock )
|
|
{
|
|
cache->engine = engine;
|
|
cache->clazz = clazz;
|
|
cache->lock = lock;
|
|
cache->idle_count = 0;
|
|
|
|
ZERO_List( cache->active );
|
|
ZERO_List( cache->idle );
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : Cache_Destroy
|
|
*
|
|
* Description : Destroys a cache and all its idle and active
|
|
* objects. This will call each object's destructor
|
|
* before freeing it.
|
|
*
|
|
* Input : cache address of cache to destroy
|
|
*
|
|
* Output : error code.
|
|
*
|
|
* Note: This function is not MT-Safe, as we assume that a client
|
|
* isn't stupid enough to use an object while destroying it.
|
|
*
|
|
******************************************************************/
|
|
|
|
LOCAL_FUNC
|
|
TT_Error Cache_Destroy( TCache* cache )
|
|
{
|
|
PDestructor destroy;
|
|
PList_Element current;
|
|
PList_Element next;
|
|
|
|
|
|
/* now destroy all active and idle listed objects */
|
|
|
|
/* get the destructor function */
|
|
destroy = cache->clazz->done;
|
|
|
|
/* destroy all elements in active list */
|
|
current = cache->active;
|
|
while ( current )
|
|
{
|
|
next = current->next;
|
|
destroy( current->data );
|
|
FREE( current->data );
|
|
|
|
Element_Done( cache->engine, current );
|
|
current = next;
|
|
}
|
|
ZERO_List(cache->active);
|
|
|
|
/* destroy all elements in idle list */
|
|
current = cache->idle;
|
|
while ( current )
|
|
{
|
|
next = current->next;
|
|
destroy( current->data );
|
|
FREE( current->data );
|
|
|
|
Element_Done( cache->engine, current );
|
|
current = next;
|
|
}
|
|
ZERO_List(cache->idle);
|
|
|
|
cache->clazz = NULL;
|
|
cache->idle_count = 0;
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : Cache_New
|
|
*
|
|
* Description : Extracts a new object from a cache. This will
|
|
* try to recycle an idle object, if any is found.
|
|
* Otherwise, a new object will be allocated and
|
|
* built (by calling its constructor).
|
|
*
|
|
* Input : cache address of cache to use
|
|
* new_object address of target pointer to the 'new'
|
|
* object
|
|
* parent_object this pointer is passed to a new object
|
|
* constructor (unused if object is
|
|
* recycled)
|
|
*
|
|
* Output : Error code.
|
|
*
|
|
* Notes: This function is thread-safe, each cache list is protected
|
|
* through the cache's mutex, if there is one...
|
|
*
|
|
* *new_object will be set to NULL in case of failure.
|
|
*
|
|
******************************************************************/
|
|
|
|
LOCAL_FUNC
|
|
TT_Error Cache_New( TCache* cache,
|
|
void** new_object,
|
|
void* parent_object )
|
|
{
|
|
TT_Error error;
|
|
PList_Element current;
|
|
PConstructor build;
|
|
PRefresher reset;
|
|
void* object;
|
|
|
|
|
|
LOCK();
|
|
current = cache->idle;
|
|
if ( current )
|
|
{
|
|
cache->idle = current->next;
|
|
cache->idle_count--;
|
|
}
|
|
UNLOCK();
|
|
|
|
if ( current )
|
|
{
|
|
object = current->data;
|
|
reset = cache->clazz->reset;
|
|
if ( reset )
|
|
{
|
|
error = reset( object, parent_object );
|
|
if ( error )
|
|
{
|
|
LOCK();
|
|
current->next = cache->idle;
|
|
cache->idle = current;
|
|
cache->idle_count++;
|
|
UNLOCK();
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* if no object was found in the cache, create a new one */
|
|
build = cache->clazz->init;
|
|
|
|
if ( MEM_Alloc( object, cache->clazz->object_size ) )
|
|
goto Memory_Fail;
|
|
|
|
current = Element_New( cache->engine );
|
|
if ( !current )
|
|
goto Memory_Fail;
|
|
|
|
current->data = object;
|
|
|
|
error = build( object, parent_object );
|
|
if ( error )
|
|
{
|
|
Element_Done( cache->engine, current );
|
|
goto Fail;
|
|
}
|
|
}
|
|
|
|
LOCK();
|
|
current->next = cache->active;
|
|
cache->active = current;
|
|
UNLOCK();
|
|
|
|
*new_object = current->data;
|
|
return TT_Err_Ok;
|
|
|
|
Exit:
|
|
*new_object = NULL;
|
|
return error;
|
|
|
|
Memory_Fail:
|
|
error = TT_Err_Out_Of_Memory;
|
|
|
|
Fail:
|
|
FREE( object );
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : Cache_Done
|
|
*
|
|
* Description : Releases an object to the cache. This will either
|
|
* recycle or destroy the object, based on the cache's
|
|
* class and state.
|
|
*
|
|
* Input : cache the cache to use
|
|
* data the object to recycle/discard
|
|
*
|
|
* Output : error code.
|
|
*
|
|
* Notes : The object's destructor is called only when
|
|
* the objectwill be effectively destroyed by this
|
|
* function. This will not happen during recycling.
|
|
*
|
|
******************************************************************/
|
|
|
|
LOCAL_FUNC
|
|
TT_Error Cache_Done( TCache* cache, void* data )
|
|
{
|
|
TT_Error error;
|
|
PList_Element element;
|
|
PList_Element prev;
|
|
PFinalizer finalize;
|
|
Long limit;
|
|
Bool destroy;
|
|
|
|
|
|
/* Look for object in active list */
|
|
LOCK();
|
|
|
|
element = cache->active;
|
|
prev = NULL;
|
|
while ( element )
|
|
{
|
|
if ( element->data == data )
|
|
{
|
|
if ( prev )
|
|
prev->next = element->next;
|
|
else
|
|
cache->active = element->next;
|
|
goto Suite;
|
|
}
|
|
prev = element;
|
|
element = element->next;
|
|
}
|
|
|
|
UNLOCK();
|
|
return TT_Err_Unlisted_Object;
|
|
|
|
Suite:
|
|
|
|
limit = cache->clazz->idle_limit;
|
|
destroy = (cache->idle_count >= limit);
|
|
UNLOCK();
|
|
|
|
if ( destroy )
|
|
{
|
|
/* destroy the object when the cache is full */
|
|
cache->clazz->done( element->data );
|
|
FREE( element->data );
|
|
Element_Done( cache->engine, element );
|
|
}
|
|
else
|
|
{
|
|
/* Finalize the object before adding it to the */
|
|
/* idle list. Return the error if any is found. */
|
|
|
|
finalize = cache->clazz->finalize;
|
|
if ( finalize )
|
|
{
|
|
error = finalize( element->data );
|
|
if ( error )
|
|
goto Exit;
|
|
|
|
/* Note: a failure at finalize time is a severe bug in */
|
|
/* the engine, which is why we allow ourselves to */
|
|
/* lose the object in this case. A finalizer should */
|
|
/* have its own error codes to spot this kind of */
|
|
/* problems easily. */
|
|
}
|
|
|
|
LOCK();
|
|
element->next = cache->idle;
|
|
cache->idle = element;
|
|
cache->idle_count++;
|
|
UNLOCK();
|
|
}
|
|
|
|
error = TT_Err_Ok;
|
|
|
|
Exit:
|
|
return error;
|
|
}
|
|
|
|
|
|
LOCAL_FUNC
|
|
TT_Error TTCache_Init( PEngine_Instance engine )
|
|
{
|
|
/* Create list elements mutex */
|
|
FREE_Elements = NULL;
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
LOCAL_FUNC
|
|
TT_Error TTCache_Done( PEngine_Instance engine )
|
|
{
|
|
/* We don't protect this function, as this is the end of the engine's */
|
|
/* execution.. */
|
|
PList_Element element, next;
|
|
|
|
|
|
/* frees the recycled list elements */
|
|
element = FREE_Elements;
|
|
while ( element )
|
|
{
|
|
next = element->next;
|
|
FREE( element );
|
|
element = next;
|
|
}
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
/* END */
|