bug 146532 Improving recycling allocator

- Uses freelist
- 1 int overhead to store size
- nsIMemory wrapping
r=dougt, sr=darin
This commit is contained in:
dp%netscape.com 2002-07-01 23:36:03 +00:00
parent b421736c2e
commit e1522de4fb
8 changed files with 473 additions and 231 deletions

View File

@ -105,6 +105,7 @@
#ifdef GC_LEAK_DETECTOR
#include "nsLeakDetector.h"
#endif
#include "nsRecyclingAllocator.h"
// Registry Factory creation function defined in nsRegistry.cpp
// We hook into this function locally to create and register the registry
@ -283,7 +284,9 @@ static const nsModuleComponentInfo components[] = {
COMPONENT(FASTLOADSERVICE, nsFastLoadService::Create),
COMPONENT(VARIANT, nsVariantConstructor),
COMPONENT(INTERFACEINFOMANAGER_SERVICE, nsXPTIInterfaceInfoManagerGetSingleton)
COMPONENT(INTERFACEINFOMANAGER_SERVICE, nsXPTIInterfaceInfoManagerGetSingleton),
COMPONENT(RECYCLINGALLOCATOR, nsRecyclingAllocatorImpl::nsRecyclingAllocatorImplConstructor),
};
#undef COMPONENT

View File

@ -7,6 +7,7 @@ nsIObserverService.idl
nsIPersistentProperties2.idl
nsIProperties.idl
nsIPropertyBag.idl
nsIRecyclingAllocator.idl
nsISerializable.idl
nsISimpleEnumerator.idl
nsISupportsArray.idl

View File

@ -116,6 +116,7 @@ XPIDLSRCS = \
nsIPersistentProperties2.idl \
nsIProperties.idl \
nsIPropertyBag.idl \
nsIRecyclingAllocator.idl \
nsIVariant.idl \
nsISerializable.idl \
nsISupportsArray.idl \

View File

@ -75,6 +75,7 @@ XPIDLSRCS = \
.\nsITimelineService.idl \
.\nsIProperties.idl \
.\nsIPropertyBag.idl \
.\nsIRecyclingAllocator.idl \
.\nsISerializable.idl \
.\nsISimpleEnumerator.idl \
.\nsISupportsArray.idl \

View File

@ -0,0 +1,67 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s): Suresh Duddi <dp@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsIMemory.idl"
/**
*
* nsIRecyclingAllocator: A wrapper for the nsRecyclingAllocator
*
* Holds allocations and reuses them for subsequent allocs.
* Thread safe and uses a timer to release freelist.
*
* @status UNDER-DEVELOPMENT
*/
[scriptable, uuid(d064a04c-9cee-4319-be31-64d565bccba9)]
interface nsIRecyclingAllocator : nsIMemory
{
void init(in size_t nblocks, in size_t recycleAfter, in string id);
};
%{C++
#define NS_RECYCLINGALLOCATOR_CID \
{ /* ac07ed4c-bf17-4976-a15c-d2194db3b1bf */ \
0xac07ed4c, \
0xbf17, \
0x4976, \
{0xa1, 0x5c, 0xd2, 0x19, 0x4d, 0xb3, 0xb1, 0xbf} }
#define NS_RECYCLINGALLOCATOR_CLASSNAME "Recycling Allocator"
#define NS_RECYCLINGALLOCATOR_CONTRACTID "@mozilla.org/recycling-allocator;1"
%}

View File

@ -45,6 +45,9 @@
#include <stdio.h>
#include "nsRecyclingAllocator.h"
#include "nsITimer.h"
#include "nsIMemory.h"
#include "nsAutoLock.h"
#include "prprf.h"
#define NS_SEC_TO_MS(s) ((s) * 1000)
@ -54,17 +57,14 @@ nsRecyclingAllocator::nsRecycleTimerCallback(nsITimer *aTimer, void *aClosure)
nsRecyclingAllocator *obj = (nsRecyclingAllocator *) aClosure;
if (!obj->mTouched)
{
#ifdef DEBUG_dp
printf("DEBUG: nsRecyclingAllocator not touched: %d allocations avaliable\n", obj->mNAllocations);
#endif
if (obj->mNAllocations)
if (obj->mFreeList)
obj->FreeUnusedBuckets();
// If we are holding no more memory, there is not need for the timer.
// If we are holding no more memory, there is no need for the timer.
// We will revive the timer on the next allocation.
// XXX Unfortunately there is no way to Cancel and restart the same timer.
// XXX So we pretty much kill it and create a new one later.
if (!obj->mNAllocations && obj->mRecycleTimer)
if (!obj->mFreeList && obj->mRecycleTimer)
{
obj->mRecycleTimer->Cancel();
NS_RELEASE(obj->mRecycleTimer);
@ -79,186 +79,129 @@ nsRecyclingAllocator::nsRecycleTimerCallback(nsITimer *aTimer, void *aClosure)
}
nsRecyclingAllocator::nsRecyclingAllocator(PRUint32 nbucket, PRUint32 recycleAfter, const char *id)
: mNBucket(nbucket), mRecycleTimer(nsnull), mRecycleAfter(recycleAfter), mTouched(0),
mNAllocations(0), mId(id)
nsRecyclingAllocator::nsRecyclingAllocator(PRUint32 nbucket, PRUint32 recycleAfter, const char *id) :
mMaxBlocks(nbucket), mBlocks(nsnull), mFreeList(nsnull), mNotUsedList(nsnull),
mRecycleTimer(nsnull), mRecycleAfter(recycleAfter), mTouched(0), mId(id)
#ifdef DEBUG
,mNAllocated(0)
#endif
{
NS_ASSERTION(mNBucket <= NS_MAX_BUCKETS, "Too many buckets. This will affect the allocator's performance.");
if (mNBucket > NS_MAX_BUCKETS)
{
mNBucket = NS_MAX_BUCKETS;
}
mMemBucket = (nsMemBucket *) calloc(mNBucket, sizeof(nsMemBucket));
if (!mMemBucket)
mNBucket = 0;
else
{
for (PRUint32 i = 0; i < mNBucket; i++)
mMemBucket[i].available = 1;
NS_ASSERTION(mMaxBlocks <= NS_MAX_BLOCKS, "Too many blocks. This will affect the allocator's performance.");
mLock = PR_NewLock();
NS_ASSERTION(mLock, "Recycling allocator cannot get lock");
Init(nbucket, recycleAfter, id);
}
NS_IMETHODIMP
nsRecyclingAllocator::Init(PRUint32 nbucket, PRUint32 recycleAfter, const char *id)
{
nsAutoLock lock(mLock);
// Free all memory held, if any
while(mFreeList)
{
free(mFreeList->block);
mFreeList = mFreeList->next;
}
mFreeList = nsnull;
if (mBlocks)
delete [] mBlocks;
// Reinitialize everything
mMaxBlocks = nbucket;
if (nbucket)
{
// Create memory for our bookkeeping
mBlocks = new BlockStoreNode[mMaxBlocks];
if (!mBlocks)
return NS_ERROR_OUT_OF_MEMORY;
// Link them together
mNotUsedList = mBlocks;
for (PRUint32 i=0; i < mMaxBlocks-1; i++)
mBlocks[i].next = &(mBlocks[i+1]);
}
mRecycleAfter = recycleAfter;
mId = id;
return NS_OK;
}
nsRecyclingAllocator::~nsRecyclingAllocator()
{
// Cancel and destroy recycle timer
if (mRecycleTimer) {
if (mRecycleTimer)
{
mRecycleTimer->Cancel();
NS_RELEASE(mRecycleTimer);
}
// Free all memory held, if any
if (mNAllocations)
for (PRUint32 i = 0; i < mNBucket; i++)
{
PRBool claimed = Claim(i);
// ASSERT that we claimed the bucked. If we cannot, then the bucket is in use.
// We dont attempt to free this ptr.
// This will most likely cause a leak of this memory.
NS_ASSERTION(claimed, "~nsRecycledAllocator: Bucket memory still in use.");
// Free bucket memory if not in use
if (claimed && mMemBucket[i].ptr)
free(mMemBucket[i].ptr);
}
while(mFreeList)
{
free(mFreeList->block);
mFreeList = mFreeList->next;
}
mFreeList = nsnull;
if (mBlocks)
delete [] mBlocks;
// Free memory for buckets
if (mMemBucket)
free(mMemBucket);
if (mLock)
{
PR_DestroyLock(mLock);
mLock = nsnull;
}
}
// Allocation and free routines
void*
nsRecyclingAllocator::Malloc(PRUint32 bytes, PRBool zeroit)
{
PRInt32 availableBucketIndex = -1;
PRUint32 i;
PRUint32 size;
void *ptr;
// Mark that we are using. This will prevent any
// timer based release of unused memory.
Touch();
for (i = 0; i < mNBucket; i++)
{
size = mMemBucket[i].size;
ptr = mMemBucket[i].ptr;
// Dont look at buckets with no memory allocated or less memory than
// what we need.
// Can we do this check without claiming the bucket? I think we can
// because the next thing we do is try claim this bucket.
if (!ptr || size < bytes)
continue;
// Try Claim a bucket. If we cant, skip it.
if (!Claim(i))
continue;
if (size == bytes)
{
// Let go of any freeAllocatedBucket that we claimed
if (availableBucketIndex >= 0)
Unclaim(availableBucketIndex);
if (zeroit)
memset(ptr, bytes, 0);
return ptr;
}
// Meanwhile, remember a free allocated bucket.
// At this point we know the bucket is not in use and it has more
// than what we need
if (availableBucketIndex < 0)
availableBucketIndex = i;
else
{
// See if this bucket is closer to what we need
if (size < mMemBucket[availableBucketIndex].size)
{
Unclaim(availableBucketIndex);
availableBucketIndex = i;
}
else
// Undo our claim as availableBucketIndex does better than this one
Unclaim(i);
}
}
// See if we have an allocated bucket
if (availableBucketIndex >= 0) {
ptr = mMemBucket[availableBucketIndex].ptr;
memset(ptr, bytes, 0);
return ptr;
}
// We dont have that memory already
// Allocate.
ptr = zeroit ? calloc(1, bytes) : malloc(bytes);
// Take care of no memory and no free slot situation
if (!ptr || mNAllocations == (PRInt32)mNBucket)
{
#ifdef DEBUG_dp
// Warn if we are failing over to malloc and not storing it
// This says we have a misdesigned memory pool. The intent was
// once the pool was full, we would never fail over to calloc.
printf("nsRecyclingAllocator(%s) malloc %d - FAILOVER 0x%p Memory pool has sizes: ",
mId, bytes, ptr);
for (i = 0; i < mNBucket; i++)
{
printf("%d ", mMemBucket[i].size);
}
printf("\n");
#endif
return ptr;
}
// Find a free unallocated bucket and store allocation
for (i = 0; i < mNBucket; i++)
Block* freeBlock = FindFreeBlock(bytes);
if (freeBlock)
{
// If bucket cannot be claimed, continue search.
if (!Claim(i))
continue;
if (!mMemBucket[i].ptr)
{
// Found free slot. Store it
mMemBucket[i].ptr = ptr;
mMemBucket[i].size = bytes;
PR_AtomicIncrement(&mNAllocations);
// This is the first allocation we are holding.
// Setup timer for releasing memory
// If this fails, then we wont have a timer to release unused
// memory. We can live with that. Also, the next allocation
// will try again to set the timer.
if (mNAllocations && !mRecycleTimer)
{
(void) NS_NewTimer(&mRecycleTimer, nsRecycleTimerCallback, this,
NS_SEC_TO_MS(mRecycleAfter), PR_TRUE,
NS_TYPE_REPEATING_SLACK);
}
return ptr;
}
// This is an already allocated bucket. Cant use this one.
Unclaim(i);
return DATA(freeBlock);
}
#ifdef DEBUG_dp
// Warn if we are failing over to malloc and not storing it
// This says we have a misdesigned memory pool. The intent was
// once the pool was full, we would never fail over to calloc.
printf("nsRecyclingAllocator(%s) malloc %d - FAILOVER 0x%p Memory pool has sizes: ",
mId, bytes, ptr);
for (i = 0; i < mNBucket; i++)
// We need to do an allocation
// Add 4 bytes to what we allocate to hold the bucket index
PRUint32 allocBytes = bytes + NS_ALLOCATOR_OVERHEAD_BYTES;
// We dont have that memory already. Allocate.
Block *ptr = (Block *) (zeroit ? calloc(1, allocBytes) : malloc(allocBytes));
// Deal with no memory situation
if (!ptr)
return ptr;
// This is the first allocation we are holding.
// Setup timer for releasing memory
// If this fails, then we wont have a timer to release unused
// memory. We can live with that. Also, the next allocation
// will try again to set the timer.
if (mRecycleAfter && !mRecycleTimer)
{
printf("%d ", mMemBucket[i].size);
(void) NS_NewTimer(&mRecycleTimer, nsRecycleTimerCallback, this,
NS_SEC_TO_MS(mRecycleAfter), PR_TRUE,
NS_TYPE_REPEATING_SLACK);
NS_ASSERTION(mRecycleTimer, "nsRecyclingAllocator: Creating timer failed.\n");
}
printf("\n");
#ifdef DEBUG
mNAllocated++;
#endif
return ptr;
// Store size and return data portion
ptr->bytes = bytes;
return DATA(ptr);
}
void
@ -268,28 +211,23 @@ nsRecyclingAllocator::Free(void *ptr)
// timer based release of unused memory.
Touch();
for (PRUint32 i = 0; i < mNBucket; i++)
{
if (mMemBucket[i].ptr == ptr)
{
// Ah ha. One of the slots we allocated.
// Nothing to do. Mark it unused.
Unclaim(i);
return;
}
}
Block* block = DATA_TO_BLOCK(ptr);
#ifdef DEBUG_dp
// Warn if we are failing over to free
printf("nsRecyclingAllocator(%s) free - FAILOVER 0x%p Memory pool has sizes: ", mId, ptr);
for (i = 0; i < mNBucket; i++)
if (!AddToFreeList(block))
{
printf("%d ", mMemBucket[i].size);
}
printf("\n");
// We are holding more than max. Failover to free
#ifdef DEBUG_dp
char buf[1024];
// Warn if we are failing over to malloc/free and not storing it
// This says we have a misdesigned memory pool. The intent was
// once the pool was full, we would never fail over to calloc.
PR_snprintf(buf, sizeof(buf), "nsRecyclingAllocator(%s) FAILOVER 0x%p (%d) - %d allocations, %d max\n",
mId, (char *)ptr, block->bytes, mNAllocated, mMaxBlocks);
NS_WARNING(buf);
mNAllocated--;
#endif
// Failover to free
free(ptr);
free(block);
}
}
/* FreeUnusedBuckets
@ -303,28 +241,182 @@ nsRecyclingAllocator::FreeUnusedBuckets()
#ifdef DEBUG_dp
printf("DEBUG: nsRecyclingAllocator(%s) FreeUnusedBuckets: ", mId);
#endif
if (!mNAllocations)
return;
nsAutoLock lock(mLock);
for (PRUint32 i = 0; i < mNBucket; i++)
// We will run through the freelist and free all blocks
BlockStoreNode* node = mFreeList;
while (node)
{
if (Claim(i))
{
if (mMemBucket[i].ptr)
{
// Free the allocated block
free(node->block);
#ifdef DEBUG_dp
printf("%d ", mMemBucket[i].size);
printf("%d ", node->bytes);
#endif
free(mMemBucket[i].ptr);
mMemBucket[i].ptr = nsnull;
mMemBucket[i].size = 0;
PR_AtomicDecrement(&mNAllocations);
}
Unclaim(i);
}
// Clear Node
node->block = nsnull;
node->bytes = 0;
node = node->next;
}
// remake the lists
mNotUsedList = mBlocks;
for (PRUint32 i=0; i < mMaxBlocks-1; i++)
mBlocks[i].next = &(mBlocks[i+1]);
mBlocks[mMaxBlocks-1].next = nsnull;
mFreeList = nsnull;
#ifdef DEBUG
mNAllocated = 0;
#endif
#ifdef DEBUG_dp
printf("\n");
#endif
}
inline nsRecyclingAllocator::Block*
nsRecyclingAllocator::FindFreeBlock(PRUint32 bytes)
{
// We dont enter lock for this check. This is intentional.
// Here is my logic: we are checking if (!mFreeList). Doing this check
// without locking can lead to unpredictable results. YES. But the effect
// of the unpredictedness are ok. here is why:
//
// a) if the check returned NULL when there is stuff in freelist
// We would just end up reallocating.
//
// b) if the check returned nonNULL when our freelist is empty
// This is the more likely and dangerous case. The code for
// FindFreeBlock() will enter lock, while (null) and return null.
//
// The reason why I chose to not enter lock for this check was that when
// the allocator is full, we dont want to impose any more overhead than
// we already are for failing over to malloc/free.
if (!mFreeList)
return NULL;
Block *block = nsnull;
nsAutoLock lock(mLock);
BlockStoreNode* freeNode = mFreeList;
BlockStoreNode** prevp = &mFreeList;
while (freeNode)
{
if (freeNode->bytes >= bytes)
{
// Found the best fit free block
block = freeNode->block;
// Clear the free node
freeNode->block = nsnull;
freeNode->bytes = 0;
// Remove free node from free list
*prevp = freeNode->next;
// Add removed BlockStoreNode to not used list
freeNode->next = mNotUsedList;
mNotUsedList = freeNode;
break;
}
prevp = &(freeNode->next);
freeNode = freeNode->next;
}
return block;
}
inline PRInt32
nsRecyclingAllocator::AddToFreeList(Block* block)
{
// Make sure we arent keeping more than mMaxBlocks
if (!mNotUsedList)
return PR_FALSE;
nsAutoLock lock(mLock);
// Pick a node from the not used list
BlockStoreNode *node = mNotUsedList;
mNotUsedList = mNotUsedList->next;
// Initialize the node
node->bytes = block->bytes;
node->block = block;
// Find the right spot in the sorted list.
BlockStoreNode* freeNode = mFreeList;
BlockStoreNode** prevp = &mFreeList;
while (freeNode)
{
if (freeNode->bytes >= block->bytes)
break;
prevp = &(freeNode->next);
freeNode = freeNode->next;
}
// Needs to be inserted between *prevp and freeNode
*prevp = node;
node->next = freeNode;
return PR_TRUE;
}
// ----------------------------------------------------------------------
// Wrapping the recyling allocator with nsIMemory
// ----------------------------------------------------------------------
// nsIMemory methods
NS_IMPL_THREADSAFE_ISUPPORTS2(nsRecyclingAllocatorImpl, nsIMemory, nsIRecyclingAllocator);
NS_IMETHODIMP_(void *)
nsRecyclingAllocatorImpl::Alloc(PRSize size)
{
if (size < 0)
return NULL;
else
return nsRecyclingAllocatorImpl::Malloc(size, PR_FALSE);
}
NS_IMETHODIMP_(void *)
nsRecyclingAllocatorImpl::Realloc(void *ptr, PRSize size)
{
// XXX Not yet implemented
return NULL;
}
NS_IMETHODIMP_(void)
nsRecyclingAllocatorImpl::Free(void *ptr)
{
nsRecyclingAllocator::Free(ptr);
}
NS_IMETHODIMP
nsRecyclingAllocatorImpl::Init(size_t nbuckets, size_t recycleAfter, const char *id)
{
// normalize input
if (nbuckets < 0)
nbuckets = 0;
if (recycleAfter < 0)
recycleAfter = 0;
return nsRecyclingAllocator::Init((PRUint32) nbuckets, (PRUint32) recycleAfter, id);
}
NS_IMETHODIMP
nsRecyclingAllocatorImpl::HeapMinimize(PRBool immediate)
{
// XXX Not yet implemented
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsRecyclingAllocatorImpl::IsLowMemory(PRBool *lowmemoryb_ptr)
{
// XXX Not yet implemented
return NS_ERROR_NOT_IMPLEMENTED;
}

View File

@ -50,11 +50,13 @@
* Uses a timer to release all memory allocated if not used for more than
* 10 secs automatically.
*
* Also there is a 4 byte maintenance overhead on every allocation.
*
* This allocator is thread safe.
*
* CAVEATS: As the number of buckets increases, this allocators performance
* will drop. As a general guideline, dont use this for more
* than NS_MAX_BUCKETS (10)
* than NS_MAX_BLOCKS
*/
#ifndef nsRecyclingAllocator_h__
@ -62,55 +64,87 @@
#include "nscore.h"
#include "pratom.h"
#include "prlock.h"
#include "nsIRecyclingAllocator.h"
#include "nsIGenericFactory.h"
#define NS_DEFAULT_RECYCLE_TIMEOUT 10 // secs
#define NS_MAX_BUCKETS 10
#define NS_MAX_BLOCKS 24
#define NS_ALLOCATOR_OVERHEAD_BYTES (sizeof(Block)) // bytes
class nsITimer;
class nsIMemory;
class NS_COM nsRecyclingAllocator {
protected:
struct nsMemBucket {
void *ptr;
PRUint32 size;
// Is this bucket available ?
// 1 : free
// 0 or negative : in use
// This is an int because we use atomic inc/dec to operate on it
PRInt32 available;
struct Block {
PRUint32 bytes;
};
PRUint32 mNBucket;
nsMemBucket *mMemBucket;
struct BlockStoreNode {
BlockStoreNode() : bytes(0), block(nsnull), next(nsnull) {};
PRUint32 bytes;
Block *block;
BlockStoreNode *next;
};
#define DATA(block) ((void *)(((char *)block) + NS_ALLOCATOR_OVERHEAD_BYTES))
#define DATA_TO_BLOCK(data) ((Block *)((char *)(data) - NS_ALLOCATOR_OVERHEAD_BYTES))
// mMaxBlocks: Maximum number of blocks that can be allocated
PRUint32 mMaxBlocks;
// mBlocks:
// All blocks used or not.
BlockStoreNode *mBlocks;
// mFreeList
// A linked list of free blocks sorted by increasing order of size
BlockStoreNode* mFreeList;
// mNotUsedList
// A linked list of BlockStoreNodes that are not used to store
// any block information. When we add blocks into mFreeList, we
// take BlockStoreNode from here.
BlockStoreNode* mNotUsedList;
// mLock: Thread safety of mFreeList and mNotUsedList
PRLock *mLock;
// Timer for freeing unused memory
nsITimer *mRecycleTimer;
// mRecycleAfter:
// Allocator should be untouched for this many seconds for freeing
// unused Blocks.
PRUint32 mRecycleAfter;
// mTouched : says if the allocator touched any bucket
// If allocator didn't touch any bucket over a time
// time interval, timer will call FreeUnusedBuckets()
// mTouched:
// says if the allocator touched any bucket. If allocator didn't touch
// any bucket over a time time interval, timer will call FreeUnusedBuckets()
PRInt32 mTouched;
// mNAllocations: says how many buckets still have memory.
// If all buckets were freed the last time
// around and allocator didn't touch any bucket, then
// there is nothing to be freed.
PRInt32 mNAllocations;
// mId: a string for identifying the user of nsRecyclingAllocator
// User mainly for debug prints
// mId:
// a string for identifying the user of nsRecyclingAllocator
// User mainly for debug prints
const char *mId;
#ifdef DEBUG
// mNAllocated: Number of blocks allocated
PRInt32 mNAllocated;
#endif
public:
// nbucket : number of buckets to hold. Capped at NS_MAX_BUCKET
// recycleAfter : Try recycling allocated buckets after this many seconds
// id : a string used to identify debug prints. Will not be released.
nsRecyclingAllocator(PRUint32 nbucket, PRUint32 recycleAfter = NS_DEFAULT_RECYCLE_TIMEOUT,
nsRecyclingAllocator(PRUint32 nbucket = 0, PRUint32 recycleAfter = NS_DEFAULT_RECYCLE_TIMEOUT,
const char *id = NULL);
~nsRecyclingAllocator();
NS_IMETHOD Init(PRUint32 nbucket, PRUint32 recycleAfter, const char *id);
// Allocation and free routines
void* Malloc(PRUint32 size, PRBool zeroit = PR_FALSE);
void Free(void *ptr);
@ -120,27 +154,20 @@ class NS_COM nsRecyclingAllocator {
return Malloc(items * size, PR_TRUE);
}
// FreeUnusedBuckets - Frees any bucket memory that isn't in use
void FreeUnusedBuckets();
protected:
// Timer callback to trigger unused memory
// Timer callback to trigger unused memory
static void nsRecycleTimerCallback(nsITimer *aTimer, void *aClosure);
// Bucket handling
PRBool Claim(PRUint32 i)
{
PRBool claimed = (PR_AtomicDecrement(&mMemBucket[i].available) == 0);
// Undo the claim, if claim failed
if (!claimed)
Unclaim(i);
return claimed;
}
void Unclaim(PRUint32 i) { PR_AtomicIncrement(&mMemBucket[i].available); }
// Freelist management
// FindFreeBlock: return a free block that can hold bytes (best fit)
Block* FindFreeBlock(PRUint32 bytes);
// AddToFreeList: adds block into our freelist for future retrieval.
// Returns PR_TRUE is addition was successful. PR_FALSE otherewise.
PRBool AddToFreeList(Block* block);
// Touch will mark that someone used this allocator
// Timer based release will free unused memory only if allocator
@ -156,4 +183,24 @@ class NS_COM nsRecyclingAllocator {
friend void nsRecycleTimerCallback(nsITimer *aTimer, void *aClosure);
};
// ----------------------------------------------------------------------
// Wrapping the recyling allocator with nsIMemory
// ----------------------------------------------------------------------
// Wrapping the nsRecyclingAllocator with nsIMemory
class NS_COM nsRecyclingAllocatorImpl : public nsRecyclingAllocator, public nsIRecyclingAllocator {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMEMORY
NS_DECL_NSIRECYCLINGALLOCATOR
nsRecyclingAllocatorImpl()
{
NS_INIT_ISUPPORTS();
}
virtual ~nsRecyclingAllocatorImpl() {}
NS_GENERIC_FACTORY_CONSTRUCTOR(nsRecyclingAllocatorImpl);
};
#endif // nsRecyclingAllocator_h__

View File

@ -1305,6 +1305,13 @@
<FILEKIND>Text</FILEKIND>
<FILEFLAGS></FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsIRecyclingAllocator.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
<FILEKIND>Text</FILEKIND>
<FILEFLAGS></FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsIStringStream.idl</PATH>
@ -1703,6 +1710,11 @@
<PATH>nsIPropertyBag.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsIRecyclingAllocator.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsIStringStream.idl</PATH>
@ -2972,6 +2984,13 @@
<FILEKIND>Text</FILEKIND>
<FILEFLAGS></FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsIRecyclingAllocator.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
<FILEKIND>Text</FILEKIND>
<FILEFLAGS></FILEFLAGS>
</FILE>
<FILE>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsIStringStream.idl</PATH>
@ -3370,6 +3389,11 @@
<PATH>nsIPropertyBag.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsIRecyclingAllocator.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsIStringStream.idl</PATH>
@ -3845,6 +3869,12 @@
<PATH>nsIPropertyBag.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<TARGETNAME>headers</TARGETNAME>
<PATHTYPE>Name</PATHTYPE>
<PATH>nsIRecyclingAllocator.idl</PATH>
<PATHFORMAT>MacOS</PATHFORMAT>
</FILEREF>
<FILEREF>
<TARGETNAME>headers</TARGETNAME>
<PATHTYPE>Name</PATHTYPE>