memcache.cpp revision 0aa150e34ed49f14aaa37368c2e6999ec89e5f43
/* $Id$ */
/** @file
* IPRT - Memory Object Allocation Cache.
*/
/*
* Copyright (C) 2006-2010 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/memcache.h>
#include <iprt/critsect.h>
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/** Pointer to a cache instance. */
typedef struct RTMEMCACHEINT *PRTMEMCACHEINT;
/** Pointer to a cache page. */
typedef struct RTMEMCACHEPAGE *PRTMEMCACHEPAGE;
/**
* A free object.
*
* @remarks This only works if the objects don't have a constructor or
* destructor and are big enough.
*/
typedef struct RTMEMCACHEFREEOBJ
{
/** Pointer to the next free object */
struct RTMEMCACHEFREEOBJ * volatile pNext;
/** Pointer to a free object. */
typedef RTMEMCACHEFREEOBJ *PRTMEMCACHEFREEOBJ;
/**
* A cache page.
*
* This is a page of memory that we split up in to a bunch object sized chunks
* and hand out to the cache users. The bitmap is updated in an atomic fashion
* so that we don't have to take any locks when freeing or allocating memory.
*/
typedef struct RTMEMCACHEPAGE
{
/** Pointer to the cache owning this page.
* This is used for validation purposes only. */
/** Pointer to the next page.
* This is marked as volatile since we'll be adding new entries to the list
* without taking any locks. */
PRTMEMCACHEPAGE volatile pNext;
/** Bitmap tracking allocated blocks. */
void volatile *pbmAlloc;
/** Bitmap tracking which blocks that has been thru the constructor. */
void volatile *pbmCtor;
/** Pointer to the object array.. */
/** The number of objects on this page. */
/** Padding to force cFree into the next cache line. (ASSUMES CL = 64) */
/** The number of free objects. */
/**
* Memory object cache instance.
*/
typedef struct RTMEMCACHEINT
{
/** Magic value (RTMEMCACHE_MAGIC). */
/** The object size. */
/** Object alignment. */
/** The per page object count. */
/** Number of bits in the bitmap.
* @remarks This is higher or equal to cPerPage and it is aligned such that
/** The maximum number of objects. */
/** Whether to the use the free list or not. */
bool fUseFreeList;
/** Head of the page list. */
/** Constructor callback. */
/** Destructor callback. */
/** Callback argument. */
void *pvUser;
/** Critical section serializing page allocation and similar. */
/** The total object count. */
/** The number of free objects. */
/** This may point to a page with free entries. */
PRTMEMCACHEPAGE volatile pPageHint;
/** Stack of free items.
* These are marked as used in the allocation bitmaps.
*
* @todo This doesn't scale well when several threads are beating on the
* cache. Also, it totally doesn't work when we've got a
* constructor/destructor around or the objects are too small. */
PRTMEMCACHEFREEOBJ volatile pFreeTop;
RTDECL(int) RTMemCacheCreate(PRTMEMCACHE phMemCache, size_t cbObject, size_t cbAlignment, uint32_t cMaxObjects,
{
if (cbAlignment == 0)
{
if (cbAlignment > 64)
cbAlignment = 64;
}
else
{
}
/*
* Allocate and initialize the instance memory.
*/
if (!pThis)
return VERR_NO_MEMORY;
if (RT_FAILURE(rc))
{
return rc;
}
pThis->cPerPage = (uint32_t)((PAGE_SIZE - RT_ALIGN_Z(sizeof(RTMEMCACHEPAGE), cbAlignment)) / pThis->cbObject);
> PAGE_SIZE)
&& !pfnCtor
&& !pfnDtor;
*phMemCache = pThis;
return VINF_SUCCESS;
}
{
if (!pThis)
return VINF_SUCCESS;
#ifdef RT_STRICT
cFree++;
#endif
/*
* Destroy it.
*/
AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTMEMCACHE_MAGIC_DEAD, RTMEMCACHE_MAGIC), VERR_INVALID_HANDLE);
{
{
while (iObj-- > 0)
}
}
return VINF_SUCCESS;
}
/**
* Grows the cache.
*
* @returns IPRT status code.
* @param pThis The memory cache instance.
*/
{
/*
* Enter the critical section here to avoid allocation races leading to
* wasted memory (++) and make it easier to link in the new page.
*/
int rc = VINF_SUCCESS;
{
/*
* Allocate and initialize the new page.
*
* We put the constructor bitmap at the lower end right after cFree.
* We then push the object array to the end of the page and place the
* allocation bitmap below it. The hope is to increase the chance that
* the allocation bitmap is in a different cache line than cFree since
* this increases performance markably when lots of threads are beating
* on the cache.
*/
if (pPage)
{
/* Mark the bitmap padding and any unused objects as allocated. */
/* Make it the hint. */
/* Link the page. */
if (!pPrevPage)
else
{
}
/* Add it to the page counts. */
}
else
rc = VERR_NO_MEMORY;
}
return rc;
}
/**
* Grabs a an object in a page.
* @returns New cFree value on success (0 or higher), -1 on failure.
* @param pPage Pointer to the page.
*/
{
if (cFreeNew < 0)
{
return -1;
}
return cFreeNew;
}
{
/*
* Try grab a free object from the stack.
*/
PRTMEMCACHEFREEOBJ pObj = (PRTMEMCACHEFREEOBJ)ASMAtomicUoReadPtr((void * volatile *)&pThis->pFreeTop);
if (pObj)
{
do
{
{
return VINF_SUCCESS;
}
} while (pObj);
}
/*
* Try grab a free object at the cache level.
*/
{
{
return VERR_MEM_CACHE_MAX_SIZE;
}
if (RT_FAILURE(rc))
{
return rc;
}
}
/*
* Grab a free object at the page level.
*/
if (iObj < 0)
{
{
{
if (iObj >= 0)
{
if (iObj > 0)
break;
}
}
if (iObj >= 0)
break;
}
}
/*
* Find a free object in the allocation bitmap. Use the new cFree count
* as a hint.
*/
{
{
{
break;
}
else
}
}
/*
* Call the constructor?
*/
{
if (RT_FAILURE(rc))
{
return rc;
}
}
return VINF_SUCCESS;
}
{
void *pvObj;
if (RT_SUCCESS(rc))
return pvObj;
return NULL;
}
{
if (!pvObj)
return;
if (pThis->fUseFreeList)
{
# ifdef RT_STRICT
/* This is the same as the other branch, except it's not actually freed. */
# endif
/*
* Push it onto the free stack.
*/
do
{
}
else
{
/* Note: Do *NOT* attempt to poison the object if we have a constructor
/*
* Find the cache page. The page structure is at the start of the page.
*/
/*
* Clear the bitmap bit and update the two object counter. Order matters!
*/
}
}