FreeType 1.31.1
This commit is contained in:
463
lib/ttcache.c
Normal file
463
lib/ttcache.c
Normal file
@@ -0,0 +1,463 @@
|
||||
/*******************************************************************
|
||||
*
|
||||
* 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 */
|
||||
Reference in New Issue
Block a user