bug 115986 Thread safe zlib allocator. r=waterson, sr=brendan

This commit is contained in:
dp%netscape.com 2002-01-04 05:46:48 +00:00
Родитель 6aef8850f7
Коммит 511f55d7ee
2 изменённых файлов: 132 добавлений и 90 удалений

Просмотреть файл

@ -44,6 +44,14 @@
#define READTYPE PRInt32 #define READTYPE PRInt32
#include "zlib.h" #include "zlib.h"
#include "nsISupportsUtils.h" #include "nsISupportsUtils.h"
#include "nsZlibAllocator.h"
/**
* Globals
*
* Global allocator used with zlib. Destroyed in module shutdown.
*/
nsZlibAllocator *gZlibAllocator = NULL;
#else /* STANDALONE */ #else /* STANDALONE */
@ -74,14 +82,6 @@ char * strdup(const char *src)
#endif /* STANDALONE */ #endif /* STANDALONE */
#include "nsZlibAllocator.h"
/**
* Globals
*
* Global allocator used with zlib. Destroyed in module shutdown.
*/
nsZlibAllocator *gZlibAllocator = NULL;
#ifdef XP_UNIX #ifdef XP_UNIX
#include <sys/stat.h> #include <sys/stat.h>
#include <limits.h> #include <limits.h>
@ -334,15 +334,11 @@ void ProcessWindowsMessages()
} }
#endif /* XP_WIN */ #endif /* XP_WIN */
#endif /* STANDALONE */ #else /* STANDALONE */
//*********************************************************** //***********************************************************
// Allocators for use with zlib // Allocators for use with zlib
// //
// WARNING: NOT THREADSAFE. But usage from nsJAR.cpp protects against
// multithread usage. nsJAR.cpp assumes nsZipArchive is
// not thread safe.
//
// These are allocators that are performance tuned for // These are allocators that are performance tuned for
// use with zlib. Our use of zlib for every file we read from // use with zlib. Our use of zlib for every file we read from
// the jar file when running navigator, we do these allocation. // the jar file when running navigator, we do these allocation.
@ -386,98 +382,110 @@ void ProcessWindowsMessages()
// we startup and disabled after we startup if memory is a concern. // we startup and disabled after we startup if memory is a concern.
//*********************************************************** //***********************************************************
// zClearBuckets() : Frees used memory and initalizes all memory to 0 void * nsZlibAllocator::zAlloc(PRUint32 items, PRUint32 bytes)
// DO NOT CALL THIS FROM CONSTRUCTOR. Set all memebers to 0 in constructor.
int nsZlibAllocator::zClearBuckets()
{ {
for (int i = 0; i < NBUCKETS; i++) PRUint32 totalsize = items * bytes;
PRInt32 freeAllocatedBucketIndex = -1;
PRUint32 i;
PRUint32 size;
void *ptr;
for (i = 0; i < NBUCKETS; i++)
{ {
if (mMemBucket[i].ptr) 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 < totalsize)
continue;
// Try Claim a bucket. If we cant, skip it.
if (!Claim(i))
continue;
if (size == totalsize)
{ {
// If the bucket is in use, then we will leak that memory. // Let go of any freeAllocatedBucket that we claimed
PR_ASSERT(!mMemBucket[i].inUse); if (freeAllocatedBucketIndex >= 0)
if (!mMemBucket[i].inUse) Unclaim(freeAllocatedBucketIndex);
// zero out memory and return it
memset(ptr, 0, totalsize);
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 (freeAllocatedBucketIndex < 0)
freeAllocatedBucketIndex = i;
else
{
// See if this bucket is closer to what we need
if (size < mMemBucket[freeAllocatedBucketIndex].size)
{ {
free(mMemBucket[i].ptr); Unclaim(freeAllocatedBucketIndex);
} freeAllocatedBucketIndex = i;
}
mMemBucket[i].ptr = NULL;
mMemBucket[i].size = 0;
mMemBucket[i].inUse = PR_FALSE;
}
return 0;
}
void * nsZlibAllocator::zAlloc(PRUint32 items, PRUint32 size)
{
PRUint32 totalsize = items * size;
nsMemBucket *freeBucket = NULL;
nsMemBucket *freeAllocatedBucket = NULL;
for (int i = 0; i < NBUCKETS; i++)
{
// See if we have the memory already allocated. This is the
// most common case.
if (!mMemBucket[i].inUse && mMemBucket[i].ptr && mMemBucket[i].size == totalsize)
{
// zero out memory, increase refcnt and return it
memset(mMemBucket[i].ptr, 0, totalsize);
mMemBucket[i].inUse = PR_TRUE;
return mMemBucket[i].ptr;
}
// Meanwhile, remember a free bucket, any one will do
if (!mMemBucket[i].inUse) {
if (mMemBucket[i].size >= totalsize)
freeAllocatedBucket = &mMemBucket[i];
else if (!mMemBucket[i].ptr) {
// This is a free slot
freeBucket = &mMemBucket[i];
} }
else
// Undo our claim as freeAllocatedBucketIndex does better than this one
Unclaim(i);
} }
} }
// See if we have an allocated bucket // See if we have an allocated bucket
if (freeAllocatedBucket) if (freeAllocatedBucketIndex >= 0)
{ {
ptr = mMemBucket[freeAllocatedBucketIndex].ptr;
// Clear it, Mark it used and return ptr // Clear it, Mark it used and return ptr
// We need to clear only the size that was requested although // We need to clear only the size that was requested although
// the bucket may be larger // the bucket may be larger
memset(freeAllocatedBucket->ptr, 0, totalsize); memset(ptr, 0, totalsize);
freeAllocatedBucket->inUse = PR_TRUE; return ptr;
return freeAllocatedBucket->ptr;
} }
// We dont have that memory already // We dont have that memory already
// Allocate. Make sure we bump up our allocations of x4 allocations // Allocate. Make sure we bump up our allocations of x4 allocations
int realitems = items; int realitems = items;
if (size == 4) if (bytes == 4 && items < BY4ALLOC_ITEMS)
realitems = BY4ALLOC_ITEMS; realitems = BY4ALLOC_ITEMS;
void *ptr = calloc(realitems, size); ptr = calloc(realitems, bytes);
// Take care of no memory situation
if (!ptr)
return ptr;
if (freeBucket) // Find a free unallocated bucket and store allocation
for (i = 0; i < NBUCKETS; i++)
{ {
// Found free slot. Store it // If bucket cannot be calimed, continue search.
freeBucket->inUse = PR_TRUE; if (!Claim(i))
freeBucket->ptr = ptr; continue;
freeBucket->size = realitems * size;
if (!mMemBucket[i].ptr)
{
// Found free slot. Store it
mMemBucket[i].ptr = ptr;
mMemBucket[i].size = realitems * bytes;
return ptr;
}
// This is an already allocated bucket. Cant use this one.
Unclaim(i);
} }
#ifdef DEBUG_dp #ifdef DEBUG_dp
// Warn if we are failing over to calloc and not storing it // Warn if we are failing over to calloc and not storing it
// This says we have a misdesigned memory pool. The intent was // This says we have a misdesigned memory pool. The intent was
// once the pool was full, we would never fail over to calloc. // once the pool was full, we would never fail over to calloc.
else printf("zalloc %d [%dx%d] - FAILOVER 0x%p Memory pool has sizes: ",
items*bytes, items, bytes, ptr);
for (i = 0; i < NBUCKETS; i++)
{ {
printf("0x%p - zalloc %d [%dx%d] - FAILOVER 0x%p Memory pool has sizes: ", printf("%d ", mMemBucket[i].size);
this, items*size, items, size, ptr);
for (i = 0; i < NBUCKETS; i++)
{
printf("%d ", mMemBucket[i].size);
}
printf("\n");
} }
printf("\n");
#endif #endif
return ptr; return ptr;
@ -485,13 +493,15 @@ void * nsZlibAllocator::zAlloc(PRUint32 items, PRUint32 size)
void nsZlibAllocator::zFree(void *ptr) void nsZlibAllocator::zFree(void *ptr)
{ {
for (int i = 0; i < NBUCKETS; i++) PRUint32 i;
for (i = 0; i < NBUCKETS; i++)
{ {
if (mMemBucket[i].ptr == ptr) if (mMemBucket[i].ptr == ptr)
{ {
// Ah ha. One of the slots we allocated. // Ah ha. One of the slots we allocated.
// Nothing to do. Mark it unused. // Nothing to do. Mark it unused.
mMemBucket[i].inUse = PR_FALSE; Unclaim(i);
return; return;
} }
} }
@ -500,7 +510,7 @@ void nsZlibAllocator::zFree(void *ptr)
// Warn if we are failing over to free. // Warn if we are failing over to free.
// This says we have a misdesigned memory pool. The intent was // This says we have a misdesigned memory pool. The intent was
// once the pool was full, we would never fail over to free. // once the pool was full, we would never fail over to free.
printf("DEBUG: zlib memory pool freeing 0x%p", ptr); printf("DEBUG: zlib memory pool freeing 0x%p\n", ptr);
#endif #endif
// Failover to free // Failover to free
@ -528,7 +538,7 @@ zlibFree(void *opaque, void *ptr)
free(ptr); free(ptr);
return; return;
} }
#endif /* STANDALONE */
//*********************************************************** //***********************************************************
// nsZipArchive -- public methods // nsZipArchive -- public methods
@ -1388,16 +1398,20 @@ PRInt32 nsZipArchive::InflateItem( const nsZipItem* aItem, PRFileDesc* fOut,
Bytef inbuf[ZIP_BUFLEN]; Bytef inbuf[ZIP_BUFLEN];
Bytef outbuf[ZIP_BUFLEN]; Bytef outbuf[ZIP_BUFLEN];
//-- set up the inflate
memset( &zs, 0, sizeof(zs) );
#ifndef STANDALONE
//-- ensure we have our zlib allocator for better performance //-- ensure we have our zlib allocator for better performance
if (!gZlibAllocator) { if (!gZlibAllocator) {
gZlibAllocator = new nsZlibAllocator(); gZlibAllocator = new nsZlibAllocator();
} }
//-- set up the inflate
memset( &zs, 0, sizeof(zs) );
zs.zalloc = zlibAlloc; zs.zalloc = zlibAlloc;
zs.zfree = zlibFree; zs.zfree = zlibFree;
zs.opaque = gZlibAllocator; zs.opaque = gZlibAllocator;
#endif
zerr = inflateInit2( &zs, -MAX_WBITS ); zerr = inflateInit2( &zs, -MAX_WBITS );
if ( zerr != Z_OK ) if ( zerr != Z_OK )
{ {

Просмотреть файл

@ -51,25 +51,53 @@ class nsZlibAllocator {
struct nsMemBucket { struct nsMemBucket {
void *ptr; void *ptr;
PRUint32 size; PRUint32 size;
PRBool inUse;
// 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;
}; };
nsMemBucket mMemBucket[NBUCKETS]; nsMemBucket mMemBucket[NBUCKETS];
nsZlibAllocator() { nsZlibAllocator() {
memset(mMemBucket, 0, sizeof(mMemBucket)); memset(mMemBucket, 0, sizeof(mMemBucket));
for (PRUint32 i = 0; i < NBUCKETS; i++)
mMemBucket[i].available = 1;
return; return;
} }
~nsZlibAllocator() { ~nsZlibAllocator() {
zClearBuckets(); for (PRUint32 i = 0; i < NBUCKETS; 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.
PR_ASSERT(claimed);
// Free bucket memory if not in use
if (claimed && mMemBucket[i].ptr)
free(mMemBucket[i].ptr);
}
} }
// zlib allocators // zlib allocators
void* zAlloc(PRUint32 items, PRUint32 size); void* zAlloc(PRUint32 items, PRUint32 size);
void zFree(void *ptr); void zFree(void *ptr);
// Clear the buckets of memory we have for zlib
int zClearBuckets();
// 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); }
}; };